1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
57 #if defined (Q_WS_X11)
58 # include <private/qfontengine_ft_p.h>
61 // #include <private/qdatabuffer_p.h>
62 // #include <private/qpainter_p.h>
63 #include <private/qmath_p.h>
64 #include <private/qtextengine_p.h>
65 #include <private/qfontengine_p.h>
66 #include <private/qpixmap_raster_p.h>
67 // #include <private/qpolygonclipper_p.h>
68 // #include <private/qrasterizer_p.h>
69 #include <private/qimage_p.h>
70 #include <private/qstatictext_p.h>
71 #include "qmemrotate_p.h"
73 #include "qpaintengine_raster_p.h"
74 // #include "qbezier_p.h"
75 #include "qoutlinemapper_p.h"
78 # include <qt_windows.h>
79 # include <qvarlengtharray.h>
80 # include <private/qfontengine_p.h>
81 # if defined(Q_OS_WINCE)
82 # include "qguifunctions_wince.h"
84 #elif defined(Q_WS_MAC)
85 # include <private/qt_mac_p.h>
86 # include <private/qpixmap_mac_p.h>
87 # include <private/qpaintengine_mac_p.h>
88 #elif defined(Q_WS_QWS)
89 # if !defined(QT_NO_FREETYPE)
90 # include <private/qfontengine_ft_p.h>
92 # if !defined(QT_NO_QWS_QPF2)
93 # include <private/qfontengine_qpf_p.h>
95 # include <private/qabstractfontengine_p.h>
96 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
97 # include <private/qfontengine_s60_p.h>
98 #elif defined(Q_WS_QPA)
99 # include <private/qfontengine_ft_p.h>
102 #if defined(Q_WS_WIN64)
109 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
111 #define qreal_to_fixed_26_6(f) (int(f * 64))
112 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
113 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
115 // #define QT_DEBUG_DRAW
117 void dumpClip(int width, int height, const QClipData *clip);
120 #define QT_FAST_SPANS
123 // A little helper macro to get a better approximation of dimensions.
124 // If we have a rect that starting at 0.5 of width 3.5 it should span
126 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
129 extern bool qt_cleartype_enabled;
133 extern bool qt_applefontsmoothing_enabled;
137 /********************************************************************************
140 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
141 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
142 static void qt_span_clip(int count, const QSpan *spans, void *userData);
143 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
149 Qt::ClipOperation operation;
155 LineDrawIncludeLastPixel
158 static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
159 LineDrawMode style, const QIntRect &rect);
160 static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
161 QPen *pen, ProcessSpans span_func, QSpanData *data,
162 LineDrawMode style, const QIntRect &devRect,
164 // static void drawLine_midpoint_f(qreal x1, qreal y1, qreal x2, qreal y2,
165 // ProcessSpans span_func, QSpanData *data,
166 // LineDrawMode style, const QRect &devRect);
168 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
169 ProcessSpans pen_func, ProcessSpans brush_func,
170 QSpanData *pen_data, QSpanData *brush_data);
172 struct QRasterFloatPoint {
178 static const QRectF boundingRect(const QPointF *points, int pointCount)
180 const QPointF *e = points;
181 const QPointF *last = points + pointCount;
182 qreal minx, maxx, miny, maxy;
183 minx = maxx = e->x();
184 miny = maxy = e->y();
188 else if (e->x() > maxx)
192 else if (e->y() > maxy)
195 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
199 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
200 return (elementCount == 5 // 5-point polygon, check for closed rect
201 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
202 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
203 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
204 && pts[0] < pts[4] && pts[1] < pts[5]
206 (elementCount == 4 // 4-point polygon, check for unclosed rect
207 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
208 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
209 && pts[0] < pts[4] && pts[1] < pts[5]
214 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
216 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
219 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
221 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
224 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
225 qfixed c2x, qfixed c2y,
226 qfixed ex, qfixed ey,
229 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
230 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
231 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
235 #if !defined(QT_NO_DEBUG) && 0
236 static void qt_debug_path(const QPainterPath &path)
238 const char *names[] = {
245 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
246 for (int i=0; i<path.elementCount(); ++i) {
247 const QPainterPath::Element &e = path.elementAt(i);
248 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
249 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
254 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
255 QPaintEngineExPrivate(),
262 \class QRasterPaintEngine
267 \brief The QRasterPaintEngine class enables hardware acceleration
268 of painting operations in Qt for Embedded Linux.
270 Note that this functionality is only available in
271 \l{Qt for Embedded Linux}.
273 In \l{Qt for Embedded Linux}, painting is a pure software
274 implementation. But starting with Qt 4.2, it is
275 possible to add an accelerated graphics driver to take advantage
276 of available hardware resources.
278 Hardware acceleration is accomplished by creating a custom screen
279 driver, accelerating the copying from memory to the screen, and
280 implementing a custom paint engine accelerating the various
281 painting operations. Then a custom paint device (derived from the
282 QCustomRasterPaintDevice class) and a custom window surface
283 (derived from QWSWindowSurface) must be implemented to make
284 \l{Qt for Embedded Linux} aware of the accelerated driver.
286 \note The QRasterPaintEngine class does not support 8-bit images.
287 Instead, they need to be converted to a supported format, such as
288 QImage::Format_ARGB32_Premultiplied.
290 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
291 documentation for details.
293 \sa QCustomRasterPaintDevice, QPaintEngine
297 \fn Type QRasterPaintEngine::type() const
303 \relates QRasterPaintEngine
305 A struct equivalent to QT_FT_Span, containing a position (x,
306 y), the span's length in pixels and its color/coverage (a value
307 ranging from 0 to 255).
313 Creates a raster based paint engine for operating on the given
314 \a device, with the complete set of \l
315 {QPaintEngine::PaintEngineFeature}{paint engine features and
318 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
319 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
321 d_func()->device = device;
328 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
331 d_func()->device = device;
335 void QRasterPaintEngine::init()
337 Q_D(QRasterPaintEngine);
344 // The antialiasing raster.
345 d->grayRaster.reset(new QT_FT_Raster);
346 Q_CHECK_PTR(d->grayRaster.data());
347 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
348 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
351 d->rasterizer.reset(new QRasterizer);
352 d->rasterBuffer.reset(new QRasterBuffer());
353 d->outlineMapper.reset(new QOutlineMapper);
354 d->outlinemapper_xform_dirty = true;
356 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
357 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
358 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
360 d->baseClip.reset(new QClipData(d->device->height()));
361 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
363 d->image_filler.init(d->rasterBuffer.data(), this);
364 d->image_filler.type = QSpanData::Texture;
366 d->image_filler_xform.init(d->rasterBuffer.data(), this);
367 d->image_filler_xform.type = QSpanData::Texture;
369 d->solid_color_filler.init(d->rasterBuffer.data(), this);
370 d->solid_color_filler.type = QSpanData::Solid;
372 d->deviceDepth = d->device->depth();
374 d->mono_surface = false;
375 gccaps &= ~PorterDuff;
377 QImage::Format format = QImage::Format_Invalid;
379 switch (d->device->devType()) {
380 case QInternal::Pixmap:
381 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
383 case QInternal::Image:
384 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
387 case QInternal::CustomRaster:
388 d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
392 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
398 case QImage::Format_MonoLSB:
399 case QImage::Format_Mono:
400 d->mono_surface = true;
402 case QImage::Format_ARGB8565_Premultiplied:
403 case QImage::Format_ARGB8555_Premultiplied:
404 case QImage::Format_ARGB6666_Premultiplied:
405 case QImage::Format_ARGB4444_Premultiplied:
406 case QImage::Format_ARGB32_Premultiplied:
407 case QImage::Format_ARGB32:
408 gccaps |= PorterDuff;
410 case QImage::Format_RGB32:
411 case QImage::Format_RGB444:
412 case QImage::Format_RGB555:
413 case QImage::Format_RGB666:
414 case QImage::Format_RGB888:
415 case QImage::Format_RGB16:
426 Destroys this paint engine.
428 QRasterPaintEngine::~QRasterPaintEngine()
430 Q_D(QRasterPaintEngine);
432 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
438 bool QRasterPaintEngine::begin(QPaintDevice *device)
440 Q_D(QRasterPaintEngine);
442 if (device->devType() == QInternal::Pixmap) {
443 QPixmap *pixmap = static_cast<QPixmap *>(device);
444 QPixmapData *pd = pixmap->pixmapData();
445 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
446 d->device = pd->buffer();
451 // Make sure QPaintEngine::paintDevice() returns the proper device.
454 Q_ASSERT(d->device->devType() == QInternal::Image
455 || d->device->devType() == QInternal::CustomRaster);
457 d->systemStateChanged();
459 QRasterPaintEngineState *s = state();
460 ensureOutlineMapper();
461 d->outlineMapper->m_clip_rect = d->deviceRect;
463 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
464 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
465 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
466 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
468 d->rasterizer->setClipRect(d->deviceRect);
470 s->penData.init(d->rasterBuffer.data(), this);
471 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
472 s->stroker = &d->basicStroker;
473 d->basicStroker.setClipRect(d->deviceRect);
475 s->brushData.init(d->rasterBuffer.data(), this);
476 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
478 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
480 setDirty(DirtyBrushOrigin);
483 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
484 << ") devType:" << device->devType()
485 << "devRect:" << d->deviceRect;
487 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
491 #if defined(Q_WS_WIN)
492 d->isPlain45DegreeRotation = true;
496 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
497 #if defined(Q_WS_WIN)
498 else if (qt_cleartype_enabled)
499 #elif defined (Q_WS_MAC)
500 else if (qt_applefontsmoothing_enabled)
505 QImage::Format format = static_cast<QImage *>(d->device)->format();
506 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
507 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
509 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
511 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
520 bool QRasterPaintEngine::end()
523 Q_D(QRasterPaintEngine);
524 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
526 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
536 void QRasterPaintEngine::releaseBuffer()
538 Q_D(QRasterPaintEngine);
539 d->rasterBuffer.reset(new QRasterBuffer);
545 QSize QRasterPaintEngine::size() const
547 Q_D(const QRasterPaintEngine);
548 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
555 void QRasterPaintEngine::saveBuffer(const QString &s) const
557 Q_D(const QRasterPaintEngine);
558 d->rasterBuffer->bufferImage().save(s, "PNG");
565 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
567 QRasterPaintEngineState *s = state();
568 // FALCON: get rid of this line, see drawImage call below.
570 QTransform::TransformationType txop = s->matrix.type();
574 case QTransform::TxNone:
575 s->flags.int_xform = true;
578 case QTransform::TxTranslate:
579 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
580 && qreal(int(s->matrix.dy())) == s->matrix.dy();
583 case QTransform::TxScale:
584 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
585 && qreal(int(s->matrix.dy())) == s->matrix.dy()
586 && qreal(int(s->matrix.m11())) == s->matrix.m11()
587 && qreal(int(s->matrix.m22())) == s->matrix.m22();
590 default: // shear / perspective...
591 s->flags.int_xform = false;
595 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
597 ensureOutlineMapper();
600 Q_D(QRasterPaintEngine);
601 d->isPlain45DegreeRotation = false;
602 if (txop >= QTransform::TxRotate) {
603 d->isPlain45DegreeRotation =
604 (qFuzzyIsNull(matrix.m11())
605 && qFuzzyIsNull(matrix.m12() - qreal(1))
606 && qFuzzyIsNull(matrix.m21() + qreal(1))
607 && qFuzzyIsNull(matrix.m22())
610 (qFuzzyIsNull(matrix.m11() + qreal(1))
611 && qFuzzyIsNull(matrix.m12())
612 && qFuzzyIsNull(matrix.m21())
613 && qFuzzyIsNull(matrix.m22() + qreal(1))
616 (qFuzzyIsNull(matrix.m11())
617 && qFuzzyIsNull(matrix.m12() + qreal(1))
618 && qFuzzyIsNull(matrix.m21() - qreal(1))
619 && qFuzzyIsNull(matrix.m22())
629 QRasterPaintEngineState::~QRasterPaintEngineState()
631 if (flags.has_clip_ownership)
636 QRasterPaintEngineState::QRasterPaintEngineState()
648 flags.fast_pen = true;
649 flags.antialiased = false;
650 flags.bilinear = false;
651 flags.fast_text = true;
652 flags.int_xform = true;
653 flags.tx_noshear = true;
654 flags.fast_images = true;
657 flags.has_clip_ownership = false;
662 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
667 , strokeFlags(s.strokeFlags)
668 , lastBrush(s.lastBrush)
669 , brushData(s.brushData)
670 , fillFlags(s.fillFlags)
671 , pixmapFlags(s.pixmapFlags)
672 , intOpacity(s.intOpacity)
676 , flag_bits(s.flag_bits)
678 brushData.tempImage = 0;
679 penData.tempImage = 0;
680 flags.has_clip_ownership = false;
686 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
688 QRasterPaintEngineState *s;
690 s = new QRasterPaintEngineState();
692 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
700 void QRasterPaintEngine::setState(QPainterState *s)
702 Q_D(QRasterPaintEngine);
703 QPaintEngineEx::setState(s);
704 d->rasterBuffer->compositionMode = s->composition_mode;
708 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
713 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
720 void QRasterPaintEngine::penChanged()
723 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
725 QRasterPaintEngineState *s = state();
726 s->strokeFlags |= DirtyPen;
727 s->dirty |= DirtyPen;
733 void QRasterPaintEngine::updatePen(const QPen &pen)
735 Q_D(QRasterPaintEngine);
736 QRasterPaintEngineState *s = state();
738 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
741 Qt::PenStyle pen_style = qpen_style(pen);
746 s->penData.clip = d->clip();
747 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
749 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
750 || pen.brush().transform().type() >= QTransform::TxNone) {
751 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
754 // Slightly ugly handling of an uncommon case... We need to change
755 // the pen because it is reused in draw_midpoint to decide dashed
757 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
758 pen_style = Qt::SolidLine;
759 s->lastPen.setStyle(Qt::SolidLine);
762 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
763 d->basicStroker.setCapStyle(qpen_capStyle(pen));
764 d->basicStroker.setMiterLimit(pen.miterLimit());
766 qreal penWidth = qpen_widthf(pen);
768 d->basicStroker.setStrokeWidth(1);
770 d->basicStroker.setStrokeWidth(penWidth);
772 if(pen_style == Qt::SolidLine) {
773 s->stroker = &d->basicStroker;
774 } else if (pen_style != Qt::NoPen) {
776 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
777 if (pen.isCosmetic()) {
778 d->dashStroker->setClipRect(d->deviceRect);
780 // ### I've seen this inverted devrect multiple places now...
781 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
782 d->dashStroker->setClipRect(clipRect);
784 d->dashStroker->setDashPattern(pen.dashPattern());
785 d->dashStroker->setDashOffset(pen.dashOffset());
786 s->stroker = d->dashStroker.data();
791 s->flags.fast_pen = pen_style > Qt::NoPen
793 && !s->flags.antialiased
794 && (penWidth == 0 || (penWidth <= 1
795 && (s->matrix.type() <= QTransform::TxTranslate
796 || pen.isCosmetic())));
798 ensureState(); // needed because of tx_noshear...
799 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
809 void QRasterPaintEngine::brushOriginChanged()
811 QRasterPaintEngineState *s = state();
813 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
816 s->fillFlags |= DirtyBrushOrigin;
823 void QRasterPaintEngine::brushChanged()
825 QRasterPaintEngineState *s = state();
827 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
829 s->fillFlags |= DirtyBrush;
838 void QRasterPaintEngine::updateBrush(const QBrush &brush)
841 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
843 Q_D(QRasterPaintEngine);
844 QRasterPaintEngineState *s = state();
845 // must set clip prior to setup, as setup uses it...
846 s->brushData.clip = d->clip();
847 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
848 if (s->fillFlags & DirtyTransform
849 || brush.transform().type() >= QTransform::TxNone)
850 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
851 s->lastBrush = brush;
855 void QRasterPaintEngine::updateOutlineMapper()
857 Q_D(QRasterPaintEngine);
858 d->outlineMapper->setMatrix(state()->matrix);
861 void QRasterPaintEngine::updateState()
863 QRasterPaintEngineState *s = state();
865 if (s->dirty & DirtyTransform)
866 updateMatrix(s->matrix);
868 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
869 const QPainter::CompositionMode mode = s->composition_mode;
870 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
871 && s->intOpacity == 256
872 && (mode == QPainter::CompositionMode_Source
873 || (mode == QPainter::CompositionMode_SourceOver
874 && qAlpha(s->penData.solid.color) == 255));
884 void QRasterPaintEngine::opacityChanged()
886 QRasterPaintEngineState *s = state();
889 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
892 s->fillFlags |= DirtyOpacity;
893 s->strokeFlags |= DirtyOpacity;
894 s->pixmapFlags |= DirtyOpacity;
895 s->dirty |= DirtyOpacity;
896 s->intOpacity = (int) (s->opacity * 256);
902 void QRasterPaintEngine::compositionModeChanged()
904 Q_D(QRasterPaintEngine);
905 QRasterPaintEngineState *s = state();
908 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
911 s->fillFlags |= DirtyCompositionMode;
912 s->dirty |= DirtyCompositionMode;
914 s->strokeFlags |= DirtyCompositionMode;
915 d->rasterBuffer->compositionMode = s->composition_mode;
917 d->recalculateFastImages();
923 void QRasterPaintEngine::renderHintsChanged()
925 QRasterPaintEngineState *s = state();
928 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
931 bool was_aa = s->flags.antialiased;
932 bool was_bilinear = s->flags.bilinear;
934 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
935 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
937 if (was_aa != s->flags.antialiased)
938 s->strokeFlags |= DirtyHints;
940 if (was_bilinear != s->flags.bilinear) {
941 s->strokeFlags |= DirtyPen;
942 s->fillFlags |= DirtyBrush;
945 Q_D(QRasterPaintEngine);
946 d->recalculateFastImages();
952 void QRasterPaintEngine::transformChanged()
954 QRasterPaintEngineState *s = state();
957 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
960 s->fillFlags |= DirtyTransform;
961 s->strokeFlags |= DirtyTransform;
963 s->dirty |= DirtyTransform;
965 Q_D(QRasterPaintEngine);
966 d->recalculateFastImages();
972 void QRasterPaintEngine::clipEnabledChanged()
974 QRasterPaintEngineState *s = state();
977 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
981 s->clip->enabled = s->clipEnabled;
982 s->fillFlags |= DirtyClipEnabled;
983 s->strokeFlags |= DirtyClipEnabled;
984 s->pixmapFlags |= DirtyClipEnabled;
989 void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
991 rasterBuffer->prepare(device);
995 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
997 SrcOverBlendFunc func,
1002 if (alpha == 0 || !clip.isValid())
1005 Q_ASSERT(img.depth() >= 8);
1007 int srcBPL = img.bytesPerLine();
1008 const uchar *srcBits = img.bits();
1009 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1010 int iw = img.width();
1011 int ih = img.height();
1013 if (!sr.isEmpty()) {
1016 // Adjust the image according to the source offset...
1017 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1020 // adapt the x parameters
1021 int x = qRound(pt.x());
1023 int cx2 = clip.x() + clip.width();
1026 srcBits += srcSize * d;
1031 int d = x + iw - cx2;
1037 // adapt the y paremeters...
1039 int cy2 = clip.y() + clip.height();
1040 int y = qRound(pt.y());
1043 srcBits += srcBPL * d;
1048 int d = y + ih - cy2;
1054 // call the blend function...
1055 int dstSize = rasterBuffer->bytesPerPixel();
1056 int dstBPL = rasterBuffer->bytesPerLine();
1057 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1064 void QRasterPaintEnginePrivate::systemStateChanged()
1066 QRect clipRect(0, 0,
1067 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1068 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1070 if (!systemClip.isEmpty()) {
1071 QRegion clippedDeviceRgn = systemClip & clipRect;
1072 deviceRect = clippedDeviceRgn.boundingRect();
1073 baseClip->setClipRegion(clippedDeviceRgn);
1075 deviceRect = clipRect;
1076 baseClip->setClipRect(deviceRect);
1078 #ifdef QT_DEBUG_DRAW
1079 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1082 exDeviceRect = deviceRect;
1084 Q_Q(QRasterPaintEngine);
1085 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1086 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1087 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1090 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1092 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1095 Q_Q(QRasterPaintEngine);
1096 bool bilinear = q->state()->flags.bilinear;
1098 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1099 spanData->setupMatrix(b.transform() * m, bilinear);
1101 if (m.type() <= QTransform::TxTranslate) {
1102 // specialize setupMatrix for translation matrices
1103 // to avoid needless matrix inversion
1111 spanData->dx = -m.dx();
1112 spanData->dy = -m.dy();
1113 spanData->txop = m.type();
1114 spanData->bilinear = bilinear;
1115 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1116 spanData->adjustSpanMethods();
1118 spanData->setupMatrix(m, bilinear);
1123 // #define QT_CLIPPING_RATIOS
1125 #ifdef QT_CLIPPING_RATIOS
1130 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1132 if (d->clip()->hasRectClip)
1134 if (d->clip()->hasRegionClip)
1138 if ((totalClips % 5000) == 0) {
1139 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1140 rectClips * 100.0 / (qreal) totalClips,
1141 regionClips * 100.0 / (qreal) totalClips,
1142 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1151 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1153 if (s->flags.has_clip_ownership)
1156 s->flags.has_clip_ownership = false;
1159 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1161 s->fillFlags |= QPaintEngine::DirtyClipPath;
1162 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1163 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1165 d->solid_color_filler.clip = d->clip();
1166 d->solid_color_filler.adjustSpanMethods();
1168 #ifdef QT_DEBUG_DRAW
1169 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1178 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1180 #ifdef QT_DEBUG_DRAW
1181 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1183 if (path.elements()) {
1184 for (int i=0; i<path.elementCount(); ++i) {
1185 qDebug() << " - " << path.elements()[i]
1186 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1189 for (int i=0; i<path.elementCount(); ++i) {
1190 qDebug() << " ---- "
1191 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1196 Q_D(QRasterPaintEngine);
1197 QRasterPaintEngineState *s = state();
1199 const qreal *points = path.points();
1200 const QPainterPath::ElementType *types = path.elements();
1202 // There are some cases that are not supported by clip(QRect)
1203 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1204 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1205 if (s->matrix.type() <= QTransform::TxScale
1206 && ((path.shape() == QVectorPath::RectangleHint)
1207 || (isRect(points, path.elementCount())
1208 && (!types || (types[0] == QPainterPath::MoveToElement
1209 && types[1] == QPainterPath::LineToElement
1210 && types[2] == QPainterPath::LineToElement
1211 && types[3] == QPainterPath::LineToElement))))) {
1212 #ifdef QT_DEBUG_DRAW
1213 qDebug() << " --- optimizing vector clip to rect clip...";
1216 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1217 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1222 if (op == Qt::NoClip) {
1223 qrasterpaintengine_state_setNoClip(s);
1226 QClipData *base = d->baseClip.data();
1228 // Intersect with current clip when available...
1229 if (op == Qt::IntersectClip && s->clip)
1232 // We always intersect, except when there is nothing to
1233 // intersect with, in which case we simplify the operation to
1235 Qt::ClipOperation isectOp = Qt::IntersectClip;
1237 isectOp = Qt::ReplaceClip;
1239 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1240 newClip->initialize();
1241 ClipData clipData = { base, newClip, isectOp };
1242 ensureOutlineMapper();
1243 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1247 if (op == Qt::UniteClip) {
1249 QClipData *result = new QClipData(d->rasterBuffer->height());
1250 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1251 qt_merge_clip(current, newClip, result);
1259 if (s->flags.has_clip_ownership)
1263 s->flags.has_clip_ownership = true;
1265 qrasterpaintengine_dirty_clip(d, s);
1273 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1275 #ifdef QT_DEBUG_DRAW
1276 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1279 QRasterPaintEngineState *s = state();
1281 if (op == Qt::NoClip) {
1282 qrasterpaintengine_state_setNoClip(s);
1284 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1285 QPaintEngineEx::clip(rect, op);
1288 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1289 QPaintEngineEx::clip(rect, op);
1295 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1297 Q_D(QRasterPaintEngine);
1298 QRect clipRect = r & d->deviceRect;
1299 QRasterPaintEngineState *s = state();
1301 if (op == Qt::ReplaceClip || s->clip == 0) {
1303 // No current clip, hence we intersect with sysclip and be
1305 QRegion clipRegion = systemClip();
1306 QClipData *clip = new QClipData(d->rasterBuffer->height());
1308 if (clipRegion.isEmpty())
1309 clip->setClipRect(clipRect);
1311 clip->setClipRegion(clipRegion & clipRect);
1313 if (s->flags.has_clip_ownership)
1317 s->clip->enabled = true;
1318 s->flags.has_clip_ownership = true;
1320 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1321 QClipData *base = s->clip;
1324 if (base->hasRectClip || base->hasRegionClip) {
1325 if (!s->flags.has_clip_ownership) {
1326 s->clip = new QClipData(d->rasterBuffer->height());
1327 s->flags.has_clip_ownership = true;
1329 if (base->hasRectClip)
1330 s->clip->setClipRect(base->clipRect & clipRect);
1332 s->clip->setClipRegion(base->clipRegion & clipRect);
1333 s->clip->enabled = true;
1341 qrasterpaintengine_dirty_clip(d, s);
1349 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1351 #ifdef QT_DEBUG_DRAW
1352 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1355 Q_D(QRasterPaintEngine);
1357 if (region.rectCount() == 1) {
1358 clip(region.boundingRect(), op);
1362 QRasterPaintEngineState *s = state();
1363 const QClipData *clip = d->clip();
1364 const QClipData *baseClip = d->baseClip.data();
1366 if (op == Qt::NoClip) {
1367 qrasterpaintengine_state_setNoClip(s);
1368 } else if (s->matrix.type() > QTransform::TxScale
1369 || op == Qt::UniteClip
1370 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1371 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1372 QPaintEngineEx::clip(region, op);
1374 const QClipData *curClip;
1377 if (op == Qt::IntersectClip)
1382 if (s->flags.has_clip_ownership) {
1386 newClip = new QClipData(d->rasterBuffer->height());
1388 s->flags.has_clip_ownership = true;
1391 QRegion r = s->matrix.map(region);
1392 if (curClip->hasRectClip)
1393 newClip->setClipRegion(r & curClip->clipRect);
1394 else if (curClip->hasRegionClip)
1395 newClip->setClipRegion(r & curClip->clipRegion);
1397 qrasterpaintengine_dirty_clip(d, s);
1404 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1406 #ifdef QT_DEBUG_DRAW
1407 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1410 if (!fillData->blend)
1413 Q_D(QRasterPaintEngine);
1415 const QRectF controlPointRect = path.controlPointRect();
1417 QRasterPaintEngineState *s = state();
1418 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1419 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1420 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1421 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1422 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1423 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1425 if (!s->flags.antialiased && !do_clip) {
1426 d->initializeRasterizer(fillData);
1427 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1431 ensureOutlineMapper();
1432 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1435 static void fillRect_normalized(const QRect &r, QSpanData *data,
1436 QRasterPaintEnginePrivate *pe)
1440 bool rectClipped = true;
1443 x1 = qMax(r.x(), data->clip->xmin);
1444 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1445 y1 = qMax(r.y(), data->clip->ymin);
1446 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1447 rectClipped = data->clip->hasRectClip;
1450 x1 = qMax(r.x(), pe->deviceRect.x());
1451 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1452 y1 = qMax(r.y(), pe->deviceRect.y());
1453 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1455 x1 = qMax(r.x(), 0);
1456 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1457 y1 = qMax(r.y(), 0);
1458 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1461 if (x2 <= x1 || y2 <= y1)
1464 const int width = x2 - x1;
1465 const int height = y2 - y1;
1467 bool isUnclipped = rectClipped
1468 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1470 if (pe && isUnclipped) {
1471 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1473 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1474 || (mode == QPainter::CompositionMode_SourceOver
1475 && qAlpha(data->solid.color) == 255)))
1477 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1483 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1485 const int nspans = 256;
1486 QT_FT_Span spans[nspans];
1488 Q_ASSERT(data->blend);
1491 int n = qMin(nspans, y2 - y);
1495 spans[i].len = width;
1497 spans[i].coverage = 255;
1501 blend(n, spans, data);
1509 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1511 #ifdef QT_DEBUG_DRAW
1512 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1514 Q_D(QRasterPaintEngine);
1515 QRasterPaintEngineState *s = state();
1519 if (s->brushData.blend) {
1520 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1521 const QRect *r = rects;
1522 const QRect *lastRect = rects + rectCount;
1524 int offset_x = int(s->matrix.dx());
1525 int offset_y = int(s->matrix.dy());
1526 while (r < lastRect) {
1527 QRect rect = r->normalized();
1528 QRect rr = rect.translated(offset_x, offset_y);
1529 fillRect_normalized(rr, &s->brushData, d);
1533 QRectVectorPath path;
1534 for (int i=0; i<rectCount; ++i) {
1536 fill(path, s->brush);
1542 if (s->penData.blend) {
1543 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1544 const QRect *r = rects;
1545 const QRect *lastRect = rects + rectCount;
1546 while (r < lastRect) {
1548 int right = r->x() + r->width();
1550 int bottom = r->y() + r->height();
1553 int pts[] = { top, left,
1558 int pts[] = { left, top,
1564 strokePolygonCosmetic((QPoint *) pts, 4, WindingMode);
1568 QRectVectorPath path;
1569 for (int i = 0; i < rectCount; ++i) {
1571 stroke(path, s->pen);
1580 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1582 #ifdef QT_DEBUG_DRAW
1583 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1585 #ifdef QT_FAST_SPANS
1586 Q_D(QRasterPaintEngine);
1587 QRasterPaintEngineState *s = state();
1591 if (s->flags.tx_noshear) {
1593 if (s->brushData.blend) {
1594 d->initializeRasterizer(&s->brushData);
1595 for (int i = 0; i < rectCount; ++i) {
1596 const QRectF &rect = rects[i].normalized();
1599 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1600 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1601 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1606 if (s->penData.blend) {
1607 qreal width = s->pen.isCosmetic()
1608 ? (s->lastPen.widthF() == 0 ? 1 : s->lastPen.widthF())
1609 : s->lastPen.widthF() * s->txscale;
1611 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1612 for (int i = 0; i < rectCount; ++i) {
1613 const QRectF &r = rects[i];
1615 qreal right = r.x() + r.width();
1617 qreal bottom = r.y() + r.height();
1618 qreal pts[] = { left, top,
1622 strokePolygonCosmetic((QPointF *) pts, 4, WindingMode);
1624 } else if (width <= 1 && qpen_style(s->lastPen) == Qt::SolidLine) {
1625 d->initializeRasterizer(&s->penData);
1627 for (int i = 0; i < rectCount; ++i) {
1628 const QRectF &rect = rects[i].normalized();
1629 if (rect.isEmpty()) {
1630 qreal pts[] = { rect.left(), rect.top(), rect.right(), rect.bottom() };
1631 QVectorPath vp(pts, 2, 0, QVectorPath::LinesHint);
1632 QPaintEngineEx::stroke(vp, s->lastPen);
1634 const QPointF tl = s->matrix.map(rect.topLeft());
1635 const QPointF tr = s->matrix.map(rect.topRight());
1636 const QPointF bl = s->matrix.map(rect.bottomLeft());
1637 const QPointF br = s->matrix.map(rect.bottomRight());
1638 const qreal w = width / (rect.width() * s->txscale);
1639 const qreal h = width / (rect.height() * s->txscale);
1640 d->rasterizer->rasterizeLine(tl, tr, w); // top
1641 d->rasterizer->rasterizeLine(bl, br, w); // bottom
1642 d->rasterizer->rasterizeLine(bl, tl, h); // left
1643 d->rasterizer->rasterizeLine(br, tr, h); // right
1647 for (int i = 0; i < rectCount; ++i) {
1648 const QRectF &r = rects[i];
1650 qreal right = r.x() + r.width();
1652 qreal bottom = r.y() + r.height();
1653 qreal pts[] = { left, top,
1658 QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
1659 QPaintEngineEx::stroke(vp, s->lastPen);
1666 #endif // QT_FAST_SPANS
1667 QPaintEngineEx::drawRects(rects, rectCount);
1674 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1676 QRasterPaintEngineState *s = state();
1678 if (!s->penData.blend)
1681 if (s->flags.fast_pen && !path.isCurved()
1682 && s->lastPen.brush().isOpaque()) {
1683 int count = path.elementCount();
1684 QPointF *points = (QPointF *) path.points();
1685 const QPainterPath::ElementType *types = path.elements();
1689 while (first < count) {
1690 while (first < count && types[first] != QPainterPath::MoveToElement) ++first;
1692 while (last < count && types[last] == QPainterPath::LineToElement) ++last;
1693 strokePolygonCosmetic(points + first, last - first,
1694 path.hasImplicitClose() && last == count // only close last one..
1700 strokePolygonCosmetic(points, count,
1701 path.hasImplicitClose()
1706 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1707 qreal width = s->lastPen.isCosmetic()
1708 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1709 : qpen_widthf(s->lastPen) * s->txscale;
1711 qreal dashOffset = s->lastPen.dashOffset();
1713 qreal patternLength = 0;
1714 const QVector<qreal> pattern = s->lastPen.dashPattern();
1715 for (int i = 0; i < pattern.size(); ++i)
1716 patternLength += pattern.at(i);
1718 if (patternLength > 0) {
1719 int n = qFloor(dashOffset / patternLength);
1720 dashOffset -= n * patternLength;
1721 while (dashOffset >= pattern.at(dashIndex)) {
1722 dashOffset -= pattern.at(dashIndex);
1723 if (++dashIndex >= pattern.size())
1729 Q_D(QRasterPaintEngine);
1730 d->initializeRasterizer(&s->penData);
1731 int lineCount = path.elementCount() / 2;
1732 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1734 for (int i = 0; i < lineCount; ++i) {
1735 if (lines[i].p1() == lines[i].p2()) {
1736 if (s->lastPen.capStyle() != Qt::FlatCap) {
1737 QPointF p = lines[i].p1();
1738 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1739 QPointF(p.x() + width*0.5, p.y())));
1740 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1745 const QLineF line = s->matrix.map(lines[i]);
1746 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1747 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1748 width / line.length(),
1749 s->lastPen.capStyle() == Qt::SquareCap);
1751 d->rasterizeLine_dashed(line, width,
1752 &dashIndex, &dashOffset, &inDash);
1757 QPaintEngineEx::stroke(path, pen);
1760 static inline QRect toNormalizedFillRect(const QRectF &rect)
1762 int x1 = qRound(rect.x());
1763 int y1 = qRound(rect.y());
1764 int x2 = qRound(rect.right());
1765 int y2 = qRound(rect.bottom());
1772 return QRect(x1, y1, x2 - x1, y2 - y1);
1778 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1782 #ifdef QT_DEBUG_DRAW
1783 QRectF rf = path.controlPointRect();
1784 qDebug() << "QRasterPaintEngine::fill(): "
1785 << "size=" << path.elementCount()
1786 << ", hints=" << hex << path.hints()
1790 Q_D(QRasterPaintEngine);
1791 QRasterPaintEngineState *s = state();
1794 if (!s->brushData.blend)
1797 if (path.shape() == QVectorPath::RectangleHint) {
1798 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1799 const qreal *p = path.points();
1800 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1801 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1802 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1806 if (s->flags.tx_noshear) {
1807 d->initializeRasterizer(&s->brushData);
1808 // ### Is normalizing really necessary here?
1809 const qreal *p = path.points();
1810 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1812 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1813 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1814 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1820 if (path.shape() == QVectorPath::EllipseHint) {
1821 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1822 const qreal *p = path.points();
1823 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1824 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1825 QRectF r = s->matrix.mapRect(QRectF(tl, br));
1827 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
1828 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
1829 const QRect brect = QRect(int(r.x()), int(r.y()),
1830 int_dim(r.x(), r.width()),
1831 int_dim(r.y(), r.height()));
1833 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
1834 &s->penData, &s->brushData);
1840 // ### Optimize for non transformed ellipses and rectangles...
1841 QRectF cpRect = path.controlPointRect();
1842 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1843 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1846 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1847 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1848 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1849 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1851 // ### Falonc: implement....
1852 // if (!s->flags.antialiased && !do_clip) {
1853 // d->initializeRasterizer(&s->brushData);
1854 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1858 ensureOutlineMapper();
1859 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1862 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1864 Q_D(QRasterPaintEngine);
1865 QRasterPaintEngineState *s = state();
1867 if (!s->flags.antialiased) {
1868 uint txop = s->matrix.type();
1869 if (txop == QTransform::TxNone) {
1870 fillRect_normalized(toNormalizedFillRect(r), data, d);
1872 } else if (txop == QTransform::TxTranslate) {
1873 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1874 fillRect_normalized(rr, data, d);
1876 } else if (txop == QTransform::TxScale) {
1877 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1878 fillRect_normalized(rr, data, d);
1883 if (s->flags.tx_noshear) {
1884 d->initializeRasterizer(data);
1885 QRectF nr = r.normalized();
1886 if (!nr.isEmpty()) {
1887 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1888 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1889 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1896 ensureOutlineMapper();
1897 fillPath(path, data);
1903 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1905 #ifdef QT_DEBUG_DRAW
1906 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1908 QRasterPaintEngineState *s = state();
1911 if (!s->brushData.blend)
1914 fillRect(r, &s->brushData);
1920 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1922 #ifdef QT_DEBUG_DRAW
1923 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1925 Q_D(QRasterPaintEngine);
1926 QRasterPaintEngineState *s = state();
1928 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1929 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1930 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1933 d->solid_color_filler.clip = d->clip();
1934 d->solid_color_filler.adjustSpanMethods();
1935 fillRect(r, &d->solid_color_filler);
1938 static inline bool isAbove(const QPointF *a, const QPointF *b)
1940 return a->y() < b->y();
1943 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1948 Q_ASSERT(pointCount >= 2);
1950 QVector<const QPointF *> sorted;
1951 sorted.reserve(pointCount);
1953 upper->reserve(pointCount * 3 / 4);
1954 lower->reserve(pointCount * 3 / 4);
1956 for (int i = 0; i < pointCount; ++i)
1957 sorted << points + i;
1959 qSort(sorted.begin(), sorted.end(), isAbove);
1961 qreal splitY = sorted.at(sorted.size() / 2)->y();
1963 const QPointF *end = points + pointCount;
1964 const QPointF *last = end - 1;
1966 QVector<QPointF> *bin[2] = { upper, lower };
1968 for (const QPointF *p = points; p < end; ++p) {
1969 int side = p->y() < splitY;
1970 int lastSide = last->y() < splitY;
1972 if (side != lastSide) {
1973 if (qFuzzyCompare(p->y(), splitY)) {
1974 bin[!side]->append(*p);
1975 } else if (qFuzzyCompare(last->y(), splitY)) {
1976 bin[side]->append(*last);
1978 QPointF delta = *p - *last;
1979 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1981 bin[0]->append(intersection);
1982 bin[1]->append(intersection);
1986 bin[side]->append(*p);
1991 // give up if we couldn't reduce the point count
1992 return upper->size() < pointCount && lower->size() < pointCount;
1998 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2000 Q_D(QRasterPaintEngine);
2001 QRasterPaintEngineState *s = state();
2003 const int maxPoints = 0xffff;
2005 // max amount of points that raster engine can reliably handle
2006 if (pointCount > maxPoints) {
2007 QVector<QPointF> upper, lower;
2009 if (splitPolygon(points, pointCount, &upper, &lower)) {
2010 fillPolygon(upper.constData(), upper.size(), mode);
2011 fillPolygon(lower.constData(), lower.size(), mode);
2013 qWarning("Polygon too complex for filling.");
2018 // Compose polygon fill..,
2019 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2020 ensureOutlineMapper();
2021 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
2024 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2026 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
2032 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2034 QRasterPaintEngineState *s = state();
2036 #ifdef QT_DEBUG_DRAW
2037 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
2038 for (int i=0; i<pointCount; ++i)
2039 qDebug() << " - " << points[i];
2041 Q_ASSERT(pointCount >= 2);
2043 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
2044 QRectF r(points[0], points[2]);
2051 if (mode != PolylineMode) {
2053 if (s->brushData.blend) {
2054 fillPolygon(points, pointCount, mode);
2058 // Do the outline...
2059 if (s->penData.blend) {
2060 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2061 strokePolygonCosmetic(points, pointCount, mode);
2063 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2064 QPaintEngineEx::stroke(vp, s->lastPen);
2072 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2074 Q_D(QRasterPaintEngine);
2075 QRasterPaintEngineState *s = state();
2077 #ifdef QT_DEBUG_DRAW
2078 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
2079 for (int i=0; i<pointCount; ++i)
2080 qDebug() << " - " << points[i];
2082 Q_ASSERT(pointCount >= 2);
2083 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
2084 QRect r(points[0].x(),
2086 points[2].x() - points[0].x(),
2087 points[2].y() - points[0].y());
2094 if (!(s->flags.int_xform && s->flags.fast_pen && (!s->penData.blend || s->pen.brush().isOpaque()))) {
2095 // this calls the float version
2096 QPaintEngineEx::drawPolygon(points, pointCount, mode);
2101 if (mode != PolylineMode) {
2103 if (s->brushData.blend) {
2104 // Compose polygon fill..,
2105 ensureOutlineMapper();
2106 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
2107 d->outlineMapper->moveTo(*points);
2108 const QPoint *p = points;
2109 const QPoint *ep = points + pointCount - 1;
2111 d->outlineMapper->lineTo(*(++p));
2113 d->outlineMapper->endOutline();
2116 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2118 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2122 // Do the outline...
2123 if (s->penData.blend) {
2124 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2125 strokePolygonCosmetic(points, pointCount, mode);
2127 int count = pointCount * 2;
2128 QVarLengthArray<qreal> fpoints(count);
2130 for (int i=0; i<count; i+=2) {
2131 fpoints[i] = ((int *) points)[i+1];
2132 fpoints[i+1] = ((int *) points)[i];
2135 for (int i=0; i<count; ++i)
2136 fpoints[i] = ((int *) points)[i];
2138 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2139 QPaintEngineEx::stroke(vp, s->lastPen);
2147 void QRasterPaintEngine::strokePolygonCosmetic(const QPointF *points, int pointCount, PolygonDrawMode mode)
2149 Q_D(QRasterPaintEngine);
2150 QRasterPaintEngineState *s = state();
2152 Q_ASSERT(s->penData.blend);
2153 Q_ASSERT(s->flags.fast_pen);
2155 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2157 // Use fast path for 0 width / trivial pens.
2159 devRect.set(d->deviceRect);
2161 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2162 ? LineDrawIncludeLastPixel
2164 int dashOffset = int(s->lastPen.dashOffset());
2166 // Draw all the line segments.
2167 for (int i=1; i<pointCount; ++i) {
2169 QPointF lp1 = points[i-1] * s->matrix;
2170 QPointF lp2 = points[i] * s->matrix;
2172 const QRectF brect(lp1, lp2);
2173 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2174 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2175 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2176 qFloor(lp2.x()), qFloor(lp2.y()),
2177 penBlend, &s->penData,
2178 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2181 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2182 qFloor(lp2.x()), qFloor(lp2.y()),
2184 penBlend, &s->penData,
2185 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2186 devRect, &dashOffset);
2190 // Polygons are implicitly closed.
2191 if (needs_closing) {
2192 QPointF lp1 = points[pointCount-1] * s->matrix;
2193 QPointF lp2 = points[0] * s->matrix;
2195 const QRectF brect(lp1, lp2);
2196 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2197 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2198 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2199 qFloor(lp2.x()), qFloor(lp2.y()),
2200 penBlend, &s->penData,
2201 LineDrawIncludeLastPixel,
2204 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2205 qFloor(lp2.x()), qFloor(lp2.y()),
2207 penBlend, &s->penData,
2208 LineDrawIncludeLastPixel,
2209 devRect, &dashOffset);
2218 void QRasterPaintEngine::strokePolygonCosmetic(const QPoint *points, int pointCount, PolygonDrawMode mode)
2220 Q_D(QRasterPaintEngine);
2221 QRasterPaintEngineState *s = state();
2223 // We assert here because this function is called from drawRects
2224 // and drawPolygon and they already do ensurePen(), so we skip that
2225 // here to avoid duplicate checks..
2226 Q_ASSERT(s->penData.blend);
2228 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2231 devRect.set(d->deviceRect);
2233 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2234 ? LineDrawIncludeLastPixel
2237 int m11 = int(s->matrix.m11());
2238 int m22 = int(s->matrix.m22());
2239 int dx = int(s->matrix.dx());
2240 int dy = int(s->matrix.dy());
2241 int m13 = int(s->matrix.m13());
2242 int m23 = int(s->matrix.m23());
2243 bool affine = !m13 && !m23;
2245 int dashOffset = int(s->lastPen.dashOffset());
2248 // Draw all the line segments.
2249 for (int i=1; i<pointCount; ++i) {
2250 const QPoint lp1 = points[i-1] * s->matrix;
2251 const QPoint lp2 = points[i] * s->matrix;
2252 const QRect brect(lp1, lp2);
2253 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2255 if (qpen_style(s->lastPen) == Qt::SolidLine)
2256 drawLine_midpoint_i(lp1.x(), lp1.y(),
2258 penBlend, &s->penData,
2259 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2262 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2265 penBlend, &s->penData,
2266 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2267 devRect, &dashOffset);
2271 // Polygons are implicitly closed.
2272 if (needs_closing) {
2273 const QPoint lp1 = points[pointCount - 1] * s->matrix;
2274 const QPoint lp2 = points[0] * s->matrix;
2275 const QRect brect(lp1, lp2);
2276 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2278 if (qpen_style(s->lastPen) == Qt::SolidLine)
2279 drawLine_midpoint_i(lp1.x(), lp1.y(),
2281 penBlend, &s->penData, LineDrawIncludeLastPixel,
2284 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2287 penBlend, &s->penData, LineDrawIncludeLastPixel,
2288 devRect, &dashOffset);
2291 // Draw all the line segments.
2292 for (int i=1; i<pointCount; ++i) {
2293 int x1 = points[i-1].x() * m11 + dx;
2294 int y1 = points[i-1].y() * m22 + dy;
2295 qreal w = m13*points[i-1].x() + m23*points[i-1].y() + 1.;
2299 int x2 = points[i].x() * m11 + dx;
2300 int y2 = points[i].y() * m22 + dy;
2301 w = m13*points[i].x() + m23*points[i].y() + 1.;
2306 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2307 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2308 if (qpen_style(s->lastPen) == Qt::SolidLine)
2309 drawLine_midpoint_i(x1, y1, x2, y2,
2310 penBlend, &s->penData,
2311 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2314 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2316 penBlend, &s->penData,
2317 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2318 devRect, &dashOffset);
2322 int x1 = points[pointCount-1].x() * m11 + dx;
2323 int y1 = points[pointCount-1].y() * m22 + dy;
2324 qreal w = m13*points[pointCount-1].x() + m23*points[pointCount-1].y() + 1.;
2328 int x2 = points[0].x() * m11 + dx;
2329 int y2 = points[0].y() * m22 + dy;
2330 w = m13*points[0].x() + m23*points[0].y() + 1.;
2334 // Polygons are implicitly closed.
2336 if (needs_closing) {
2337 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2338 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2339 if (qpen_style(s->lastPen) == Qt::SolidLine)
2340 drawLine_midpoint_i(x1, y1, x2, y2,
2341 penBlend, &s->penData, LineDrawIncludeLastPixel,
2344 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2346 penBlend, &s->penData, LineDrawIncludeLastPixel,
2347 devRect, &dashOffset);
2355 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2357 #ifdef QT_DEBUG_DRAW
2358 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2361 QPixmapData *pd = pixmap.pixmapData();
2362 if (pd->classId() == QPixmapData::RasterClass) {
2363 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2364 if (image.depth() == 1) {
2365 Q_D(QRasterPaintEngine);
2366 QRasterPaintEngineState *s = state();
2367 if (s->matrix.type() <= QTransform::TxTranslate) {
2369 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2371 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2374 QRasterPaintEngine::drawImage(pos, image);
2377 const QImage image = pixmap.toImage();
2378 if (pixmap.depth() == 1) {
2379 Q_D(QRasterPaintEngine);
2380 QRasterPaintEngineState *s = state();
2381 if (s->matrix.type() <= QTransform::TxTranslate) {
2383 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2385 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2388 QRasterPaintEngine::drawImage(pos, image);
2396 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2398 #ifdef QT_DEBUG_DRAW
2399 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2402 QPixmapData* pd = pixmap.pixmapData();
2403 if (pd->classId() == QPixmapData::RasterClass) {
2404 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2405 if (image.depth() == 1) {
2406 Q_D(QRasterPaintEngine);
2407 QRasterPaintEngineState *s = state();
2408 if (s->matrix.type() <= QTransform::TxTranslate
2409 && r.size() == sr.size()
2410 && r.size() == pixmap.size()) {
2412 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2415 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2418 drawImage(r, image, sr);
2421 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2422 const QImage image = pd->toImage(clippedSource);
2423 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2424 if (image.depth() == 1) {
2425 Q_D(QRasterPaintEngine);
2426 QRasterPaintEngineState *s = state();
2427 if (s->matrix.type() <= QTransform::TxTranslate
2428 && r.size() == sr.size()
2429 && r.size() == pixmap.size()) {
2431 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2434 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2437 drawImage(r, image, translatedSource);
2442 // assumes that rect has positive width and height
2443 static inline const QRect toRect_normalized(const QRectF &rect)
2445 const int x = qRound(rect.x());
2446 const int y = qRound(rect.y());
2447 const int w = int(rect.width() + qreal(0.5));
2448 const int h = int(rect.height() + qreal(0.5));
2450 return QRect(x, y, w, h);
2453 static inline int fast_ceil_positive(const qreal &v)
2455 const int iv = int(v);
2462 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2464 const int xmin = int(rect.x());
2465 const int xmax = int(fast_ceil_positive(rect.right()));
2466 const int ymin = int(rect.y());
2467 const int ymax = int(fast_ceil_positive(rect.bottom()));
2468 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2474 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2476 #ifdef QT_DEBUG_DRAW
2477 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2480 Q_D(QRasterPaintEngine);
2481 QRasterPaintEngineState *s = state();
2483 if (s->matrix.type() > QTransform::TxTranslate) {
2484 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2486 QRectF(0, 0, img.width(), img.height()));
2489 const QClipData *clip = d->clip();
2490 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2492 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2493 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2496 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2498 } else if (clip->hasRectClip) {
2499 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2507 d->image_filler.clip = clip;
2508 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2509 if (!d->image_filler.blend)
2511 d->image_filler.dx = -pt.x();
2512 d->image_filler.dy = -pt.y();
2513 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2515 fillRect_normalized(rr, &d->image_filler, d);
2520 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2522 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2533 inline RotationType qRotationType(const QTransform &transform)
2535 QTransform::TransformationType type = transform.type();
2537 if (type > QTransform::TxRotate)
2540 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2541 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2544 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2545 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2548 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2549 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2555 inline bool isPixelAligned(const QRectF &rect) {
2556 return QRectF(rect.toRect()) == rect;
2563 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2564 Qt::ImageConversionFlags)
2566 #ifdef QT_DEBUG_DRAW
2567 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2573 Q_D(QRasterPaintEngine);
2574 QRasterPaintEngineState *s = state();
2575 int sr_l = qFloor(sr.left());
2576 int sr_r = qCeil(sr.right()) - 1;
2577 int sr_t = qFloor(sr.top());
2578 int sr_b = qCeil(sr.bottom()) - 1;
2580 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2581 QTransform old = s->matrix;
2583 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2584 QRgb color = img.pixel(sr_l, sr_t);
2585 switch (img.format()) {
2586 case QImage::Format_ARGB32_Premultiplied:
2587 case QImage::Format_ARGB8565_Premultiplied:
2588 case QImage::Format_ARGB6666_Premultiplied:
2589 case QImage::Format_ARGB8555_Premultiplied:
2590 case QImage::Format_ARGB4444_Premultiplied:
2591 // Combine premultiplied color with the opacity set on the painter.
2592 d->solid_color_filler.solid.color =
2593 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2594 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2597 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2601 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2602 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2606 d->solid_color_filler.clip = d->clip();
2607 d->solid_color_filler.adjustSpanMethods();
2608 fillRect(r, &d->solid_color_filler);
2614 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2616 const QClipData *clip = d->clip();
2618 if (s->matrix.type() > QTransform::TxTranslate
2620 && (!clip || clip->hasRectClip)
2621 && s->intOpacity == 256
2622 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2623 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2624 && d->rasterBuffer->format == img.format()
2625 && (d->rasterBuffer->format == QImage::Format_RGB16
2626 || d->rasterBuffer->format == QImage::Format_RGB32
2627 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2628 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2630 RotationType rotationType = qRotationType(s->matrix);
2632 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2633 QRectF transformedTargetRect = s->matrix.mapRect(r);
2635 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2636 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2638 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2639 if (clippedTransformedTargetRect.isNull())
2642 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2644 QRect clippedSourceRect
2645 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2646 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2648 uint dbpl = d->rasterBuffer->bytesPerLine();
2649 uint sbpl = img.bytesPerLine();
2651 uchar *dst = d->rasterBuffer->buffer();
2652 uint bpp = img.depth() >> 3;
2654 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2655 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2657 uint cw = clippedSourceRect.width();
2658 uint ch = clippedSourceRect.height();
2660 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2667 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2669 QRectF targetBounds = s->matrix.mapRect(r);
2670 bool exceedsPrecision = targetBounds.width() > 0xffff
2671 || targetBounds.height() > 0xffff;
2673 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2674 if (s->matrix.type() > QTransform::TxScale) {
2675 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2676 if (func && (!clip || clip->hasRectClip)) {
2677 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2678 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2679 s->matrix, s->intOpacity);
2683 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2684 if (func && (!clip || clip->hasRectClip)) {
2685 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2686 img.bits(), img.bytesPerLine(),
2687 qt_mapRect_non_normalizing(r, s->matrix), sr,
2688 !clip ? d->deviceRect : clip->clipRect,
2695 QTransform copy = s->matrix;
2696 copy.translate(r.x(), r.y());
2698 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2699 copy.translate(-sr.x(), -sr.y());
2701 d->image_filler_xform.clip = clip;
2702 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2703 if (!d->image_filler_xform.blend)
2705 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2707 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2708 QRectF rr = s->matrix.mapRect(r);
2710 const int x1 = qRound(rr.x());
2711 const int y1 = qRound(rr.y());
2712 const int x2 = qRound(rr.right());
2713 const int y2 = qRound(rr.bottom());
2715 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2719 #ifdef QT_FAST_SPANS
2721 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2722 d->initializeRasterizer(&d->image_filler_xform);
2723 d->rasterizer->setAntialiased(s->flags.antialiased);
2725 const QRectF &rect = r.normalized();
2726 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2727 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2729 if (s->flags.tx_noshear)
2730 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2732 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2738 QTransform m = s->matrix;
2739 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2740 m.m21(), m.m22(), m.m23(),
2741 m.m31(), m.m32(), m.m33());
2742 fillPath(path, &d->image_filler_xform);
2745 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2746 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2748 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2750 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2752 } else if (clip->hasRectClip) {
2753 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2759 d->image_filler.clip = clip;
2760 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2761 if (!d->image_filler.blend)
2763 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2764 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2767 rr.translate(s->matrix.dx(), s->matrix.dy());
2769 const int x1 = qRound(rr.x());
2770 const int y1 = qRound(rr.y());
2771 const int x2 = qRound(rr.right());
2772 const int y2 = qRound(rr.bottom());
2774 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2781 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2783 #ifdef QT_DEBUG_DRAW
2784 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2786 Q_D(QRasterPaintEngine);
2787 QRasterPaintEngineState *s = state();
2791 QPixmapData *pd = pixmap.pixmapData();
2792 if (pd->classId() == QPixmapData::RasterClass) {
2793 image = static_cast<QRasterPixmapData *>(pd)->image;
2795 image = pixmap.toImage();
2798 if (image.depth() == 1)
2799 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2801 if (s->matrix.type() > QTransform::TxTranslate) {
2802 QTransform copy = s->matrix;
2803 copy.translate(r.x(), r.y());
2804 copy.translate(-sr.x(), -sr.y());
2805 d->image_filler_xform.clip = d->clip();
2806 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2807 if (!d->image_filler_xform.blend)
2809 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2811 #ifdef QT_FAST_SPANS
2813 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2814 d->initializeRasterizer(&d->image_filler_xform);
2815 d->rasterizer->setAntialiased(s->flags.antialiased);
2817 const QRectF &rect = r.normalized();
2818 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2819 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2820 if (s->flags.tx_noshear)
2821 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2823 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2829 fillPath(path, &d->image_filler_xform);
2831 d->image_filler.clip = d->clip();
2833 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2834 if (!d->image_filler.blend)
2836 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2837 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2840 rr.translate(s->matrix.dx(), s->matrix.dy());
2841 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2847 static inline bool monoVal(const uchar* s, int x)
2849 return (s[x>>3] << (x&7)) & 0x80;
2855 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2857 Q_D(QRasterPaintEngine);
2858 QRasterPaintEngineState *s = state();
2860 if (!s->penData.blend)
2863 QRasterBuffer *rb = d->rasterBuffer.data();
2865 const QRect rect(rx, ry, w, h);
2866 const QClipData *clip = d->clip();
2867 bool unclipped = false;
2869 // inlined QRect::intersects
2870 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2871 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2873 if (clip->hasRectClip) {
2874 unclipped = rx > clip->xmin
2875 && rx + w < clip->xmax
2877 && ry + h < clip->ymax;
2883 // inlined QRect::intersects
2884 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2885 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2889 // inlined QRect::contains
2890 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2891 && rect.top() >= 0 && rect.bottom() < rb->height();
2893 unclipped = contains && d->isUnclipped_normalized(rect);
2896 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2897 const uchar * scanline = static_cast<const uchar *>(src);
2899 if (s->flags.fast_text) {
2902 if (s->penData.bitmapBlit) {
2903 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2904 scanline, w, h, bpl);
2907 } else if (depth == 8) {
2908 if (s->penData.alphamapBlit) {
2909 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2910 scanline, w, h, bpl, 0);
2913 } else if (depth == 32) {
2914 // (A)RGB Alpha mask where the alpha component is not used.
2915 if (s->penData.alphaRGBBlit) {
2916 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2917 (const uint *) scanline, w, h, bpl / 4, 0);
2921 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2922 // (A)RGB Alpha mask where the alpha component is not used.
2924 int nx = qMax(0, rx);
2925 int ny = qMax(0, ry);
2927 // Move scanline pointer to compensate for moved x and y
2928 int xdiff = nx - rx;
2929 int ydiff = ny - ry;
2930 scanline += ydiff * bpl;
2931 scanline += xdiff * (depth == 32 ? 4 : 1);
2936 if (nx + w > d->rasterBuffer->width())
2937 w = d->rasterBuffer->width() - nx;
2938 if (ny + h > d->rasterBuffer->height())
2939 h = d->rasterBuffer->height() - ny;
2944 if (depth == 8 && s->penData.alphamapBlit) {
2945 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2946 scanline, w, h, bpl, clip);
2947 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2948 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2949 (const uint *) scanline, w, h, bpl / 4, clip);
2964 scanline += bpl * y0;
2968 w = qMin(w, rb->width() - qMax(0, rx));
2969 h = qMin(h, rb->height() - qMax(0, ry));
2971 if (w <= 0 || h <= 0)
2974 const int NSPANS = 256;
2975 QSpan spans[NSPANS];
2978 const int x1 = x0 + w;
2979 const int y1 = y0 + h;
2982 for (int y = y0; y < y1; ++y) {
2983 for (int x = x0; x < x1; ) {
2984 if (!monoVal(scanline, x)) {
2989 if (current == NSPANS) {
2990 blend(current, spans, &s->penData);
2993 spans[current].x = x + rx;
2994 spans[current].y = y + ry;
2995 spans[current].coverage = 255;
2998 // extend span until we find a different one.
2999 while (x < x1 && monoVal(scanline, x)) {
3003 spans[current].len = len;
3008 } else if (depth == 8) {
3009 for (int y = y0; y < y1; ++y) {
3010 for (int x = x0; x < x1; ) {
3011 // Skip those with 0 coverage
3012 if (scanline[x] == 0) {
3017 if (current == NSPANS) {
3018 blend(current, spans, &s->penData);
3021 int coverage = scanline[x];
3022 spans[current].x = x + rx;
3023 spans[current].y = y + ry;
3024 spans[current].coverage = coverage;
3028 // extend span until we find a different one.
3029 while (x < x1 && scanline[x] == coverage) {
3033 spans[current].len = len;
3038 } else { // 32-bit alpha...
3039 uint *sl = (uint *) src;
3040 for (int y = y0; y < y1; ++y) {
3041 for (int x = x0; x < x1; ) {
3042 // Skip those with 0 coverage
3043 if ((sl[x] & 0x00ffffff) == 0) {
3048 if (current == NSPANS) {
3049 blend(current, spans, &s->penData);
3052 uint rgbCoverage = sl[x];
3053 int coverage = qGreen(rgbCoverage);
3054 spans[current].x = x + rx;
3055 spans[current].y = y + ry;
3056 spans[current].coverage = coverage;
3060 // extend span until we find a different one.
3061 while (x < x1 && sl[x] == rgbCoverage) {
3065 spans[current].len = len;
3068 sl += bpl / sizeof(uint);
3071 // qDebug() << "alphaPenBlt: num spans=" << current
3072 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
3073 // Call span func for current set of spans.
3075 blend(current, spans, &s->penData);
3078 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
3079 const QFixedPoint *positions, QFontEngine *fontEngine)
3081 Q_D(QRasterPaintEngine);
3082 QRasterPaintEngineState *s = state();
3084 #if !defined(QT_NO_FREETYPE)
3085 if (fontEngine->type() == QFontEngine::Freetype) {
3086 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3087 QFontEngineFT::GlyphFormat neededFormat =
3088 painter()->device()->devType() == QInternal::Widget
3089 ? fe->defaultGlyphFormat()
3090 : QFontEngineFT::Format_A8;
3092 if (d_func()->mono_surface
3093 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
3095 neededFormat = QFontEngineFT::Format_Mono;
3097 if (neededFormat == QFontEngineFT::Format_None)
3098 neededFormat = QFontEngineFT::Format_A8;
3100 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
3101 if (s->matrix.type() >= QTransform::TxScale) {
3102 if (s->matrix.isAffine())
3103 gset = fe->loadTransformedGlyphSet(s->matrix);
3108 if (!gset || gset->outline_drawing
3109 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
3112 FT_Face lockedFace = 0;
3115 switch (neededFormat) {
3116 case QFontEngineFT::Format_Mono:
3119 case QFontEngineFT::Format_A8:
3122 case QFontEngineFT::Format_A32:
3130 for (int i = 0; i < numGlyphs; i++) {
3131 QFixed spp = fe->subPixelPositionForX(positions[i].x);
3132 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
3134 if (!glyph || glyph->format != neededFormat) {
3136 lockedFace = fe->lockFace();
3137 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
3140 if (!glyph || !glyph->data)
3144 switch (neededFormat) {
3145 case QFontEngineFT::Format_Mono:
3146 pitch = ((glyph->width + 31) & ~31) >> 3;
3148 case QFontEngineFT::Format_A8:
3149 pitch = (glyph->width + 3) & ~3;
3151 case QFontEngineFT::Format_A32:
3152 pitch = glyph->width * 4;
3159 alphaPenBlt(glyph->data, pitch, depth,
3160 qFloor(positions[i].x) + glyph->x,
3161 qFloor(positions[i].y) - glyph->y,
3162 glyph->width, glyph->height);
3169 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
3171 QImageTextureGlyphCache *cache =
3172 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
3174 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
3175 fontEngine->setGlyphCache(0, cache);
3178 cache->populate(fontEngine, numGlyphs, glyphs, positions);
3179 cache->fillInPendingGlyphs();
3181 const QImage &image = cache->image();
3182 int bpl = image.bytesPerLine();
3184 int depth = image.depth();
3188 leftShift = 2; // multiply by 4
3189 else if (depth == 1)
3190 rightShift = 3; // divide by 8
3192 int margin = cache->glyphMargin();
3193 const uchar *bits = image.bits();
3194 for (int i=0; i<numGlyphs; ++i) {
3196 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
3197 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
3198 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
3202 int x = qFloor(positions[i].x) + c.baseLineX - margin;
3203 int y = qFloor(positions[i].y) - c.baseLineY - margin;
3205 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
3208 // c.baseLineX, c.baseLineY,
3211 // positions[i].x.toInt(), positions[i].y.toInt());
3213 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
3219 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
3220 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
3222 Q_D(QRasterPaintEngine);
3223 QRasterPaintEngineState *s = state();
3225 QFontEngine *fontEngine = ti.fontEngine;
3226 if (fontEngine->type() != QFontEngine::S60FontEngine) {
3227 QPaintEngineEx::drawTextItem(p, ti);
3231 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
3233 QVarLengthArray<QFixedPoint> positions;
3234 QVarLengthArray<glyph_t> glyphs;
3235 QTransform matrix = s->matrix;
3236 matrix.translate(p.x(), p.y());
3237 if (matrix.type() == QTransform::TxScale)
3238 fe->setFontScale(matrix.m11());
3239 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3241 for (int i=0; i<glyphs.size(); ++i) {
3242 TOpenFontCharMetrics tmetrics;
3243 const TUint8 *glyphBitmapBytes;
3244 TSize glyphBitmapSize;
3245 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
3246 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
3247 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
3248 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
3251 if (matrix.type() == QTransform::TxScale)
3252 fe->setFontScale(1.0);
3256 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
3259 * Returns true if the rectangle is completely within the current clip
3260 * state of the paint engine.
3262 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
3264 const QClipData *cl = clip();
3266 // inline contains() for performance (we know the rects are normalized)
3267 const QRect &r1 = deviceRect;
3268 return (r.left() >= r1.left() && r.right() <= r1.right()
3269 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3273 if (cl->hasRectClip) {
3274 // currently all painting functions clips to deviceRect internally
3275 if (cl->clipRect == deviceRect)
3278 // inline contains() for performance (we know the rects are normalized)
3279 const QRect &r1 = cl->clipRect;
3280 return (r.left() >= r1.left() && r.right() <= r1.right()
3281 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3283 return qt_region_strictContains(cl->clipRegion, r);
3287 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
3290 Q_Q(const QRasterPaintEngine);
3291 const QRasterPaintEngineState *s = q->state();
3292 const QClipData *cl = clip();
3294 QRect r = rect.normalized();
3295 // inline contains() for performance (we know the rects are normalized)
3296 const QRect &r1 = deviceRect;
3297 return (r.left() >= r1.left() && r.right() <= r1.right()
3298 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3302 // currently all painting functions that call this function clip to deviceRect internally
3303 if (cl->hasRectClip && cl->clipRect == deviceRect)
3306 if (s->flags.antialiased)
3309 QRect r = rect.normalized();
3311 r.setX(r.x() - penWidth);
3312 r.setY(r.y() - penWidth);
3313 r.setWidth(r.width() + 2 * penWidth);
3314 r.setHeight(r.height() + 2 * penWidth);
3317 if (cl->hasRectClip) {
3318 // inline contains() for performance (we know the rects are normalized)
3319 const QRect &r1 = cl->clipRect;
3320 return (r.left() >= r1.left() && r.right() <= r1.right()
3321 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3323 return qt_region_strictContains(cl->clipRegion, r);
3327 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3330 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3334 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3335 const QSpanData *data) const
3337 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3341 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3342 const QSpanData *data) const
3344 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3348 QRasterPaintEnginePrivate::getPenFunc(const QRect &rect,
3349 const QSpanData *data) const
3351 Q_Q(const QRasterPaintEngine);
3352 const QRasterPaintEngineState *s = q->state();
3354 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3356 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->pen.widthF());
3357 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3361 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
3362 const QSpanData *data) const
3364 Q_Q(const QRasterPaintEngine);
3365 const QRasterPaintEngineState *s = q->state();
3367 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3369 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3370 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3376 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3381 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3382 textItem->fontEngine());
3388 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3390 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3391 QRasterPaintEngineState *s = state();
3393 #ifdef QT_DEBUG_DRAW
3394 Q_D(QRasterPaintEngine);
3395 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3396 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3403 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3405 bool drawCached = true;
3407 if (s->matrix.type() >= QTransform::TxProject)
3410 // don't try to cache huge fonts
3411 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
3412 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
3415 // ### Remove the TestFontEngine and Box engine crap, in these
3416 // ### cases we should delegate painting to the font engine
3419 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3420 QFontEngine::Type fontEngineType = ti.fontEngine->type();
3421 // qDebug() << "type" << fontEngineType << s->matrix.type();
3422 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
3423 || (s->matrix.type() <= QTransform::TxTranslate
3424 && (fontEngineType == QFontEngine::TestFontEngine
3425 || fontEngineType == QFontEngine::Box))) {
3429 if (s->matrix.type() > QTransform::TxTranslate)
3433 QRasterPaintEngineState *s = state();
3435 QVarLengthArray<QFixedPoint> positions;
3436 QVarLengthArray<glyph_t> glyphs;
3438 QTransform matrix = s->matrix;
3439 matrix.translate(p.x(), p.y());
3441 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3443 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3447 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3448 if (s->matrix.type() <= QTransform::TxTranslate
3449 || (s->matrix.type() == QTransform::TxScale
3450 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3451 drawGlyphsS60(p, ti);
3454 #else // Q_WS_WIN || Q_WS_MAC
3456 QFontEngine *fontEngine = ti.fontEngine;
3458 #if defined(Q_WS_QWS)
3459 if (fontEngine->type() == QFontEngine::Box) {
3460 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3464 if (s->matrix.type() < QTransform::TxScale
3465 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3466 || (fontEngine->type() == QFontEngine::Proxy
3467 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3469 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3475 if (s->matrix.type() < QTransform::TxScale) {
3477 QVarLengthArray<QFixedPoint> positions;
3478 QVarLengthArray<glyph_t> glyphs;
3479 QTransform matrix = state()->transform();
3481 qreal _x = qFloor(p.x());
3482 qreal _y = qFloor(p.y());
3483 matrix.translate(_x, _y);
3485 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3486 if (glyphs.size() == 0)
3489 for(int i = 0; i < glyphs.size(); i++) {
3490 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3491 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3492 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3493 qRound(positions[i].x + metrics.x),
3494 qRound(positions[i].y + metrics.y),
3495 img.width(), img.height());
3501 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3503 #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3504 if (fontEngine->type() == QFontEngine::QPF2) {
3505 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3506 if (renderingEngine)
3507 fontEngine = renderingEngine;
3511 if (fontEngine->type() != QFontEngine::Freetype) {
3512 QPaintEngineEx::drawTextItem(p, ti);
3516 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3518 QTransform matrix = s->matrix;
3519 matrix.translate(p.x(), p.y());
3521 QVarLengthArray<QFixedPoint> positions;
3522 QVarLengthArray<glyph_t> glyphs;
3523 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3524 if (glyphs.size() == 0)
3527 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3528 QPaintEngine::drawTextItem(p, ti);
3534 QPaintEngineEx::drawTextItem(p, ti);
3540 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3542 Q_D(QRasterPaintEngine);
3543 QRasterPaintEngineState *s = state();
3546 qreal pw = s->lastPen.widthF();
3547 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3548 QPaintEngineEx::drawPoints(points, pointCount);
3551 if (!s->penData.blend)
3554 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3555 QT_FT_Span span = { 0, 1, 0, 255 };
3556 const QPointF *end = points + pointCount;
3557 qreal trans_x, trans_y;
3559 int left = d->deviceRect.x();
3560 int right = left + d->deviceRect.width();
3561 int top = d->deviceRect.y();
3562 int bottom = top + d->deviceRect.height();
3564 while (points < end) {
3565 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3566 x = qFloor(trans_x);
3567 y = qFloor(trans_y);
3568 if (x >= left && x < right && y >= top && y < bottom) {
3570 const QT_FT_Span &last = array[count - 1];
3571 // spans must be sorted on y (primary) and x (secondary)
3572 if (y < last.y || (y == last.y && x < last.x)) {
3573 s->penData.blend(count, array.constData(), &s->penData);
3580 array[count++] = span;
3586 s->penData.blend(count, array.constData(), &s->penData);
3591 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3593 Q_D(QRasterPaintEngine);
3594 QRasterPaintEngineState *s = state();
3597 double pw = s->lastPen.widthF();
3598 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3599 QPaintEngineEx::drawPoints(points, pointCount);
3602 if (!s->penData.blend)
3605 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3606 QT_FT_Span span = { 0, 1, 0, 255 };
3607 const QPoint *end = points + pointCount;
3608 qreal trans_x, trans_y;
3610 int left = d->deviceRect.x();
3611 int right = left + d->deviceRect.width();
3612 int top = d->deviceRect.y();
3613 int bottom = top + d->deviceRect.height();
3615 while (points < end) {
3616 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3617 x = qFloor(trans_x);
3618 y = qFloor(trans_y);
3619 if (x >= left && x < right && y >= top && y < bottom) {
3621 const QT_FT_Span &last = array[count - 1];
3622 // spans must be sorted on y (primary) and x (secondary)
3623 if (y < last.y || (y == last.y && x < last.x)) {
3624 s->penData.blend(count, array.constData(), &s->penData);
3631 array[count++] = span;
3637 s->penData.blend(count, array.constData(), &s->penData);
3644 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3646 #ifdef QT_DEBUG_DRAW
3647 qDebug() << " - QRasterPaintEngine::drawLine()";
3649 Q_D(QRasterPaintEngine);
3650 QRasterPaintEngineState *s = state();
3653 if (s->flags.fast_pen) {
3654 QIntRect bounds; bounds.set(d->deviceRect);
3655 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3657 : LineDrawIncludeLastPixel;
3659 int m11 = int(s->matrix.m11());
3660 int m22 = int(s->matrix.m22());
3661 int dx = qFloor(s->matrix.dx());
3662 int dy = qFloor(s->matrix.dy());
3663 for (int i=0; i<lineCount; ++i) {
3664 int dashOffset = int(s->lastPen.dashOffset());
3665 if (s->flags.int_xform) {
3666 const QLine &l = lines[i];
3667 int x1 = l.x1() * m11 + dx;
3668 int y1 = l.y1() * m22 + dy;
3669 int x2 = l.x2() * m11 + dx;
3670 int y2 = l.y2() * m22 + dy;
3672 const QRect brect(QPoint(x1, y1), QPoint(x2, y2));
3673 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3674 if (qpen_style(s->lastPen) == Qt::SolidLine)
3675 drawLine_midpoint_i(x1, y1, x2, y2,
3676 penBlend, &s->penData, mode, bounds);
3678 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
3679 &s->lastPen, penBlend,
3680 &s->penData, mode, bounds,
3683 QLineF line = lines[i] * s->matrix;
3684 const QRectF brect(QPointF(line.x1(), line.y1()),
3685 QPointF(line.x2(), line.y2()));
3686 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3687 if (qpen_style(s->lastPen) == Qt::SolidLine)
3688 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3689 int(line.x2()), int(line.y2()),
3690 penBlend, &s->penData, mode, bounds);
3692 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3693 int(line.x2()), int(line.y2()),
3694 &s->lastPen, penBlend,
3695 &s->penData, mode, bounds,
3699 } else if (s->penData.blend) {
3700 QPaintEngineEx::drawLines(lines, lineCount);
3704 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3710 Q_Q(QRasterPaintEngine);
3711 QRasterPaintEngineState *s = q->state();
3713 const QPen &pen = s->lastPen;
3714 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3715 const QVector<qreal> pattern = pen.dashPattern();
3717 qreal patternLength = 0;
3718 for (int i = 0; i < pattern.size(); ++i)
3719 patternLength += pattern.at(i);
3721 if (patternLength <= 0)
3724 qreal length = line.length();
3725 Q_ASSERT(length > 0);
3726 while (length > 0) {
3727 const bool rasterize = *inDash;
3728 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3731 if (dash >= length) {
3733 *dashOffset += dash / width;
3737 *inDash = !(*inDash);
3738 if (++*dashIndex >= pattern.size())
3745 if (rasterize && dash > 0)
3746 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3753 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3755 #ifdef QT_DEBUG_DRAW
3756 qDebug() << " - QRasterPaintEngine::drawLine()";
3758 Q_D(QRasterPaintEngine);
3759 QRasterPaintEngineState *s = state();
3762 if (!s->penData.blend)
3764 if (s->flags.fast_pen) {
3766 bounds.set(d->deviceRect);
3767 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3769 : LineDrawIncludeLastPixel;
3771 for (int i=0; i<lineCount; ++i) {
3772 int dashOffset = int(s->lastPen.dashOffset());
3773 QLineF line = lines[i] * s->matrix;
3774 const QRectF brect(QPointF(line.x1(), line.y1()),
3775 QPointF(line.x2(), line.y2()));
3776 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3777 if (qpen_style(s->lastPen) == Qt::SolidLine)
3778 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3779 int(line.x2()), int(line.y2()),
3780 penBlend, &s->penData, mode, bounds);
3782 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3783 int(line.x2()), int(line.y2()),
3785 penBlend, &s->penData, mode,
3786 bounds, &dashOffset);
3789 QPaintEngineEx::drawLines(lines, lineCount);
3797 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3799 Q_D(QRasterPaintEngine);
3800 QRasterPaintEngineState *s = state();
3803 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3804 || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased))
3805 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3807 && s->matrix.type() <= QTransform::TxScale) // no shear
3810 const QRectF r = s->matrix.mapRect(rect);
3811 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3812 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3813 const QRect brect = QRect(int(r.x()), int(r.y()),
3814 int_dim(r.x(), r.width()),
3815 int_dim(r.y(), r.height()));
3817 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3818 &s->penData, &s->brushData);
3822 QPaintEngineEx::drawEllipse(rect);
3829 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3831 Q_D(QRasterPaintEngine);
3838 CGContextRef QRasterPaintEngine::getCGContext() const
3840 Q_D(const QRasterPaintEngine);
3841 return d->cgContext;
3849 void QRasterPaintEngine::setDC(HDC hdc) {
3850 Q_D(QRasterPaintEngine);
3857 HDC QRasterPaintEngine::getDC() const
3859 Q_D(const QRasterPaintEngine);
3866 void QRasterPaintEngine::releaseDC(HDC) const
3875 QPoint QRasterPaintEngine::coordinateOffset() const
3877 return QPoint(0, 0);
3881 Draws the given color \a spans with the specified \a color. The \a
3882 count parameter specifies the number of spans.
3884 The default implementation does nothing; reimplement this function
3885 to draw the given color \a spans with the specified \a color. Note
3886 that this function \e must be reimplemented if the framebuffer is
3889 \sa drawBufferSpan()
3891 #if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3892 void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3897 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3898 "a non memory-mapped device");
3902 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3904 Draws the given \a buffer.
3906 The default implementation does nothing; reimplement this function
3907 to draw a buffer that contains more than one color. Note that this
3908 function \e must be reimplemented if the framebuffer is not
3911 The \a size parameter specifies the total size of the given \a
3912 buffer, while the \a length parameter specifies the number of
3913 pixels to draw. The buffer's position is given by (\a x, \a
3914 y). The provided \a alpha value is added to each pixel in the
3915 buffer when drawing.
3917 \sa drawColorSpans()
3919 void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3920 int x, int y, int length, uint const_alpha)
3927 Q_UNUSED(const_alpha);
3928 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3929 "a non memory-mapped device");
3933 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3938 Q_D(QRasterPaintEngine);
3940 Q_ASSERT(image.depth() == 1);
3942 const int spanCount = 256;
3943 QT_FT_Span spans[spanCount];
3947 int w = image.width();
3948 int h = image.height();
3949 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3950 int ymin = qMax(qRound(pos.y()), 0);
3951 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3952 int xmin = qMax(qRound(pos.x()), 0);
3954 int x_offset = xmin - qRound(pos.x());
3956 QImage::Format format = image.format();
3957 for (int y = ymin; y < ymax; ++y) {
3958 const uchar *src = image.scanLine(y - qRound(pos.y()));
3959 if (format == QImage::Format_MonoLSB) {
3960 for (int x = 0; x < xmax - xmin; ++x) {
3961 int src_x = x + x_offset;
3962 uchar pixel = src[src_x >> 3];
3967 if (pixel & (0x1 << (src_x & 7))) {
3968 spans[n].x = xmin + x;
3970 spans[n].coverage = 255;
3972 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3976 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3979 if (n == spanCount) {
3980 fg->blend(n, spans, fg);
3986 for (int x = 0; x < xmax - xmin; ++x) {
3987 int src_x = x + x_offset;
3988 uchar pixel = src[src_x >> 3];
3993 if (pixel & (0x80 >> (x & 7))) {
3994 spans[n].x = xmin + x;
3996 spans[n].coverage = 255;
3998 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
4002 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
4005 if (n == spanCount) {
4006 fg->blend(n, spans, fg);
4014 fg->blend(n, spans, fg);
4020 \enum QRasterPaintEngine::ClipType
4023 \value RectClip Indicates that the currently set clip is a single rectangle.
4024 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
4029 Returns the type of the clip currently set.
4031 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
4033 Q_D(const QRasterPaintEngine);
4035 const QClipData *clip = d->clip();
4036 if (!clip || clip->hasRectClip)
4044 Returns the bounding rect of the currently set clip.
4046 QRect QRasterPaintEngine::clipBoundingRect() const
4048 Q_D(const QRasterPaintEngine);
4050 const QClipData *clip = d->clip();
4053 return d->deviceRect;
4055 if (clip->hasRectClip)
4056 return clip->clipRect;
4058 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
4061 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
4063 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
4065 QVarLengthArray<short, 4096> buffer;
4067 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
4068 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
4069 result->initialize();
4071 for (int y = 0; y < c1->clipSpanHeight; ++y) {
4072 const QSpan *c1_spans = c1ClipLines[y].spans;
4073 int c1_count = c1ClipLines[y].count;
4074 const QSpan *c2_spans = c2ClipLines[y].spans;
4075 int c2_count = c2ClipLines[y].count;
4077 if (c1_count == 0 && c2_count == 0)
4079 if (c1_count == 0) {
4080 result->appendSpans(c2_spans, c2_count);
4082 } else if (c2_count == 0) {
4083 result->appendSpans(c1_spans, c1_count);
4087 // we need to merge the two
4089 // find required length
4090 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
4091 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
4093 memset(buffer.data(), 0, buffer.size() * sizeof(short));
4095 // Fill with old spans.
4096 for (int i = 0; i < c1_count; ++i) {
4097 const QSpan *cs = c1_spans + i;
4098 for (int j=cs->x; j<cs->x + cs->len; ++j)
4099 buffer[j] = cs->coverage;
4102 // Fill with new spans
4103 for (int i = 0; i < c2_count; ++i) {
4104 const QSpan *cs = c2_spans + i;
4105 for (int j = cs->x; j < cs->x + cs->len; ++j) {
4106 buffer[j] += cs->coverage;
4107 if (buffer[j] > 255)
4115 // Skip to next span
4116 while (x < max && buffer[x] == 0) ++x;
4117 if (x >= max) break;
4120 int coverage = buffer[x];
4122 // Find length of span
4123 while (x < max && buffer[x] == coverage)
4126 result->appendSpan(sx, x - sx, y, coverage);
4131 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
4133 Q_Q(QRasterPaintEngine);
4134 QRasterPaintEngineState *s = q->state();
4136 rasterizer->setAntialiased(s->flags.antialiased);
4138 QRect clipRect(deviceRect);
4140 // ### get from optimized rectbased QClipData
4142 const QClipData *c = clip();
4144 const QRect r(QPoint(c->xmin, c->ymin),
4145 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
4146 clipRect = clipRect.intersected(r);
4147 blend = data->blend;
4149 blend = data->unclipped_blend;
4152 rasterizer->setClipRect(clipRect);
4153 rasterizer->initialize(blend, data);
4156 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4157 ProcessSpans callback,
4158 QSpanData *spanData, QRasterBuffer *rasterBuffer)
4160 if (!callback || !outline)
4163 Q_Q(QRasterPaintEngine);
4164 QRasterPaintEngineState *s = q->state();
4166 if (!s->flags.antialiased) {
4167 initializeRasterizer(spanData);
4169 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4173 rasterizer->rasterize(outline, fillRule);
4177 rasterize(outline, callback, (void *)spanData, rasterBuffer);
4181 int q_gray_rendered_spans(QT_FT_Raster raster);
4184 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4185 ProcessSpans callback,
4186 void *userData, QRasterBuffer *)
4188 if (!callback || !outline)
4191 Q_Q(QRasterPaintEngine);
4192 QRasterPaintEngineState *s = q->state();
4194 if (!s->flags.antialiased) {
4195 rasterizer->setAntialiased(s->flags.antialiased);
4196 rasterizer->setClipRect(deviceRect);
4197 rasterizer->initialize(callback, userData);
4199 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4203 rasterizer->rasterize(outline, fillRule);
4207 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
4208 // minimize memory reallocations. However if initial size for
4209 // raster pool is changed for lower value, reallocations will
4211 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
4212 int rasterPoolSize = rasterPoolInitialSize;
4213 unsigned char *rasterPoolBase;
4214 #if defined(Q_WS_WIN64)
4216 // We make use of setjmp and longjmp in qgrayraster.c which requires
4217 // 16-byte alignment, hence we hardcode this requirement here..
4218 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4220 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
4221 rasterPoolBase = rasterPoolOnStack;
4223 Q_CHECK_PTR(rasterPoolBase);
4225 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4227 void *data = userData;
4229 QT_FT_BBox clip_box = { deviceRect.x(),
4231 deviceRect.x() + deviceRect.width(),
4232 deviceRect.y() + deviceRect.height() };
4234 QT_FT_Raster_Params rasterParams;
4235 rasterParams.target = 0;
4236 rasterParams.source = outline;
4237 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
4238 rasterParams.gray_spans = 0;
4239 rasterParams.black_spans = 0;
4240 rasterParams.bit_test = 0;
4241 rasterParams.bit_set = 0;
4242 rasterParams.user = data;
4243 rasterParams.clip_box = clip_box;
4248 int rendered_spans = 0;
4252 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
4253 rasterParams.gray_spans = callback;
4254 rasterParams.skip_spans = rendered_spans;
4255 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
4257 // Out of memory, reallocate some more and try again...
4258 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
4259 int new_size = rasterPoolSize * 2;
4260 if (new_size > 1024 * 1024) {
4261 qWarning("QPainter: Rasterization of primitive failed");
4265 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
4267 #if defined(Q_WS_WIN64)
4268 _aligned_free(rasterPoolBase);
4270 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4271 free(rasterPoolBase);
4274 rasterPoolSize = new_size;
4276 #if defined(Q_WS_WIN64)
4277 // We make use of setjmp and longjmp in qgrayraster.c which requires
4278 // 16-byte alignment, hence we hardcode this requirement here..
4279 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4281 (unsigned char *) malloc(rasterPoolSize);
4283 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
4285 qt_ft_grays_raster.raster_done(*grayRaster.data());
4286 qt_ft_grays_raster.raster_new(grayRaster.data());
4287 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4293 #if defined(Q_WS_WIN64)
4294 _aligned_free(rasterPoolBase);
4296 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4297 free(rasterPoolBase);
4301 void QRasterPaintEnginePrivate::recalculateFastImages()
4303 Q_Q(QRasterPaintEngine);
4304 QRasterPaintEngineState *s = q->state();
4306 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
4307 && s->matrix.type() <= QTransform::TxShear;
4310 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
4312 Q_Q(const QRasterPaintEngine);
4313 const QRasterPaintEngineState *s = q->state();
4315 return s->flags.fast_images
4316 && (mode == QPainter::CompositionMode_SourceOver
4317 || (mode == QPainter::CompositionMode_Source
4318 && !image.hasAlphaChannel()));
4321 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
4323 Q_ASSERT(image.depth() == 1);
4325 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
4326 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
4328 QRgb fg = PREMUL(color.rgba());
4331 int height = sourceImage.height();
4332 int width = sourceImage.width();
4333 for (int y=0; y<height; ++y) {
4334 uchar *source = sourceImage.scanLine(y);
4335 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
4336 if (!source || !target)
4337 QT_THROW(std::bad_alloc()); // we must have run out of memory
4338 for (int x=0; x < width; ++x)
4339 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
4344 QRasterBuffer::~QRasterBuffer()
4348 void QRasterBuffer::init()
4350 compositionMode = QPainter::CompositionMode_SourceOver;
4351 monoDestinationWithClut = false;
4356 QImage::Format QRasterBuffer::prepare(QImage *image)
4358 m_buffer = (uchar *)image->bits();
4359 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
4360 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
4361 bytes_per_pixel = image->depth()/8;
4362 bytes_per_line = image->bytesPerLine();
4364 format = image->format();
4365 drawHelper = qDrawHelper + format;
4366 if (image->depth() == 1 && image->colorTable().size() == 2) {
4367 monoDestinationWithClut = true;
4368 destColor0 = PREMUL(image->colorTable()[0]);
4369 destColor1 = PREMUL(image->colorTable()[1]);
4375 void QRasterBuffer::resetBuffer(int val)
4377 memset(m_buffer, val, m_height*bytes_per_line);
4381 #if defined(Q_WS_QWS)
4382 void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
4384 m_buffer = reinterpret_cast<uchar*>(device->memory());
4385 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
4386 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
4387 bytes_per_pixel = device->depth() / 8;
4388 bytes_per_line = device->bytesPerLine();
4389 format = device->format();
4390 #ifndef QT_NO_RASTERCALLBACKS
4392 drawHelper = qDrawHelperCallback + format;
4395 drawHelper = qDrawHelper + format;
4398 int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
4402 return widget->frameGeometry().width();
4404 return widget->frameGeometry().height();
4409 return qt_paint_device_metric(widget, m);
4412 int QCustomRasterPaintDevice::bytesPerLine() const
4414 return (width() * depth() + 7) / 8;
4417 #elif defined(Q_OS_SYMBIAN)
4419 void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
4423 #endif // Q_OS_SYMBIAN
4426 \class QCustomRasterPaintDevice
4431 \brief The QCustomRasterPaintDevice class is provided to activate
4432 hardware accelerated paint engines in Qt for Embedded Linux.
4434 Note that this class is only available in \l{Qt for Embedded Linux}.
4436 In \l{Qt for Embedded Linux}, painting is a pure software
4437 implementation. But starting with Qt 4.2, it is
4438 possible to add an accelerated graphics driver to take advantage
4439 of available hardware resources.
4441 Hardware acceleration is accomplished by creating a custom screen
4442 driver, accelerating the copying from memory to the screen, and
4443 implementing a custom paint engine accelerating the various
4444 painting operations. Then a custom paint device (derived from the
4445 QCustomRasterPaintDevice class) and a custom window surface
4446 (derived from QWSWindowSurface) must be implemented to make
4447 \l{Qt for Embedded Linux} aware of the accelerated driver.
4449 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
4450 documentation for details.
4452 \sa QRasterPaintEngine, QPaintDevice
4456 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
4458 Constructs a custom raster based paint device for the given
4459 top-level \a widget.
4463 \fn int QCustomRasterPaintDevice::bytesPerLine() const
4465 Returns the number of bytes per line in the framebuffer. Note that
4466 this number might be larger than the framebuffer width.
4470 \fn int QCustomRasterPaintDevice::devType() const
4475 \fn QImage::Format QCustomRasterPaintDevice::format() const
4477 Returns the format of the device's memory buffet.
4479 The default format is QImage::Format_ARGB32_Premultiplied. The
4480 only other valid format is QImage::Format_RGB16.
4484 \fn void * QCustomRasterPaintDevice::memory () const
4486 Returns a pointer to the paint device's memory buffer, or 0 if no
4491 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4496 \fn QSize QCustomRasterPaintDevice::size () const
4501 QClipData::QClipData(int height)
4503 clipSpanHeight = height;
4508 xmin = xmax = ymin = ymax = 0;
4512 hasRectClip = hasRegionClip = false;
4515 QClipData::~QClipData()
4523 void QClipData::initialize()
4529 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4531 Q_CHECK_PTR(m_clipLines);
4533 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4534 allocated = clipSpanHeight;
4535 Q_CHECK_PTR(m_spans);
4541 m_clipLines[y].spans = 0;
4542 m_clipLines[y].count = 0;
4546 const int len = clipRect.width();
4549 QSpan *span = m_spans + count;
4553 span->coverage = 255;
4556 m_clipLines[y].spans = span;
4557 m_clipLines[y].count = 1;
4561 while (y < clipSpanHeight) {
4562 m_clipLines[y].spans = 0;
4563 m_clipLines[y].count = 0;
4566 } else if (hasRegionClip) {
4568 const QVector<QRect> rects = clipRegion.rects();
4569 const int numRects = rects.size();
4572 const int maxSpans = (ymax - ymin) * numRects;
4573 if (maxSpans > allocated) {
4574 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4575 allocated = maxSpans;
4580 int firstInBand = 0;
4582 while (firstInBand < numRects) {
4583 const int currMinY = rects.at(firstInBand).y();
4584 const int currMaxY = currMinY + rects.at(firstInBand).height();
4586 while (y < currMinY) {
4587 m_clipLines[y].spans = 0;
4588 m_clipLines[y].count = 0;
4592 int lastInBand = firstInBand;
4593 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4596 while (y < currMaxY) {
4598 m_clipLines[y].spans = m_spans + count;
4599 m_clipLines[y].count = lastInBand - firstInBand + 1;
4601 for (int r = firstInBand; r <= lastInBand; ++r) {
4602 const QRect &currRect = rects.at(r);
4603 QSpan *span = m_spans + count;
4604 span->x = currRect.x();
4605 span->len = currRect.width();
4607 span->coverage = 255;
4613 firstInBand = lastInBand + 1;
4616 Q_ASSERT(count <= allocated);
4618 while (y < clipSpanHeight) {
4619 m_clipLines[y].spans = 0;
4620 m_clipLines[y].count = 0;
4626 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4631 free(m_clipLines); // same for clipLines
4637 void QClipData::fixup()
4642 ymin = ymax = xmin = xmax = 0;
4647 ymin = m_spans[0].y;
4648 ymax = m_spans[count-1].y + 1;
4652 const int firstLeft = m_spans[0].x;
4653 const int firstRight = m_spans[0].x + m_spans[0].len;
4656 for (int i = 0; i < count; ++i) {
4657 QT_FT_Span_& span = m_spans[i];
4660 if (span.y != y + 1 && y != -1)
4663 m_clipLines[y].spans = &span;
4664 m_clipLines[y].count = 1;
4666 ++m_clipLines[y].count;
4668 const int spanLeft = span.x;
4669 const int spanRight = spanLeft + span.len;
4671 if (spanLeft < xmin)
4674 if (spanRight > xmax)
4677 if (spanLeft != firstLeft || spanRight != firstRight)
4683 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4688 Convert \a rect to clip spans.
4690 void QClipData::setClipRect(const QRect &rect)
4692 if (hasRectClip && rect == clipRect)
4695 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4697 hasRegionClip = false;
4701 xmax = rect.x() + rect.width();
4702 ymin = qMin(rect.y(), clipSpanHeight);
4703 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4710 // qDebug() << xmin << xmax << ymin << ymax;
4714 Convert \a region to clip spans.
4716 void QClipData::setClipRegion(const QRegion ®ion)
4718 if (region.rectCount() == 1) {
4719 setClipRect(region.rects().at(0));
4723 hasRegionClip = true;
4724 hasRectClip = false;
4725 clipRegion = region;
4727 { // set bounding rect
4728 const QRect rect = region.boundingRect();
4730 xmax = rect.x() + rect.width();
4732 ymax = rect.y() + rect.height();
4744 spans must be sorted on y
4746 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4747 const QSpan *spans, const QSpan *end,
4748 QSpan **outSpans, int available)
4750 const_cast<QClipData *>(clip)->initialize();
4752 QSpan *out = *outSpans;
4754 const QSpan *clipSpans = clip->m_spans + *currentClip;
4755 const QSpan *clipEnd = clip->m_spans + clip->count;
4757 while (available && spans < end ) {
4758 if (clipSpans >= clipEnd) {
4762 if (clipSpans->y > spans->y) {
4766 if (spans->y != clipSpans->y) {
4767 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4768 clipSpans = clip->m_clipLines[spans->y].spans;
4773 Q_ASSERT(spans->y == clipSpans->y);
4776 int sx2 = sx1 + spans->len;
4777 int cx1 = clipSpans->x;
4778 int cx2 = cx1 + clipSpans->len;
4780 if (cx1 < sx1 && cx2 < sx1) {
4783 } else if (sx1 < cx1 && sx2 < cx1) {
4787 int x = qMax(sx1, cx1);
4788 int len = qMin(sx2, cx2) - x;
4790 out->x = qMax(sx1, cx1);
4791 out->len = qMin(sx2, cx2) - out->x;
4793 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4805 *currentClip = clipSpans - clip->m_spans;
4809 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4811 // qDebug() << "qt_span_fill_clipped" << spanCount;
4812 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4814 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4816 const int NSPANS = 256;
4817 QSpan cspans[NSPANS];
4818 int currentClip = 0;
4819 const QSpan *end = spans + spanCount;
4820 while (spans < end) {
4821 QSpan *clipped = cspans;
4822 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4823 // qDebug() << "processed " << processed << "clipped" << clipped-cspans
4824 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4826 if (clipped - cspans)
4827 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4833 Clip spans to \a{clip}-rectangle.
4834 Returns number of unclipped spans
4836 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4839 const short minx = clip.left();
4840 const short miny = clip.top();
4841 const short maxx = clip.right();
4842 const short maxy = clip.bottom();
4845 for (int i = 0; i < numSpans; ++i) {
4846 if (spans[i].y > maxy)
4848 if (spans[i].y < miny
4849 || spans[i].x > maxx
4850 || spans[i].x + spans[i].len <= minx) {
4853 if (spans[i].x < minx) {
4854 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4857 spans[n].x = spans[i].x;
4858 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4860 if (spans[n].len == 0)
4862 spans[n].y = spans[i].y;
4863 spans[n].coverage = spans[i].coverage;
4870 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4873 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4874 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4876 Q_ASSERT(fillData->clip);
4877 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4879 // hw: check if this const_cast<> is safe!!!
4880 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4881 fillData->clip->clipRect);
4883 fillData->unclipped_blend(count, spans, fillData);
4886 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4888 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4890 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4891 // for (int i = 0; i < qMin(count, 10); ++i) {
4892 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4895 switch (clipData->operation) {
4897 case Qt::IntersectClip:
4899 QClipData *newClip = clipData->newClip;
4900 newClip->initialize();
4902 int currentClip = 0;
4903 const QSpan *end = spans + count;
4904 while (spans < end) {
4905 QSpan *newspans = newClip->m_spans + newClip->count;
4906 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4907 &newspans, newClip->allocated - newClip->count);
4908 newClip->count = newspans - newClip->m_spans;
4910 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4911 newClip->allocated *= 2;
4918 case Qt::ReplaceClip:
4919 clipData->newClip->appendSpans(spans, count);
4927 QImage QRasterBuffer::bufferImage() const
4929 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4931 for (int y = 0; y < m_height; ++y) {
4932 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4934 for (int x=0; x<m_width; ++x) {
4935 uint argb = span[x];
4936 image.setPixel(x, y, argb);
4944 void QRasterBuffer::flushToARGBImage(QImage *target) const
4946 int w = qMin(m_width, target->width());
4947 int h = qMin(m_height, target->height());
4949 for (int y=0; y<h; ++y) {
4950 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4951 QRgb *dest = (QRgb *) target->scanLine(y);
4952 for (int x=0; x<w; ++x) {
4953 QRgb pixel = sourceLine[x];
4954 int alpha = qAlpha(pixel);
4958 dest[x] = (alpha << 24)
4959 | ((255*qRed(pixel)/alpha) << 16)
4960 | ((255*qGreen(pixel)/alpha) << 8)
4961 | ((255*qBlue(pixel)/alpha) << 0);
4968 class QGradientCache
4972 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4973 stops(s), opacity(op), interpolationMode(mode) {}
4974 uint buffer[GRADIENT_STOPTABLE_SIZE];
4975 QGradientStops stops;
4977 QGradient::InterpolationMode interpolationMode;
4980 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4983 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4984 quint64 hash_val = 0;
4986 QGradientStops stops = gradient.stops();
4987 for (int i = 0; i < stops.size() && i <= 2; i++)
4988 hash_val += stops[i].second.rgba();
4990 QMutexLocker lock(&mutex);
4991 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4993 if (it == cache.constEnd())
4994 return addCacheElement(hash_val, gradient, opacity);
4997 const CacheInfo &cache_info = it.value();
4998 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4999 return cache_info.buffer;
5001 } while (it != cache.constEnd() && it.key() == hash_val);
5002 // an exact match for these stops and opacity was not found, create new cache
5003 return addCacheElement(hash_val, gradient, opacity);
5007 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
5009 inline int maxCacheSize() const { return 60; }
5010 inline void generateGradientColorTable(const QGradient& g,
5012 int size, int opacity) const;
5013 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
5014 if (cache.size() == maxCacheSize()) {
5015 // may remove more than 1, but OK
5016 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
5018 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
5019 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
5020 return cache.insert(hash_val, cache_entry).value().buffer;
5023 QGradientColorTableHash cache;
5027 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
5029 QGradientStops stops = gradient.stops();
5030 int stopCount = stops.count();
5031 Q_ASSERT(stopCount > 0);
5033 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
5035 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
5036 if (stopCount == 1) {
5037 current_color = PREMUL(current_color);
5038 for (int i = 0; i < size; ++i)
5039 colorTable[i] = current_color;
5043 // The position where the gradient begins and ends
5044 qreal begin_pos = stops[0].first;
5045 qreal end_pos = stops[stopCount-1].first;
5047 int pos = 0; // The position in the color table.
5050 qreal incr = 1 / qreal(size); // the double increment.
5051 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
5053 // Up to first point
5054 colorTable[pos++] = PREMUL(current_color);
5055 while (dpos <= begin_pos) {
5056 colorTable[pos] = colorTable[pos - 1];
5061 int current_stop = 0; // We always interpolate between current and current + 1.
5063 qreal t; // position between current left and right stops
5064 qreal t_delta; // the t increment per entry in the color table
5066 if (dpos < end_pos) {
5068 while (dpos > stops[current_stop+1].first)
5071 if (current_stop != 0)
5072 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
5073 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
5075 if (colorInterpolation) {
5076 current_color = PREMUL(current_color);
5077 next_color = PREMUL(next_color);
5080 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
5081 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
5082 t = (dpos - stops[current_stop].first) * c;
5086 Q_ASSERT(current_stop < stopCount);
5088 int dist = qRound(t);
5089 int idist = 256 - dist;
5091 if (colorInterpolation)
5092 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
5094 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
5099 if (dpos >= end_pos)
5105 while (dpos > stops[current_stop+skip+1].first)
5109 current_stop += skip;
5111 current_color = next_color;
5113 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
5114 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
5116 if (colorInterpolation) {
5118 current_color = PREMUL(current_color);
5119 next_color = PREMUL(next_color);
5122 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
5123 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
5124 t = (dpos - stops[current_stop].first) * c;
5131 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
5132 while (pos < size - 1) {
5133 colorTable[pos] = current_color;
5137 // Make sure the last color stop is represented at the end of the table
5138 colorTable[size - 1] = current_color;
5141 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
5144 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
5148 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
5153 m11 = m22 = m33 = 1.;
5154 m12 = m13 = m21 = m23 = dx = dy = 0.0;
5155 clip = pe ? pe->d_func()->clip() : 0;
5158 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
5160 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
5162 Qt::BrushStyle brushStyle = qbrush_style(brush);
5163 switch (brushStyle) {
5164 case Qt::SolidPattern: {
5166 QColor c = qbrush_color(brush);
5167 QRgb rgba = c.rgba();
5168 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
5169 if ((solid.color & 0xff000000) == 0
5170 && compositionMode == QPainter::CompositionMode_SourceOver) {
5176 case Qt::LinearGradientPattern:
5178 type = LinearGradient;
5179 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
5180 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5181 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5182 gradient.spread = g->spread();
5184 QLinearGradientData &linearData = gradient.linear;
5186 linearData.origin.x = g->start().x();
5187 linearData.origin.y = g->start().y();
5188 linearData.end.x = g->finalStop().x();
5189 linearData.end.y = g->finalStop().y();
5193 case Qt::RadialGradientPattern:
5195 type = RadialGradient;
5196 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
5197 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5198 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5199 gradient.spread = g->spread();
5201 QRadialGradientData &radialData = gradient.radial;
5203 QPointF center = g->center();
5204 radialData.center.x = center.x();
5205 radialData.center.y = center.y();
5206 QPointF focal = g->focalPoint();
5207 radialData.focal.x = focal.x();
5208 radialData.focal.y = focal.y();
5209 radialData.radius = g->radius();
5213 case Qt::ConicalGradientPattern:
5215 type = ConicalGradient;
5216 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
5217 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5218 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5219 gradient.spread = QGradient::RepeatSpread;
5221 QConicalGradientData &conicalData = gradient.conical;
5223 QPointF center = g->center();
5224 conicalData.center.x = center.x();
5225 conicalData.center.y = center.y();
5226 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
5230 case Qt::Dense1Pattern:
5231 case Qt::Dense2Pattern:
5232 case Qt::Dense3Pattern:
5233 case Qt::Dense4Pattern:
5234 case Qt::Dense5Pattern:
5235 case Qt::Dense6Pattern:
5236 case Qt::Dense7Pattern:
5237 case Qt::HorPattern:
5238 case Qt::VerPattern:
5239 case Qt::CrossPattern:
5240 case Qt::BDiagPattern:
5241 case Qt::FDiagPattern:
5242 case Qt::DiagCrossPattern:
5245 tempImage = new QImage();
5246 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
5247 initTexture(tempImage, alpha, QTextureData::Tiled);
5249 case Qt::TexturePattern:
5252 tempImage = new QImage();
5254 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
5255 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
5257 *tempImage = brush.textureImage();
5258 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
5266 adjustSpanMethods();
5269 void QSpanData::adjustSpanMethods()
5279 unclipped_blend = 0;
5282 unclipped_blend = rasterBuffer->drawHelper->blendColor;
5283 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
5284 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
5285 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
5286 fillRect = rasterBuffer->drawHelper->fillRect;
5288 case LinearGradient:
5289 case RadialGradient:
5290 case ConicalGradient:
5291 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
5295 #ifndef QT_NO_RASTERCALLBACKS
5296 if (!rasterBuffer->buffer())
5297 unclipped_blend = qBlendTextureCallback;
5300 unclipped_blend = qBlendTexture;
5302 unclipped_blend = qBlendTexture;
5304 if (!texture.imageData)
5305 unclipped_blend = 0;
5310 if (!unclipped_blend) {
5313 blend = unclipped_blend;
5314 } else if (clip->hasRectClip) {
5315 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
5317 blend = qt_span_fill_clipped;
5321 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
5324 // make sure we round off correctly in qdrawhelper.cpp
5325 delta.translate(1.0 / 65536, 1.0 / 65536);
5327 QTransform inv = (delta * matrix).inverted();
5340 const bool affine = !m13 && !m23;
5341 fast_matrix = affine
5342 && m11 * m11 + m21 * m21 < 1e4
5343 && m12 * m12 + m22 * m22 < 1e4
5347 adjustSpanMethods();
5350 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
5352 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
5354 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
5355 if (!d || d->height == 0) {
5356 texture.imageData = 0;
5363 texture.bytesPerLine = 0;
5364 texture.format = QImage::Format_Invalid;
5365 texture.colorTable = 0;
5366 texture.hasAlpha = alpha != 256;
5368 texture.imageData = d->data;
5369 texture.width = d->width;
5370 texture.height = d->height;
5372 if (sourceRect.isNull()) {
5375 texture.x2 = texture.width;
5376 texture.y2 = texture.height;
5378 texture.x1 = sourceRect.x();
5379 texture.y1 = sourceRect.y();
5380 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
5381 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
5384 texture.bytesPerLine = d->bytes_per_line;
5386 texture.format = d->format;
5387 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
5388 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
5390 texture.const_alpha = alpha;
5391 texture.type = _type;
5393 adjustSpanMethods();
5405 Draws a line using the floating point midpoint algorithm. The line
5406 \a line is already in device coords at this point.
5409 static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
5410 LineDrawMode style, const QIntRect &devRect)
5412 #ifdef QT_DEBUG_DRAW
5413 qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2));
5417 int dx, dy, d, incrE, incrNE;
5422 const int NSPANS = 256;
5423 QT_FT_Span spans[NSPANS];
5425 bool ordered = true;
5428 // specialcase horizontal lines
5429 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5430 int start = qMax(devRect.x1, qMin(x1, x2));
5431 int stop = qMax(x1, x2) + 1;
5432 int stop_clipped = qMin(devRect.x2, stop);
5433 int len = stop_clipped - start;
5434 if (style == LineDrawNormal && stop == stop_clipped)
5437 spans[0].x = ushort(start);
5438 spans[0].len = ushort(len);
5440 spans[0].coverage = 255;
5441 span_func(1, spans, data);
5445 } else if (dx == 0) {
5446 // specialcase vertical lines
5447 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5448 int start = qMax(devRect.y1, qMin(y1, y2));
5449 int stop = qMax(y1, y2) + 1;
5450 int stop_clipped = qMin(devRect.y2, stop);
5451 int len = stop_clipped - start;
5452 if (style == LineDrawNormal && stop == stop_clipped)
5454 // hw: create spans directly instead to possibly avoid clipping
5456 fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0);
5462 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5464 if (x2 < x1) { /* if coordinates are out of order */
5465 qt_swap_int(x1, x2);
5468 qt_swap_int(y1, y2);
5472 int x_lower_limit = - 128;
5473 if (x1 < x_lower_limit) {
5474 int cy = dy * (x_lower_limit - x1) / dx + y1;
5475 drawLine_midpoint_i(x_lower_limit, cy, x2, y2, span_func, data, style, devRect);
5479 if (style == LineDrawNormal)
5482 // In the loops below we increment before call the span function so
5483 // we need to stop one pixel before
5484 x2 = qMin(x2, devRect.x2 - 1);
5486 // completely clipped, so abort
5498 const int index = (ordered ? current : NSPANS - 1 - current);
5499 spans[index].coverage = 255;
5503 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2)
5504 spans[index].len = 1;
5506 spans[index].len = 0;
5509 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5510 y2 = qMin(y2, devRect.y2 - 1);
5514 incrNE = (dy - dx) * 2;
5517 goto flush_and_return;
5522 if (spans[current].len > 0)
5524 if (current == NSPANS) {
5525 span_func(NSPANS, spans, data);
5532 goto flush_and_return;
5534 spans[current].len = 0;
5535 spans[current].coverage = 255;
5536 spans[current].x = x;
5537 spans[current].y = y;
5540 if (x == devRect.x1)
5541 spans[current].x = devRect.x1;
5544 if (x < devRect.x1 || y < devRect.y1)
5547 Q_ASSERT(x<devRect.x2);
5548 Q_ASSERT(y<devRect.y2);
5549 Q_ASSERT(spans[current].y == y);
5550 spans[current].len++;
5552 if (spans[current].len > 0) {
5555 } else { // 0-45 and 180->225 (unit circle degrees)
5557 y1 = qMin(y1, devRect.y2 - 1);
5561 incrNE = (dy + dx) * 2;
5564 goto flush_and_return;
5569 if (spans[NSPANS - 1 - current].len > 0)
5571 if (current == NSPANS) {
5572 span_func(NSPANS, spans, data);
5579 goto flush_and_return;
5581 const int index = NSPANS - 1 - current;
5582 spans[index].len = 0;
5583 spans[index].coverage = 255;
5588 if (x == devRect.x1)
5589 spans[NSPANS - 1 - current].x = devRect.x1;
5592 if (x < devRect.x1 || y > y1)
5595 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5596 Q_ASSERT(spans[NSPANS - 1 - current].y == y);
5597 spans[NSPANS - 1 - current].len++;
5599 if (spans[NSPANS - 1 - current].len > 0) {
5606 // if y is the major axis:
5608 if (y2 < y1) { /* if coordinates are out of order */
5609 qt_swap_int(y1, y2);
5612 qt_swap_int(x1, x2);
5616 int y_lower_limit = - 128;
5617 if (y1 < y_lower_limit) {
5618 int cx = dx * (y_lower_limit - y1) / dy + x1;
5619 drawLine_midpoint_i(cx, y_lower_limit, x2, y2, span_func, data, style, devRect);
5623 if (style == LineDrawNormal)
5626 // In the loops below we increment before call the span function so
5627 // we need to stop one pixel before
5628 y2 = qMin(y2, devRect.y2 - 1);
5630 // completely clipped, so abort
5638 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
5639 Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2);
5640 if (current == NSPANS) {
5641 span_func(NSPANS, spans, data);
5644 spans[current].len = 1;
5645 spans[current].coverage = 255;
5646 spans[current].x = x;
5647 spans[current].y = y;
5651 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
5652 x2 = qMin(x2, devRect.x2 - 1);
5655 incrNE = (dx - dy) * 2;
5658 goto flush_and_return;
5665 goto flush_and_return;
5670 if (x < devRect.x1 || y < devRect.y1)
5672 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5673 if (current == NSPANS) {
5674 span_func(NSPANS, spans, data);
5677 spans[current].len = 1;
5678 spans[current].coverage = 255;
5679 spans[current].x = x;
5680 spans[current].y = y;
5683 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
5684 x1 = qMin(x1, devRect.x2 - 1);
5687 incrNE = (dx + dy) * 2;
5690 goto flush_and_return;
5697 goto flush_and_return;
5702 if (y < devRect.y1 || x > x1)
5704 Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2);
5705 if (current == NSPANS) {
5706 span_func(NSPANS, spans, data);
5709 spans[current].len = 1;
5710 spans[current].coverage = 255;
5711 spans[current].x = x;
5712 spans[current].y = y;
5719 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
5722 static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern)
5725 if (--*currentOffset == 0) {
5727 *dashIndex = ((*dashIndex + 1) % pattern.size());
5728 *currentOffset = int(pattern[*dashIndex]);
5733 static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
5735 ProcessSpans span_func, QSpanData *data,
5736 LineDrawMode style, const QIntRect &devRect,
5739 #ifdef QT_DEBUG_DRAW
5740 qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset;
5744 int dx, dy, d, incrE, incrNE;
5749 Q_ASSERT(*patternOffset >= 0);
5751 const QVector<qreal> penPattern = pen->dashPattern();
5752 QVarLengthArray<qreal> pattern(penPattern.size());
5754 int patternLength = 0;
5755 for (int i = 0; i < penPattern.size(); ++i)
5756 patternLength += qMax<qreal>(1.0, (penPattern.at(i)));
5758 // pattern must be reversed if coordinates are out of order
5759 int reverseLength = -1;
5760 if (dy == 0 && x1 > x2)
5761 reverseLength = x1 - x2;
5762 else if (dx == 0 && y1 > y2)
5763 reverseLength = y1 - y2;
5764 else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis
5765 reverseLength = qAbs(dx);
5766 else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis
5767 reverseLength = qAbs(dy);
5769 const bool reversed = (reverseLength > -1);
5770 if (reversed) { // reverse pattern
5771 for (int i = 0; i < penPattern.size(); ++i)
5772 pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i));
5774 *patternOffset = (patternLength - 1 - *patternOffset);
5775 *patternOffset += patternLength - (reverseLength % patternLength);
5776 *patternOffset = *patternOffset % patternLength;
5778 for (int i = 0; i < penPattern.size(); ++i)
5779 pattern[i] = qMax<qreal>(1.0, penPattern.at(i));
5783 bool inDash = !reversed;
5784 int currPattern = int(pattern[dashIndex]);
5786 // adjust pattern for offset
5787 offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern);
5789 const int NSPANS = 256;
5790 QT_FT_Span spans[NSPANS];
5792 bool ordered = true;
5795 // specialcase horizontal lines
5796 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5797 int start_unclipped = qMin(x1, x2);
5798 int start = qMax(devRect.x1, start_unclipped);
5799 int stop = qMax(x1, x2) + 1;
5800 int stop_clipped = qMin(devRect.x2, stop);
5801 int len = stop_clipped - start;
5802 if (style == LineDrawNormal && stop == stop_clipped)
5805 // adjust pattern for starting offset
5806 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5810 while (x < stop_clipped) {
5811 if (current == NSPANS) {
5812 span_func(NSPANS, spans, data);
5815 const int dash = qMin(currPattern, stop_clipped - x);
5817 spans[current].x = ushort(x);
5818 spans[current].len = ushort(dash);
5819 spans[current].y = y1;
5820 spans[current].coverage = 255;
5823 if (dash < currPattern) {
5824 currPattern -= dash;
5826 dashIndex = (dashIndex + 1) % pattern.size();
5827 currPattern = int(pattern[dashIndex]);
5834 goto flush_and_return;
5835 } else if (dx == 0) {
5836 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5837 int start_unclipped = qMin(y1, y2);
5838 int start = qMax(devRect.y1, start_unclipped);
5839 int stop = qMax(y1, y2) + 1;
5840 int stop_clipped = qMin(devRect.y2, stop);
5841 if (style == LineDrawNormal && stop == stop_clipped)
5844 stop = stop_clipped;
5846 // adjust pattern for starting offset
5847 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5852 const int dash = qMin(currPattern, stop - y);
5854 for (int i = 0; i < dash; ++i) {
5855 if (current == NSPANS) {
5856 span_func(NSPANS, spans, data);
5859 spans[current].x = x1;
5860 spans[current].len = 1;
5861 spans[current].coverage = 255;
5862 spans[current].y = ushort(y + i);
5866 if (dash < currPattern) {
5867 currPattern -= dash;
5869 dashIndex = (dashIndex + 1) % pattern.size();
5870 currPattern = int(pattern[dashIndex]);
5876 goto flush_and_return;
5879 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5881 if (x2 < x1) { /* if coordinates are out of order */
5882 qt_swap_int(x1, x2);
5885 qt_swap_int(y1, y2);
5889 if (style == LineDrawNormal)
5892 // In the loops below we increment before call the span function so
5893 // we need to stop one pixel before
5894 x2 = qMin(x2, devRect.x2 - 1);
5896 // completely clipped, so abort
5898 goto flush_and_return;
5903 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) {
5904 Q_ASSERT(x < devRect.x2);
5906 if (current == NSPANS) {
5907 span_func(NSPANS, spans, data);
5910 spans[current].len = 1;
5911 spans[current].coverage = 255;
5912 spans[current].x = x;
5913 spans[current].y = y;
5916 if (--currPattern <= 0) {
5918 dashIndex = (dashIndex + 1) % pattern.size();
5919 currPattern = int(pattern[dashIndex]);
5923 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5924 y2 = qMin(y2, devRect.y2 - 1);
5928 incrNE = (dy - dx) * 2;
5931 goto flush_and_return;
5938 goto flush_and_return;
5944 const bool skip = x < devRect.x1 || y < devRect.y1;
5945 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5946 if (inDash && !skip) {
5947 if (current == NSPANS) {
5948 span_func(NSPANS, spans, data);
5951 spans[current].len = 1;
5952 spans[current].coverage = 255;
5953 spans[current].x = x;
5954 spans[current].y = y;
5957 if (--currPattern <= 0) {
5959 dashIndex = (dashIndex + 1) % pattern.size();
5960 currPattern = int(pattern[dashIndex]);
5963 } else { // 0-45 and 180->225 (unit circle degrees)
5964 y1 = qMin(y1, devRect.y2 - 1);
5968 incrNE = (dy + dx) * 2;
5971 goto flush_and_return;
5976 span_func(current, spans, data);
5983 goto flush_and_return;
5989 const bool skip = x < devRect.x1 || y > y1;
5990 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5991 if (inDash && !skip) {
5992 if (current == NSPANS) {
5993 span_func(NSPANS, spans, data);
5996 spans[current].len = 1;
5997 spans[current].coverage = 255;
5998 spans[current].x = x;
5999 spans[current].y = y;
6002 if (--currPattern <= 0) {
6004 dashIndex = (dashIndex + 1) % pattern.size();
6005 currPattern = int(pattern[dashIndex]);
6011 // if y is the major axis:
6013 if (y2 < y1) { /* if coordinates are out of order */
6014 qt_swap_int(y1, y2);
6017 qt_swap_int(x1, x2);
6021 if (style == LineDrawNormal)
6024 // In the loops below we increment before call the span function so
6025 // we need to stop one pixel before
6026 y2 = qMin(y2, devRect.y2 - 1);
6028 // completely clipped, so abort
6030 goto flush_and_return;
6035 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
6036 Q_ASSERT(x < devRect.x2);
6038 if (current == NSPANS) {
6039 span_func(NSPANS, spans, data);
6042 spans[current].len = 1;
6043 spans[current].coverage = 255;
6044 spans[current].x = x;
6045 spans[current].y = y;
6048 if (--currPattern <= 0) {
6050 dashIndex = (dashIndex + 1) % pattern.size();
6051 currPattern = int(pattern[dashIndex]);
6055 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
6056 x2 = qMin(x2, devRect.x2 - 1);
6059 incrNE = (dx - dy) * 2;
6062 goto flush_and_return;
6069 goto flush_and_return;
6074 const bool skip = x < devRect.x1 || y < devRect.y1;
6075 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
6076 if (inDash && !skip) {
6077 if (current == NSPANS) {
6078 span_func(NSPANS, spans, data);
6081 spans[current].len = 1;
6082 spans[current].coverage = 255;
6083 spans[current].x = x;
6084 spans[current].y = y;
6087 if (--currPattern <= 0) {
6089 dashIndex = (dashIndex + 1) % pattern.size();
6090 currPattern = int(pattern[dashIndex]);
6093 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
6094 x1 = qMin(x1, devRect.x2 - 1);
6097 incrNE = (dx + dy) * 2;
6100 goto flush_and_return;
6107 goto flush_and_return;
6112 const bool skip = y < devRect.y1 || x > x1;
6113 Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2));
6114 if (inDash && !skip) {
6115 if (current == NSPANS) {
6116 span_func(NSPANS, spans, data);
6119 spans[current].len = 1;
6120 spans[current].coverage = 255;
6121 spans[current].x = x;
6122 spans[current].y = y;
6125 if (--currPattern <= 0) {
6127 dashIndex = (dashIndex + 1) % pattern.size();
6128 currPattern = int(pattern[dashIndex]);
6135 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
6139 *patternOffset = (patternLength - 1 - *patternOffset);
6142 for (int i = 0; i <= dashIndex; ++i)
6143 *patternOffset += int(pattern[i]);
6144 *patternOffset += patternLength - currPattern - 1;
6145 *patternOffset = (*patternOffset % patternLength);
6151 \a x and \a y is relative to the midpoint of \a rect.
6153 static inline void drawEllipsePoints(int x, int y, int length,
6156 ProcessSpans pen_func, ProcessSpans brush_func,
6157 QSpanData *pen_data, QSpanData *brush_data)
6162 QT_FT_Span outline[4];
6163 const int midx = rect.x() + (rect.width() + 1) / 2;
6164 const int midy = rect.y() + (rect.height() + 1) / 2;
6170 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
6171 outline[0].len = qMin(length, x - outline[0].x);
6173 outline[0].coverage = 255;
6177 outline[1].len = length;
6179 outline[1].coverage = 255;
6182 outline[2].x = outline[0].x;
6183 outline[2].len = outline[0].len;
6184 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
6185 outline[2].coverage = 255;
6189 outline[3].len = length;
6190 outline[3].y = outline[2].y;
6191 outline[3].coverage = 255;
6193 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
6197 fill[0].x = outline[0].x + outline[0].len - 1;
6198 fill[0].len = qMax(0, outline[1].x - fill[0].x);
6199 fill[0].y = outline[1].y;
6200 fill[0].coverage = 255;
6203 fill[1].x = outline[2].x + outline[2].len - 1;
6204 fill[1].len = qMax(0, outline[3].x - fill[1].x);
6205 fill[1].y = outline[3].y;
6206 fill[1].coverage = 255;
6208 int n = (fill[0].y >= fill[1].y ? 1 : 2);
6209 n = qt_intersect_spans(fill, n, clip);
6211 brush_func(n, fill, brush_data);
6214 int n = (outline[1].y >= outline[2].y ? 2 : 4);
6215 n = qt_intersect_spans(outline, n, clip);
6217 pen_func(n, outline, pen_data);
6223 Draws an ellipse using the integer point midpoint algorithm.
6225 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
6226 ProcessSpans pen_func, ProcessSpans brush_func,
6227 QSpanData *pen_data, QSpanData *brush_data)
6229 const qreal a = qreal(rect.width()) / 2;
6230 const qreal b = qreal(rect.height()) / 2;
6231 qreal d = b*b - (a*a*b) + 0.25*a*a;
6234 int y = (rect.height() + 1) / 2;
6238 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
6239 if (d < 0) { // select E
6242 } else { // select SE
6243 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
6244 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6245 pen_func, brush_func, pen_data, brush_data);
6250 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6251 pen_func, brush_func, pen_data, brush_data);
6254 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
6255 const int miny = rect.height() & 0x1;
6257 if (d < 0) { // select SE
6258 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
6260 } else { // select S
6261 d += a*a*(-2*y + 3);
6264 drawEllipsePoints(x, y, 1, rect, clip,
6265 pen_func, brush_func, pen_data, brush_data);
6270 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
6273 Draws the first \a pointCount points in the buffer \a points
6275 The default implementation converts the first \a pointCount QPoints in \a points
6276 to QPointFs and calls the floating point version of drawPoints.
6280 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
6283 Reimplement this function to draw the largest ellipse that can be
6284 contained within rectangle \a rect.
6287 #ifdef QT_DEBUG_DRAW
6288 void dumpClip(int width, int height, const QClipData *clip)
6290 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
6291 clipImg.fill(0xffff0000);
6298 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
6300 for (int i = 0; i < clip->count; ++i) {
6301 const QSpan *span = ((QClipData *) clip)->spans() + i;
6302 for (int j = 0; j < span->len; ++j)
6303 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
6304 x0 = qMin(x0, int(span->x));
6305 x1 = qMax(x1, int(span->x + span->len - 1));
6307 y0 = qMin(y0, int(span->y));
6308 y1 = qMax(y1, int(span->y));
6311 static int counter = 0;
6318 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
6319 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));