source: trunk/src/gui/painting/qpaintengine_raster.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 198.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QtCore/qglobal.h>
43#include <QtCore/qmutex.h>
44
45#define QT_FT_BEGIN_HEADER
46#define QT_FT_END_HEADER
47
48#include <private/qrasterdefs_p.h>
49#include <private/qgrayraster_p.h>
50
51#include <qpainterpath.h>
52#include <qdebug.h>
53#include <qhash.h>
54#include <qlabel.h>
55#include <qbitmap.h>
56#include <qmath.h>
57
58#if defined (Q_WS_X11) || (defined(Q_WS_PM) && !defined(QT_NO_FREETYPE))
59# include <private/qfontengine_ft_p.h>
60#endif
61
62// #include <private/qdatabuffer_p.h>
63// #include <private/qpainter_p.h>
64#include <private/qmath_p.h>
65#include <private/qtextengine_p.h>
66#include <private/qfontengine_p.h>
67#include <private/qpixmap_raster_p.h>
68// #include <private/qpolygonclipper_p.h>
69// #include <private/qrasterizer_p.h>
70#include <private/qimage_p.h>
71#include <private/qstatictext_p.h>
72#include "qmemrotate_p.h"
73
74#include "qpaintengine_raster_p.h"
75// #include "qbezier_p.h"
76#include "qoutlinemapper_p.h"
77
78#if defined(Q_WS_WIN)
79# include <qt_windows.h>
80# include <qvarlengtharray.h>
81# include <private/qfontengine_p.h>
82# if defined(Q_OS_WINCE)
83# include "qguifunctions_wince.h"
84# endif
85#elif defined(Q_WS_MAC)
86# include <private/qt_mac_p.h>
87# include <private/qpixmap_mac_p.h>
88# include <private/qpaintengine_mac_p.h>
89#elif defined(Q_WS_QWS)
90# if !defined(QT_NO_FREETYPE)
91# include <private/qfontengine_ft_p.h>
92# endif
93# if !defined(QT_NO_QWS_QPF2)
94# include <private/qfontengine_qpf_p.h>
95# endif
96# include <private/qabstractfontengine_p.h>
97#elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
98# include <private/qfontengine_s60_p.h>
99#endif
100
101#if defined(Q_WS_WIN64)
102# include <malloc.h>
103#endif
104#include <limits.h>
105
106QT_BEGIN_NAMESPACE
107
108Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
109
110#define qreal_to_fixed_26_6(f) (int(f * 64))
111#define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
112#define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
113
114// #define QT_DEBUG_DRAW
115#ifdef QT_DEBUG_DRAW
116void dumpClip(int width, int height, const QClipData *clip);
117#endif
118
119#define QT_FAST_SPANS
120
121
122// A little helper macro to get a better approximation of dimensions.
123// If we have a rect that starting at 0.5 of width 3.5 it should span
124// 4 pixels.
125#define int_dim(pos, dim) (int(pos+dim) - int(pos))
126
127// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
128static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
129
130#ifdef Q_WS_WIN
131extern bool qt_cleartype_enabled;
132#endif
133
134#ifdef Q_WS_MAC
135extern bool qt_applefontsmoothing_enabled;
136#endif
137
138
139/********************************************************************************
140 * Span functions
141 */
142static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
143static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
144static void qt_span_clip(int count, const QSpan *spans, void *userData);
145static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
146
147struct ClipData
148{
149 QClipData *oldClip;
150 QClipData *newClip;
151 Qt::ClipOperation operation;
152};
153
154enum LineDrawMode {
155 LineDrawClipped,
156 LineDrawNormal,
157 LineDrawIncludeLastPixel
158};
159
160static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
161 LineDrawMode style, const QIntRect &rect);
162static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
163 QPen *pen, ProcessSpans span_func, QSpanData *data,
164 LineDrawMode style, const QIntRect &devRect,
165 int *patternOffset);
166// static void drawLine_midpoint_f(qreal x1, qreal y1, qreal x2, qreal y2,
167// ProcessSpans span_func, QSpanData *data,
168// LineDrawMode style, const QRect &devRect);
169
170static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
171 ProcessSpans pen_func, ProcessSpans brush_func,
172 QSpanData *pen_data, QSpanData *brush_data);
173
174struct QRasterFloatPoint {
175 qreal x;
176 qreal y;
177};
178
179#ifdef QT_DEBUG_DRAW
180static const QRectF boundingRect(const QPointF *points, int pointCount)
181{
182 const QPointF *e = points;
183 const QPointF *last = points + pointCount;
184 qreal minx, maxx, miny, maxy;
185 minx = maxx = e->x();
186 miny = maxy = e->y();
187 while (++e < last) {
188 if (e->x() < minx)
189 minx = e->x();
190 else if (e->x() > maxx)
191 maxx = e->x();
192 if (e->y() < miny)
193 miny = e->y();
194 else if (e->y() > maxy)
195 maxy = e->y();
196 }
197 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
198}
199#endif
200
201template <typename T> static inline bool isRect(const T *pts, int elementCount) {
202 return (elementCount == 5 // 5-point polygon, check for closed rect
203 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
204 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
205 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
206 && pts[0] < pts[4] && pts[1] < pts[5]
207 ) ||
208 (elementCount == 4 // 4-point polygon, check for unclosed rect
209 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
210 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
211 && pts[0] < pts[4] && pts[1] < pts[5]
212 );
213}
214
215
216static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
217{
218 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
219}
220
221static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
222{
223 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
224}
225
226static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
227 qfixed c2x, qfixed c2y,
228 qfixed ex, qfixed ey,
229 void *data)
230{
231 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
232 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
233 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
234}
235
236
237#if !defined(QT_NO_DEBUG) && 0
238static void qt_debug_path(const QPainterPath &path)
239{
240 const char *names[] = {
241 "MoveTo ",
242 "LineTo ",
243 "CurveTo ",
244 "CurveToData"
245 };
246
247 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
248 for (int i=0; i<path.elementCount(); ++i) {
249 const QPainterPath::Element &e = path.elementAt(i);
250 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
251 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
252 }
253}
254#endif
255
256QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
257 QPaintEngineExPrivate(),
258 cachedLines(0)
259{
260}
261
262
263/*!
264 \class QRasterPaintEngine
265 \preliminary
266 \ingroup qws
267 \since 4.2
268
269 \brief The QRasterPaintEngine class enables hardware acceleration
270 of painting operations in Qt for Embedded Linux.
271
272 Note that this functionality is only available in
273 \l{Qt for Embedded Linux}.
274
275 In \l{Qt for Embedded Linux}, painting is a pure software
276 implementation. But starting with Qt 4.2, it is
277 possible to add an accelerated graphics driver to take advantage
278 of available hardware resources.
279
280 Hardware acceleration is accomplished by creating a custom screen
281 driver, accelerating the copying from memory to the screen, and
282 implementing a custom paint engine accelerating the various
283 painting operations. Then a custom paint device (derived from the
284 QCustomRasterPaintDevice class) and a custom window surface
285 (derived from QWSWindowSurface) must be implemented to make
286 \l{Qt for Embedded Linux} aware of the accelerated driver.
287
288 \note The QRasterPaintEngine class does not support 8-bit images.
289 Instead, they need to be converted to a supported format, such as
290 QImage::Format_ARGB32_Premultiplied.
291
292 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
293 documentation for details.
294
295 \sa QCustomRasterPaintDevice, QPaintEngine
296*/
297
298/*!
299 \fn Type QRasterPaintEngine::type() const
300 \reimp
301*/
302
303/*!
304 \typedef QSpan
305 \relates QRasterPaintEngine
306
307 A struct equivalent to QT_FT_Span, containing a position (x,
308 y), the span's length in pixels and its color/coverage (a value
309 ranging from 0 to 255).
310*/
311
312/*!
313 \since 4.5
314
315 Creates a raster based paint engine for operating on the given
316 \a device, with the complete set of \l
317 {QPaintEngine::PaintEngineFeature}{paint engine features and
318 capabilities}.
319*/
320QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
321 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
322{
323 d_func()->device = device;
324 init();
325}
326
327/*!
328 \internal
329*/
330QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
331 : QPaintEngineEx(dd)
332{
333 d_func()->device = device;
334 init();
335}
336
337void QRasterPaintEngine::init()
338{
339 Q_D(QRasterPaintEngine);
340
341
342#ifdef Q_WS_WIN
343 d->hdc = 0;
344#endif
345
346 // The antialiasing raster.
347 d->grayRaster.reset(new QT_FT_Raster);
348 Q_CHECK_PTR(d->grayRaster.data());
349 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
350 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
351
352
353 d->rasterizer.reset(new QRasterizer);
354 d->rasterBuffer.reset(new QRasterBuffer());
355 d->outlineMapper.reset(new QOutlineMapper);
356 d->outlinemapper_xform_dirty = true;
357
358 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
359 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
360 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
361
362 d->baseClip.reset(new QClipData(d->device->height()));
363 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
364
365 d->image_filler.init(d->rasterBuffer.data(), this);
366 d->image_filler.type = QSpanData::Texture;
367
368 d->image_filler_xform.init(d->rasterBuffer.data(), this);
369 d->image_filler_xform.type = QSpanData::Texture;
370
371 d->solid_color_filler.init(d->rasterBuffer.data(), this);
372 d->solid_color_filler.type = QSpanData::Solid;
373
374 d->deviceDepth = d->device->depth();
375
376 d->mono_surface = false;
377 gccaps &= ~PorterDuff;
378
379 QImage::Format format = QImage::Format_Invalid;
380
381 switch (d->device->devType()) {
382 case QInternal::Pixmap:
383 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
384 break;
385 case QInternal::Image:
386 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
387 break;
388#ifdef Q_WS_QWS
389 case QInternal::CustomRaster:
390 d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
391 break;
392#endif
393 default:
394 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
395 d->device = 0;
396 return;
397 }
398
399 switch (format) {
400 case QImage::Format_MonoLSB:
401 case QImage::Format_Mono:
402 d->mono_surface = true;
403 break;
404 case QImage::Format_ARGB8565_Premultiplied:
405 case QImage::Format_ARGB8555_Premultiplied:
406 case QImage::Format_ARGB6666_Premultiplied:
407 case QImage::Format_ARGB4444_Premultiplied:
408 case QImage::Format_ARGB32_Premultiplied:
409 case QImage::Format_ARGB32:
410 gccaps |= PorterDuff;
411 break;
412 case QImage::Format_RGB32:
413 case QImage::Format_RGB444:
414 case QImage::Format_RGB555:
415 case QImage::Format_RGB666:
416 case QImage::Format_RGB888:
417 case QImage::Format_RGB16:
418 break;
419 default:
420 break;
421 }
422}
423
424
425
426
427/*!
428 Destroys this paint engine.
429*/
430QRasterPaintEngine::~QRasterPaintEngine()
431{
432 Q_D(QRasterPaintEngine);
433
434 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
435}
436
437/*!
438 \reimp
439*/
440bool QRasterPaintEngine::begin(QPaintDevice *device)
441{
442 Q_D(QRasterPaintEngine);
443
444 if (device->devType() == QInternal::Pixmap) {
445 QPixmap *pixmap = static_cast<QPixmap *>(device);
446 QPixmapData *pd = pixmap->pixmapData();
447 if (pd->classId() == QPixmapData::RasterClass)
448 d->device = pd->buffer();
449 } else {
450 d->device = device;
451 }
452
453 // Make sure QPaintEngine::paintDevice() returns the proper device.
454 d->pdev = d->device;
455
456 Q_ASSERT(d->device->devType() == QInternal::Image
457 || d->device->devType() == QInternal::CustomRaster);
458
459 d->systemStateChanged();
460
461 QRasterPaintEngineState *s = state();
462 ensureOutlineMapper();
463 d->outlineMapper->m_clip_rect = d->deviceRect;
464
465 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
466 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
467 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
468 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
469
470 d->rasterizer->setClipRect(d->deviceRect);
471
472 s->penData.init(d->rasterBuffer.data(), this);
473 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
474 s->stroker = &d->basicStroker;
475 d->basicStroker.setClipRect(d->deviceRect);
476
477 s->brushData.init(d->rasterBuffer.data(), this);
478 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
479
480 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
481
482 setDirty(DirtyBrushOrigin);
483
484#ifdef QT_DEBUG_DRAW
485 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
486 << ") devType:" << device->devType()
487 << "devRect:" << d->deviceRect;
488 if (d->baseClip) {
489 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
490 }
491#endif
492
493#if defined(Q_WS_WIN)
494 d->isPlain45DegreeRotation = true;
495#endif
496
497 if (d->mono_surface)
498 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
499#if defined(Q_WS_WIN)
500 else if (qt_cleartype_enabled)
501#elif defined (Q_WS_MAC)
502 else if (qt_applefontsmoothing_enabled)
503#else
504 else if (false)
505#endif
506 {
507 QImage::Format format = static_cast<QImage *>(d->device)->format();
508 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
509 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
510 else
511 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
512 } else
513 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
514
515 setActive(true);
516 return true;
517}
518
519/*!
520 \reimp
521*/
522bool QRasterPaintEngine::end()
523{
524#ifdef QT_DEBUG_DRAW
525 Q_D(QRasterPaintEngine);
526 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
527 if (d->baseClip) {
528 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
529 }
530#endif
531
532 return true;
533}
534
535/*!
536 \internal
537*/
538void QRasterPaintEngine::releaseBuffer()
539{
540 Q_D(QRasterPaintEngine);
541 d->rasterBuffer.reset(new QRasterBuffer);
542}
543
544/*!
545 \internal
546*/
547QSize QRasterPaintEngine::size() const
548{
549 Q_D(const QRasterPaintEngine);
550 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
551}
552
553/*!
554 \internal
555*/
556#ifndef QT_NO_DEBUG
557void QRasterPaintEngine::saveBuffer(const QString &s) const
558{
559 Q_D(const QRasterPaintEngine);
560 d->rasterBuffer->bufferImage().save(s, "PNG");
561}
562#endif
563
564/*!
565 \internal
566*/
567void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
568{
569 QRasterPaintEngineState *s = state();
570 // FALCON: get rid of this line, see drawImage call below.
571 s->matrix = matrix;
572 QTransform::TransformationType txop = s->matrix.type();
573
574 switch (txop) {
575
576 case QTransform::TxNone:
577 s->flags.int_xform = true;
578 break;
579
580 case QTransform::TxTranslate:
581 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
582 && qreal(int(s->matrix.dy())) == s->matrix.dy();
583 break;
584
585 case QTransform::TxScale:
586 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
587 && qreal(int(s->matrix.dy())) == s->matrix.dy()
588 && qreal(int(s->matrix.m11())) == s->matrix.m11()
589 && qreal(int(s->matrix.m22())) == s->matrix.m22();
590 break;
591
592 default: // shear / perspective...
593 s->flags.int_xform = false;
594 break;
595 }
596
597 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
598
599 ensureOutlineMapper();
600
601#ifdef Q_WS_WIN
602 Q_D(QRasterPaintEngine);
603 d->isPlain45DegreeRotation = false;
604 if (txop >= QTransform::TxRotate) {
605 d->isPlain45DegreeRotation =
606 (qFuzzyIsNull(matrix.m11())
607 && qFuzzyIsNull(matrix.m12() - qreal(1))
608 && qFuzzyIsNull(matrix.m21() + qreal(1))
609 && qFuzzyIsNull(matrix.m22())
610 )
611 ||
612 (qFuzzyIsNull(matrix.m11() + qreal(1))
613 && qFuzzyIsNull(matrix.m12())
614 && qFuzzyIsNull(matrix.m21())
615 && qFuzzyIsNull(matrix.m22() + qreal(1))
616 )
617 ||
618 (qFuzzyIsNull(matrix.m11())
619 && qFuzzyIsNull(matrix.m12() + qreal(1))
620 && qFuzzyIsNull(matrix.m21() - qreal(1))
621 && qFuzzyIsNull(matrix.m22())
622 )
623 ;
624 }
625#endif
626
627}
628
629
630
631QRasterPaintEngineState::~QRasterPaintEngineState()
632{
633 if (flags.has_clip_ownership)
634 delete clip;
635}
636
637
638QRasterPaintEngineState::QRasterPaintEngineState()
639{
640 stroker = 0;
641
642 fillFlags = 0;
643 strokeFlags = 0;
644 pixmapFlags = 0;
645
646 intOpacity = 256;
647
648 txscale = 1.;
649
650 flags.fast_pen = true;
651 flags.antialiased = false;
652 flags.bilinear = false;
653 flags.fast_text = true;
654 flags.int_xform = true;
655 flags.tx_noshear = true;
656 flags.fast_images = true;
657
658 clip = 0;
659 flags.has_clip_ownership = false;
660
661 dirty = 0;
662}
663
664QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
665 : QPainterState(s)
666{
667 stroker = s.stroker;
668
669 lastBrush = s.lastBrush;
670 brushData = s.brushData;
671 brushData.tempImage = 0;
672
673 lastPen = s.lastPen;
674 penData = s.penData;
675 penData.tempImage = 0;
676
677 fillFlags = s.fillFlags;
678 strokeFlags = s.strokeFlags;
679 pixmapFlags = s.pixmapFlags;
680
681 intOpacity = s.intOpacity;
682
683 txscale = s.txscale;
684
685 flag_bits = s.flag_bits;
686
687 clip = s.clip;
688 flags.has_clip_ownership = false;
689
690 dirty = s.dirty;
691}
692
693/*!
694 \internal
695*/
696QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
697{
698 QRasterPaintEngineState *s;
699 if (!orig)
700 s = new QRasterPaintEngineState();
701 else
702 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
703
704 return s;
705}
706
707/*!
708 \internal
709*/
710void QRasterPaintEngine::setState(QPainterState *s)
711{
712 Q_D(QRasterPaintEngine);
713 QPaintEngineEx::setState(s);
714 d->rasterBuffer->compositionMode = s->composition_mode;
715}
716
717/*!
718 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
719 \internal
720*/
721
722/*!
723 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
724 \internal
725*/
726
727/*!
728 \internal
729*/
730void QRasterPaintEngine::penChanged()
731{
732#ifdef QT_DEBUG_DRAW
733 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
734#endif
735 QRasterPaintEngineState *s = state();
736 s->strokeFlags |= DirtyPen;
737 s->dirty |= DirtyPen;
738}
739
740/*!
741 \internal
742*/
743void QRasterPaintEngine::updatePen(const QPen &pen)
744{
745 Q_D(QRasterPaintEngine);
746 QRasterPaintEngineState *s = state();
747#ifdef QT_DEBUG_DRAW
748 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
749#endif
750
751 Qt::PenStyle pen_style = qpen_style(pen);
752
753 s->lastPen = pen;
754 s->strokeFlags = 0;
755
756 s->penData.clip = d->clip();
757 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
758
759 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
760 || pen.brush().transform().type() >= QTransform::TxNone) {
761 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
762 }
763
764 // Slightly ugly handling of an uncommon case... We need to change
765 // the pen because it is reused in draw_midpoint to decide dashed
766 // or non-dashed.
767 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
768 pen_style = Qt::SolidLine;
769 s->lastPen.setStyle(Qt::SolidLine);
770 }
771
772 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
773 d->basicStroker.setCapStyle(qpen_capStyle(pen));
774 d->basicStroker.setMiterLimit(pen.miterLimit());
775
776 qreal penWidth = qpen_widthf(pen);
777 if (penWidth == 0)
778 d->basicStroker.setStrokeWidth(1);
779 else
780 d->basicStroker.setStrokeWidth(penWidth);
781
782 if(pen_style == Qt::SolidLine) {
783 s->stroker = &d->basicStroker;
784 } else if (pen_style != Qt::NoPen) {
785 if (!d->dashStroker)
786 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
787 if (pen.isCosmetic()) {
788 d->dashStroker->setClipRect(d->deviceRect);
789 } else {
790 // ### I've seen this inverted devrect multiple places now...
791 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
792 d->dashStroker->setClipRect(clipRect);
793 }
794 d->dashStroker->setDashPattern(pen.dashPattern());
795 d->dashStroker->setDashOffset(pen.dashOffset());
796 s->stroker = d->dashStroker.data();
797 } else {
798 s->stroker = 0;
799 }
800
801 s->flags.fast_pen = pen_style > Qt::NoPen
802 && s->penData.blend
803 && !s->flags.antialiased
804 && (penWidth == 0 || (penWidth <= 1
805 && (s->matrix.type() <= QTransform::TxTranslate
806 || pen.isCosmetic())));
807
808 ensureState(); // needed because of tx_noshear...
809 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
810
811 s->strokeFlags = 0;
812}
813
814
815
816/*!
817 \internal
818*/
819void QRasterPaintEngine::brushOriginChanged()
820{
821 QRasterPaintEngineState *s = state();
822#ifdef QT_DEBUG_DRAW
823 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
824#endif
825
826 s->fillFlags |= DirtyBrushOrigin;
827}
828
829
830/*!
831 \internal
832*/
833void QRasterPaintEngine::brushChanged()
834{
835 QRasterPaintEngineState *s = state();
836#ifdef QT_DEBUG_DRAW
837 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
838#endif
839 s->fillFlags |= DirtyBrush;
840}
841
842
843
844
845/*!
846 \internal
847*/
848void QRasterPaintEngine::updateBrush(const QBrush &brush)
849{
850#ifdef QT_DEBUG_DRAW
851 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
852#endif
853 Q_D(QRasterPaintEngine);
854 QRasterPaintEngineState *s = state();
855 // must set clip prior to setup, as setup uses it...
856 s->brushData.clip = d->clip();
857 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
858 if (s->fillFlags & DirtyTransform
859 || brush.transform().type() >= QTransform::TxNone)
860 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
861 s->lastBrush = brush;
862 s->fillFlags = 0;
863}
864
865void QRasterPaintEngine::updateOutlineMapper()
866{
867 Q_D(QRasterPaintEngine);
868 d->outlineMapper->setMatrix(state()->matrix);
869}
870
871void QRasterPaintEngine::updateState()
872{
873 QRasterPaintEngineState *s = state();
874
875 if (s->dirty & DirtyTransform)
876 updateMatrix(s->matrix);
877
878 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
879 const QPainter::CompositionMode mode = s->composition_mode;
880 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
881 && s->intOpacity == 256
882 && (mode == QPainter::CompositionMode_Source
883 || (mode == QPainter::CompositionMode_SourceOver
884 && qAlpha(s->penData.solid.color) == 255));
885 }
886
887 s->dirty = 0;
888}
889
890
891/*!
892 \internal
893*/
894void QRasterPaintEngine::opacityChanged()
895{
896 QRasterPaintEngineState *s = state();
897
898#ifdef QT_DEBUG_DRAW
899 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
900#endif
901
902 s->fillFlags |= DirtyOpacity;
903 s->strokeFlags |= DirtyOpacity;
904 s->pixmapFlags |= DirtyOpacity;
905 s->dirty |= DirtyOpacity;
906 s->intOpacity = (int) (s->opacity * 256);
907}
908
909/*!
910 \internal
911*/
912void QRasterPaintEngine::compositionModeChanged()
913{
914 Q_D(QRasterPaintEngine);
915 QRasterPaintEngineState *s = state();
916
917#ifdef QT_DEBUG_DRAW
918 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
919#endif
920
921 s->fillFlags |= DirtyCompositionMode;
922 s->dirty |= DirtyCompositionMode;
923
924 s->strokeFlags |= DirtyCompositionMode;
925 d->rasterBuffer->compositionMode = s->composition_mode;
926
927 d->recalculateFastImages();
928}
929
930/*!
931 \internal
932*/
933void QRasterPaintEngine::renderHintsChanged()
934{
935 QRasterPaintEngineState *s = state();
936
937#ifdef QT_DEBUG_DRAW
938 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
939#endif
940
941 bool was_aa = s->flags.antialiased;
942 bool was_bilinear = s->flags.bilinear;
943
944 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
945 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
946
947 if (was_aa != s->flags.antialiased)
948 s->strokeFlags |= DirtyHints;
949
950 if (was_bilinear != s->flags.bilinear) {
951 s->strokeFlags |= DirtyPen;
952 s->fillFlags |= DirtyBrush;
953 }
954
955 Q_D(QRasterPaintEngine);
956 d->recalculateFastImages();
957}
958
959/*!
960 \internal
961*/
962void QRasterPaintEngine::transformChanged()
963{
964 QRasterPaintEngineState *s = state();
965
966#ifdef QT_DEBUG_DRAW
967 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
968#endif
969
970 s->fillFlags |= DirtyTransform;
971 s->strokeFlags |= DirtyTransform;
972
973 s->dirty |= DirtyTransform;
974
975 Q_D(QRasterPaintEngine);
976 d->recalculateFastImages();
977}
978
979/*!
980 \internal
981*/
982void QRasterPaintEngine::clipEnabledChanged()
983{
984 QRasterPaintEngineState *s = state();
985
986#ifdef QT_DEBUG_DRAW
987 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
988#endif
989
990 if (s->clip) {
991 s->clip->enabled = s->clipEnabled;
992 s->fillFlags |= DirtyClipEnabled;
993 s->strokeFlags |= DirtyClipEnabled;
994 s->pixmapFlags |= DirtyClipEnabled;
995 }
996}
997
998#ifdef Q_WS_QWS
999void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
1000{
1001 rasterBuffer->prepare(device);
1002}
1003#endif
1004
1005void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
1006 const QImage &img,
1007 SrcOverBlendFunc func,
1008 const QRect &clip,
1009 int alpha,
1010 const QRect &sr)
1011{
1012 if (alpha == 0 || !clip.isValid())
1013 return;
1014
1015 Q_ASSERT(img.depth() >= 8);
1016
1017 int srcBPL = img.bytesPerLine();
1018 const uchar *srcBits = img.bits();
1019 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1020 int iw = img.width();
1021 int ih = img.height();
1022
1023 if (!sr.isEmpty()) {
1024 iw = sr.width();
1025 ih = sr.height();
1026 // Adjust the image according to the source offset...
1027 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1028 }
1029
1030 // adapt the x parameters
1031 int x = qRound(pt.x());
1032 int cx1 = clip.x();
1033 int cx2 = clip.x() + clip.width();
1034 if (x < cx1) {
1035 int d = cx1 - x;
1036 srcBits += srcSize * d;
1037 iw -= d;
1038 x = cx1;
1039 }
1040 if (x + iw > cx2) {
1041 int d = x + iw - cx2;
1042 iw -= d;
1043 }
1044 if (iw <= 0)
1045 return;
1046
1047 // adapt the y paremeters...
1048 int cy1 = clip.y();
1049 int cy2 = clip.y() + clip.height();
1050 int y = qRound(pt.y());
1051 if (y < cy1) {
1052 int d = cy1 - y;
1053 srcBits += srcBPL * d;
1054 ih -= d;
1055 y = cy1;
1056 }
1057 if (y + ih > cy2) {
1058 int d = y + ih - cy2;
1059 ih -= d;
1060 }
1061 if (ih <= 0)
1062 return;
1063
1064 // call the blend function...
1065 int dstSize = rasterBuffer->bytesPerPixel();
1066 int dstBPL = rasterBuffer->bytesPerLine();
1067 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1068 srcBits, srcBPL,
1069 iw, ih,
1070 alpha);
1071}
1072
1073
1074void QRasterPaintEnginePrivate::systemStateChanged()
1075{
1076 QRect clipRect(0, 0,
1077 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1078 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1079
1080 if (!systemClip.isEmpty()) {
1081 QRegion clippedDeviceRgn = systemClip & clipRect;
1082 deviceRect = clippedDeviceRgn.boundingRect();
1083 baseClip->setClipRegion(clippedDeviceRgn);
1084 } else {
1085 deviceRect = clipRect;
1086 baseClip->setClipRect(deviceRect);
1087 }
1088#ifdef QT_DEBUG_DRAW
1089 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1090#endif
1091
1092 exDeviceRect = deviceRect;
1093
1094 Q_Q(QRasterPaintEngine);
1095 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1096 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1097 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1098}
1099
1100void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1101{
1102 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1103 return;
1104
1105 Q_Q(QRasterPaintEngine);
1106 bool bilinear = q->state()->flags.bilinear;
1107
1108 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1109 spanData->setupMatrix(b.transform() * m, bilinear);
1110 } else {
1111 if (m.type() <= QTransform::TxTranslate) {
1112 // specialize setupMatrix for translation matrices
1113 // to avoid needless matrix inversion
1114 spanData->m11 = 1;
1115 spanData->m12 = 0;
1116 spanData->m13 = 0;
1117 spanData->m21 = 0;
1118 spanData->m22 = 1;
1119 spanData->m23 = 0;
1120 spanData->m33 = 1;
1121 spanData->dx = -m.dx();
1122 spanData->dy = -m.dy();
1123 spanData->txop = m.type();
1124 spanData->bilinear = bilinear;
1125 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1126 spanData->adjustSpanMethods();
1127 } else {
1128 spanData->setupMatrix(m, bilinear);
1129 }
1130 }
1131}
1132
1133// #define QT_CLIPPING_RATIOS
1134
1135#ifdef QT_CLIPPING_RATIOS
1136int rectClips;
1137int regionClips;
1138int totalClips;
1139
1140static void checkClipRatios(QRasterPaintEnginePrivate *d)
1141{
1142 if (d->clip()->hasRectClip)
1143 rectClips++;
1144 if (d->clip()->hasRegionClip)
1145 regionClips++;
1146 totalClips++;
1147
1148 if ((totalClips % 5000) == 0) {
1149 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1150 rectClips * 100.0 / (qreal) totalClips,
1151 regionClips * 100.0 / (qreal) totalClips,
1152 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1153 totalClips = 0;
1154 rectClips = 0;
1155 regionClips = 0;
1156 }
1157
1158}
1159#endif
1160
1161static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1162{
1163 if (s->flags.has_clip_ownership)
1164 delete s->clip;
1165 s->clip = 0;
1166 s->flags.has_clip_ownership = false;
1167}
1168
1169static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1170{
1171 s->fillFlags |= QPaintEngine::DirtyClipPath;
1172 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1173 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1174
1175 d->solid_color_filler.clip = d->clip();
1176 d->solid_color_filler.adjustSpanMethods();
1177
1178#ifdef QT_DEBUG_DRAW
1179 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1180#endif
1181
1182}
1183
1184
1185/*!
1186 \internal
1187*/
1188void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1189{
1190#ifdef QT_DEBUG_DRAW
1191 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1192
1193 if (path.elements()) {
1194 for (int i=0; i<path.elementCount(); ++i) {
1195 qDebug() << " - " << path.elements()[i]
1196 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1197 }
1198 } else {
1199 for (int i=0; i<path.elementCount(); ++i) {
1200 qDebug() << " ---- "
1201 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1202 }
1203 }
1204#endif
1205
1206 Q_D(QRasterPaintEngine);
1207 QRasterPaintEngineState *s = state();
1208
1209 const qreal *points = path.points();
1210 const QPainterPath::ElementType *types = path.elements();
1211
1212 // There are some cases that are not supported by clip(QRect)
1213 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1214 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1215 if (s->matrix.type() <= QTransform::TxScale
1216 && ((path.shape() == QVectorPath::RectangleHint)
1217 || (isRect(points, path.elementCount())
1218 && (!types || (types[0] == QPainterPath::MoveToElement
1219 && types[1] == QPainterPath::LineToElement
1220 && types[2] == QPainterPath::LineToElement
1221 && types[3] == QPainterPath::LineToElement))))) {
1222#ifdef QT_DEBUG_DRAW
1223 qDebug() << " --- optimizing vector clip to rect clip...";
1224#endif
1225
1226 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1227 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1228 return;
1229 }
1230 }
1231
1232 if (op == Qt::NoClip) {
1233 qrasterpaintengine_state_setNoClip(s);
1234
1235 } else {
1236 QClipData *base = d->baseClip.data();
1237
1238 // Intersect with current clip when available...
1239 if (op == Qt::IntersectClip && s->clip)
1240 base = s->clip;
1241
1242 // We always intersect, except when there is nothing to
1243 // intersect with, in which case we simplify the operation to
1244 // a replace...
1245 Qt::ClipOperation isectOp = Qt::IntersectClip;
1246 if (base == 0)
1247 isectOp = Qt::ReplaceClip;
1248
1249 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1250 newClip->initialize();
1251 ClipData clipData = { base, newClip, isectOp };
1252 ensureOutlineMapper();
1253 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1254
1255 newClip->fixup();
1256
1257 if (op == Qt::UniteClip) {
1258 // merge clips
1259 QClipData *result = new QClipData(d->rasterBuffer->height());
1260 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1261 qt_merge_clip(current, newClip, result);
1262 result->fixup();
1263 delete newClip;
1264 if (!s->clip)
1265 delete current;
1266 newClip = result;
1267 }
1268
1269 if (s->flags.has_clip_ownership)
1270 delete s->clip;
1271
1272 s->clip = newClip;
1273 s->flags.has_clip_ownership = true;
1274 }
1275 qrasterpaintengine_dirty_clip(d, s);
1276}
1277
1278
1279
1280/*!
1281 \internal
1282*/
1283void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1284{
1285#ifdef QT_DEBUG_DRAW
1286 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1287#endif
1288
1289 QRasterPaintEngineState *s = state();
1290
1291 if (op == Qt::NoClip) {
1292 qrasterpaintengine_state_setNoClip(s);
1293
1294 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1295 QPaintEngineEx::clip(rect, op);
1296 return;
1297
1298 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1299 QPaintEngineEx::clip(rect, op);
1300 return;
1301 }
1302}
1303
1304
1305bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1306{
1307 Q_D(QRasterPaintEngine);
1308 QRect clipRect = r & d->deviceRect;
1309 QRasterPaintEngineState *s = state();
1310
1311 if (op == Qt::ReplaceClip || s->clip == 0) {
1312
1313 // No current clip, hence we intersect with sysclip and be
1314 // done with it...
1315 QRegion clipRegion = systemClip();
1316 QClipData *clip = new QClipData(d->rasterBuffer->height());
1317
1318 if (clipRegion.isEmpty())
1319 clip->setClipRect(clipRect);
1320 else
1321 clip->setClipRegion(clipRegion & clipRect);
1322
1323 if (s->flags.has_clip_ownership)
1324 delete s->clip;
1325
1326 s->clip = clip;
1327 s->clip->enabled = true;
1328 s->flags.has_clip_ownership = true;
1329
1330 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1331 QClipData *base = s->clip;
1332
1333 Q_ASSERT(base);
1334 if (base->hasRectClip || base->hasRegionClip) {
1335 if (!s->flags.has_clip_ownership) {
1336 s->clip = new QClipData(d->rasterBuffer->height());
1337 s->flags.has_clip_ownership = true;
1338 }
1339 if (base->hasRectClip)
1340 s->clip->setClipRect(base->clipRect & clipRect);
1341 else
1342 s->clip->setClipRegion(base->clipRegion & clipRect);
1343 s->clip->enabled = true;
1344 } else {
1345 return false;
1346 }
1347 } else {
1348 return false;
1349 }
1350
1351 qrasterpaintengine_dirty_clip(d, s);
1352 return true;
1353}
1354
1355
1356/*!
1357 \internal
1358*/
1359void QRasterPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
1360{
1361#ifdef QT_DEBUG_DRAW
1362 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1363#endif
1364
1365 Q_D(QRasterPaintEngine);
1366
1367 if (region.rectCount() == 1) {
1368 clip(region.boundingRect(), op);
1369 return;
1370 }
1371
1372 QRasterPaintEngineState *s = state();
1373 const QClipData *clip = d->clip();
1374 const QClipData *baseClip = d->baseClip.data();
1375
1376 if (op == Qt::NoClip) {
1377 qrasterpaintengine_state_setNoClip(s);
1378 } else if (s->matrix.type() > QTransform::TxScale
1379 || op == Qt::UniteClip
1380 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1381 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1382 QPaintEngineEx::clip(region, op);
1383 } else {
1384 const QClipData *curClip;
1385 QClipData *newClip;
1386
1387 if (op == Qt::IntersectClip)
1388 curClip = clip;
1389 else
1390 curClip = baseClip;
1391
1392 if (s->flags.has_clip_ownership) {
1393 newClip = s->clip;
1394 Q_ASSERT(newClip);
1395 } else {
1396 newClip = new QClipData(d->rasterBuffer->height());
1397 s->clip = newClip;
1398 s->flags.has_clip_ownership = true;
1399 }
1400
1401 QRegion r = s->matrix.map(region);
1402 if (curClip->hasRectClip)
1403 newClip->setClipRegion(r & curClip->clipRect);
1404 else if (curClip->hasRegionClip)
1405 newClip->setClipRegion(r & curClip->clipRegion);
1406
1407 qrasterpaintengine_dirty_clip(d, s);
1408 }
1409}
1410
1411/*!
1412 \internal
1413*/
1414void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1415{
1416#ifdef QT_DEBUG_DRAW
1417 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1418#endif
1419
1420 if (!fillData->blend)
1421 return;
1422
1423 Q_D(QRasterPaintEngine);
1424
1425 const QRectF controlPointRect = path.controlPointRect();
1426
1427 QRasterPaintEngineState *s = state();
1428 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1429 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1430 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1431 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1432 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1433 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1434
1435 if (!s->flags.antialiased && !do_clip) {
1436 d->initializeRasterizer(fillData);
1437 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1438 return;
1439 }
1440
1441 ensureOutlineMapper();
1442 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1443}
1444
1445static void fillRect_normalized(const QRect &r, QSpanData *data,
1446 QRasterPaintEnginePrivate *pe)
1447{
1448 int x1, x2, y1, y2;
1449
1450 bool rectClipped = true;
1451
1452 if (data->clip) {
1453 x1 = qMax(r.x(), data->clip->xmin);
1454 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1455 y1 = qMax(r.y(), data->clip->ymin);
1456 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1457 rectClipped = data->clip->hasRectClip;
1458
1459 } else if (pe) {
1460 x1 = qMax(r.x(), pe->deviceRect.x());
1461 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1462 y1 = qMax(r.y(), pe->deviceRect.y());
1463 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1464 } else {
1465 x1 = qMax(r.x(), 0);
1466 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1467 y1 = qMax(r.y(), 0);
1468 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1469 }
1470
1471 if (x2 <= x1 || y2 <= y1)
1472 return;
1473
1474 const int width = x2 - x1;
1475 const int height = y2 - y1;
1476
1477 bool isUnclipped = rectClipped
1478 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1479
1480 if (pe && isUnclipped) {
1481 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1482
1483 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1484 || (mode == QPainter::CompositionMode_SourceOver
1485 && qAlpha(data->solid.color) == 255)))
1486 {
1487 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1488 data->solid.color);
1489 return;
1490 }
1491 }
1492
1493 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1494
1495 const int nspans = 256;
1496 QT_FT_Span spans[nspans];
1497
1498 Q_ASSERT(data->blend);
1499 int y = y1;
1500 while (y < y2) {
1501 int n = qMin(nspans, y2 - y);
1502 int i = 0;
1503 while (i < n) {
1504 spans[i].x = x1;
1505 spans[i].len = width;
1506 spans[i].y = y + i;
1507 spans[i].coverage = 255;
1508 ++i;
1509 }
1510
1511 blend(n, spans, data);
1512 y += n;
1513 }
1514}
1515
1516/*!
1517 \reimp
1518*/
1519void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1520{
1521#ifdef QT_DEBUG_DRAW
1522 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1523#endif
1524 Q_D(QRasterPaintEngine);
1525 QRasterPaintEngineState *s = state();
1526
1527 // Fill
1528 ensureBrush();
1529 if (s->brushData.blend) {
1530 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1531 const QRect *r = rects;
1532 const QRect *lastRect = rects + rectCount;
1533
1534 int offset_x = int(s->matrix.dx());
1535 int offset_y = int(s->matrix.dy());
1536 while (r < lastRect) {
1537 QRect rect = r->normalized();
1538 QRect rr = rect.translated(offset_x, offset_y);
1539 fillRect_normalized(rr, &s->brushData, d);
1540 ++r;
1541 }
1542 } else {
1543 QRectVectorPath path;
1544 for (int i=0; i<rectCount; ++i) {
1545 path.set(rects[i]);
1546 fill(path, s->brush);
1547 }
1548 }
1549 }
1550
1551 ensurePen();
1552 if (s->penData.blend) {
1553 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1554 const QRect *r = rects;
1555 const QRect *lastRect = rects + rectCount;
1556 while (r < lastRect) {
1557 int left = r->x();
1558 int right = r->x() + r->width();
1559 int top = r->y();
1560 int bottom = r->y() + r->height();
1561
1562#ifdef Q_WS_MAC
1563 int pts[] = { top, left,
1564 top, right,
1565 bottom, right,
1566 bottom, left };
1567#else
1568 int pts[] = { left, top,
1569 right, top,
1570 right, bottom,
1571 left, bottom };
1572#endif
1573
1574 strokePolygonCosmetic((QPoint *) pts, 4, WindingMode);
1575 ++r;
1576 }
1577 } else {
1578 QRectVectorPath path;
1579 for (int i = 0; i < rectCount; ++i) {
1580 path.set(rects[i]);
1581 stroke(path, s->pen);
1582 }
1583 }
1584 }
1585}
1586
1587/*!
1588 \reimp
1589*/
1590void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1591{
1592#ifdef QT_DEBUG_DRAW
1593 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1594#endif
1595#ifdef QT_FAST_SPANS
1596 Q_D(QRasterPaintEngine);
1597 QRasterPaintEngineState *s = state();
1598
1599 ensureState();
1600
1601 if (s->flags.tx_noshear) {
1602 ensureBrush();
1603 if (s->brushData.blend) {
1604 d->initializeRasterizer(&s->brushData);
1605 for (int i = 0; i < rectCount; ++i) {
1606 const QRectF &rect = rects[i].normalized();
1607 if (rect.isEmpty())
1608 continue;
1609 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1610 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1611 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1612 }
1613 }
1614
1615 ensurePen();
1616 if (s->penData.blend) {
1617 qreal width = s->pen.isCosmetic()
1618 ? (s->lastPen.widthF() == 0 ? 1 : s->lastPen.widthF())
1619 : s->lastPen.widthF() * s->txscale;
1620
1621 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1622 for (int i = 0; i < rectCount; ++i) {
1623 const QRectF &r = rects[i];
1624 qreal left = r.x();
1625 qreal right = r.x() + r.width();
1626 qreal top = r.y();
1627 qreal bottom = r.y() + r.height();
1628 qreal pts[] = { left, top,
1629 right, top,
1630 right, bottom,
1631 left, bottom };
1632 strokePolygonCosmetic((QPointF *) pts, 4, WindingMode);
1633 }
1634 } else if (width <= 1 && qpen_style(s->lastPen) == Qt::SolidLine) {
1635 d->initializeRasterizer(&s->penData);
1636
1637 for (int i = 0; i < rectCount; ++i) {
1638 const QRectF &rect = rects[i].normalized();
1639 if (rect.isEmpty()) {
1640 qreal pts[] = { rect.left(), rect.top(), rect.right(), rect.bottom() };
1641 QVectorPath vp(pts, 2, 0, QVectorPath::LinesHint);
1642 QPaintEngineEx::stroke(vp, s->lastPen);
1643 } else {
1644 const QPointF tl = s->matrix.map(rect.topLeft());
1645 const QPointF tr = s->matrix.map(rect.topRight());
1646 const QPointF bl = s->matrix.map(rect.bottomLeft());
1647 const QPointF br = s->matrix.map(rect.bottomRight());
1648 const qreal w = width / (rect.width() * s->txscale);
1649 const qreal h = width / (rect.height() * s->txscale);
1650 d->rasterizer->rasterizeLine(tl, tr, w); // top
1651 d->rasterizer->rasterizeLine(bl, br, w); // bottom
1652 d->rasterizer->rasterizeLine(bl, tl, h); // left
1653 d->rasterizer->rasterizeLine(br, tr, h); // right
1654 }
1655 }
1656 } else {
1657 for (int i = 0; i < rectCount; ++i) {
1658 const QRectF &r = rects[i];
1659 qreal left = r.x();
1660 qreal right = r.x() + r.width();
1661 qreal top = r.y();
1662 qreal bottom = r.y() + r.height();
1663 qreal pts[] = { left, top,
1664 right, top,
1665 right, bottom,
1666 left, bottom,
1667 left, top };
1668 QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
1669 QPaintEngineEx::stroke(vp, s->lastPen);
1670 }
1671 }
1672 }
1673
1674 return;
1675 }
1676#endif // QT_FAST_SPANS
1677 QPaintEngineEx::drawRects(rects, rectCount);
1678}
1679
1680
1681/*!
1682 \internal
1683*/
1684void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1685{
1686 QRasterPaintEngineState *s = state();
1687 ensurePen(pen);
1688 if (!s->penData.blend)
1689 return;
1690
1691 if (s->flags.fast_pen && !path.isCurved()
1692 && s->lastPen.brush().isOpaque()) {
1693 int count = path.elementCount();
1694 QPointF *points = (QPointF *) path.points();
1695 const QPainterPath::ElementType *types = path.elements();
1696 if (types) {
1697 int first = 0;
1698 int last;
1699 while (first < count) {
1700 while (first < count && types[first] != QPainterPath::MoveToElement) ++first;
1701 last = first + 1;
1702 while (last < count && types[last] == QPainterPath::LineToElement) ++last;
1703 strokePolygonCosmetic(points + first, last - first,
1704 path.hasImplicitClose() && last == count // only close last one..
1705 ? WindingMode
1706 : PolylineMode);
1707 first = last;
1708 }
1709 } else {
1710 strokePolygonCosmetic(points, count,
1711 path.hasImplicitClose()
1712 ? WindingMode
1713 : PolylineMode);
1714 }
1715
1716 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1717 qreal width = s->lastPen.isCosmetic()
1718 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1719 : qpen_widthf(s->lastPen) * s->txscale;
1720 int dashIndex = 0;
1721 qreal dashOffset = s->lastPen.dashOffset();
1722 bool inDash = true;
1723 qreal patternLength = 0;
1724 const QVector<qreal> pattern = s->lastPen.dashPattern();
1725 for (int i = 0; i < pattern.size(); ++i)
1726 patternLength += pattern.at(i);
1727
1728 if (patternLength > 0) {
1729 int n = qFloor(dashOffset / patternLength);
1730 dashOffset -= n * patternLength;
1731 while (dashOffset >= pattern.at(dashIndex)) {
1732 dashOffset -= pattern.at(dashIndex);
1733 if (++dashIndex >= pattern.size())
1734 dashIndex = 0;
1735 inDash = !inDash;
1736 }
1737 }
1738
1739 Q_D(QRasterPaintEngine);
1740 d->initializeRasterizer(&s->penData);
1741 int lineCount = path.elementCount() / 2;
1742 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1743
1744 for (int i = 0; i < lineCount; ++i) {
1745 if (lines[i].p1() == lines[i].p2()) {
1746 if (s->lastPen.capStyle() != Qt::FlatCap) {
1747 QPointF p = lines[i].p1();
1748 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1749 QPointF(p.x() + width*0.5, p.y())));
1750 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1751 }
1752 continue;
1753 }
1754
1755 const QLineF line = s->matrix.map(lines[i]);
1756 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1757 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1758 width / line.length(),
1759 s->lastPen.capStyle() == Qt::SquareCap);
1760 } else {
1761 d->rasterizeLine_dashed(line, width,
1762 &dashIndex, &dashOffset, &inDash);
1763 }
1764 }
1765 }
1766 else
1767 QPaintEngineEx::stroke(path, pen);
1768}
1769
1770static inline QRect toNormalizedFillRect(const QRectF &rect)
1771{
1772 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1773 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1774 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1775 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1776
1777 if (x2 < x1)
1778 qSwap(x1, x2);
1779 if (y2 < y1)
1780 qSwap(y1, y2);
1781
1782 return QRect(x1, y1, x2 - x1, y2 - y1);
1783}
1784
1785/*!
1786 \internal
1787*/
1788void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1789{
1790 if (path.isEmpty())
1791 return;
1792#ifdef QT_DEBUG_DRAW
1793 QRectF rf = path.controlPointRect();
1794 qDebug() << "QRasterPaintEngine::fill(): "
1795 << "size=" << path.elementCount()
1796 << ", hints=" << hex << path.hints()
1797 << rf << brush;
1798#endif
1799
1800 Q_D(QRasterPaintEngine);
1801 QRasterPaintEngineState *s = state();
1802
1803 ensureBrush(brush);
1804 if (!s->brushData.blend)
1805 return;
1806
1807 if (path.shape() == QVectorPath::RectangleHint) {
1808 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1809 const qreal *p = path.points();
1810 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1811 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1812 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1813 return;
1814 }
1815 ensureState();
1816 if (s->flags.tx_noshear) {
1817 d->initializeRasterizer(&s->brushData);
1818 // ### Is normalizing really necessary here?
1819 const qreal *p = path.points();
1820 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1821 if (!r.isEmpty()) {
1822 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1823 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1824 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1825 }
1826 return;
1827 }
1828 }
1829
1830 if (path.shape() == QVectorPath::EllipseHint) {
1831 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1832 const qreal *p = path.points();
1833 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1834 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1835 QRectF r = s->matrix.mapRect(QRectF(tl, br));
1836
1837 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
1838 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
1839 const QRect brect = QRect(int(r.x()), int(r.y()),
1840 int_dim(r.x(), r.width()),
1841 int_dim(r.y(), r.height()));
1842 if (brect == r) {
1843 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
1844 &s->penData, &s->brushData);
1845 return;
1846 }
1847 }
1848 }
1849
1850 // ### Optimize for non transformed ellipses and rectangles...
1851 QRectF cpRect = path.controlPointRect();
1852 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1853 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1854
1855 // ### Falcon
1856// const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1857// || deviceRect.right() > QT_RASTER_COORD_LIMIT
1858// || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1859// || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1860
1861 // ### Falonc: implement....
1862// if (!s->flags.antialiased && !do_clip) {
1863// d->initializeRasterizer(&s->brushData);
1864// d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1865// return;
1866// }
1867
1868 ensureOutlineMapper();
1869 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1870}
1871
1872void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1873{
1874 Q_D(QRasterPaintEngine);
1875 QRasterPaintEngineState *s = state();
1876
1877 if (!s->flags.antialiased) {
1878 uint txop = s->matrix.type();
1879 if (txop == QTransform::TxNone) {
1880 fillRect_normalized(toNormalizedFillRect(r), data, d);
1881 return;
1882 } else if (txop == QTransform::TxTranslate) {
1883 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1884 fillRect_normalized(rr, data, d);
1885 return;
1886 } else if (txop == QTransform::TxScale) {
1887 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1888 fillRect_normalized(rr, data, d);
1889 return;
1890 }
1891 }
1892 ensureState();
1893 if (s->flags.tx_noshear) {
1894 d->initializeRasterizer(data);
1895 QRectF nr = r.normalized();
1896 if (!nr.isEmpty()) {
1897 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1898 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1899 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1900 }
1901 return;
1902 }
1903
1904 QPainterPath path;
1905 path.addRect(r);
1906 ensureOutlineMapper();
1907 fillPath(path, data);
1908}
1909
1910/*!
1911 \reimp
1912*/
1913void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1914{
1915#ifdef QT_DEBUG_DRAW
1916 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1917#endif
1918 QRasterPaintEngineState *s = state();
1919
1920 ensureBrush(brush);
1921 if (!s->brushData.blend)
1922 return;
1923
1924 fillRect(r, &s->brushData);
1925}
1926
1927/*!
1928 \reimp
1929*/
1930void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1931{
1932#ifdef QT_DEBUG_DRAW
1933 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1934#endif
1935 Q_D(QRasterPaintEngine);
1936 QRasterPaintEngineState *s = state();
1937
1938 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1939 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1940 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1941 return;
1942 }
1943 d->solid_color_filler.clip = d->clip();
1944 d->solid_color_filler.adjustSpanMethods();
1945 fillRect(r, &d->solid_color_filler);
1946}
1947
1948static inline bool isAbove(const QPointF *a, const QPointF *b)
1949{
1950 return a->y() < b->y();
1951}
1952
1953static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1954{
1955 Q_ASSERT(upper);
1956 Q_ASSERT(lower);
1957
1958 Q_ASSERT(pointCount >= 2);
1959
1960 QVector<const QPointF *> sorted;
1961 sorted.reserve(pointCount);
1962
1963 upper->reserve(pointCount * 3 / 4);
1964 lower->reserve(pointCount * 3 / 4);
1965
1966 for (int i = 0; i < pointCount; ++i)
1967 sorted << points + i;
1968
1969 qSort(sorted.begin(), sorted.end(), isAbove);
1970
1971 qreal splitY = sorted.at(sorted.size() / 2)->y();
1972
1973 const QPointF *end = points + pointCount;
1974 const QPointF *last = end - 1;
1975
1976 QVector<QPointF> *bin[2] = { upper, lower };
1977
1978 for (const QPointF *p = points; p < end; ++p) {
1979 int side = p->y() < splitY;
1980 int lastSide = last->y() < splitY;
1981
1982 if (side != lastSide) {
1983 if (qFuzzyCompare(p->y(), splitY)) {
1984 bin[!side]->append(*p);
1985 } else if (qFuzzyCompare(last->y(), splitY)) {
1986 bin[side]->append(*last);
1987 } else {
1988 QPointF delta = *p - *last;
1989 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1990
1991 bin[0]->append(intersection);
1992 bin[1]->append(intersection);
1993 }
1994 }
1995
1996 bin[side]->append(*p);
1997
1998 last = p;
1999 }
2000
2001 // give up if we couldn't reduce the point count
2002 return upper->size() < pointCount && lower->size() < pointCount;
2003}
2004
2005/*!
2006 \internal
2007 */
2008void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2009{
2010 Q_D(QRasterPaintEngine);
2011 QRasterPaintEngineState *s = state();
2012
2013 const int maxPoints = 0xffff;
2014
2015 // max amount of points that raster engine can reliably handle
2016 if (pointCount > maxPoints) {
2017 QVector<QPointF> upper, lower;
2018
2019 if (splitPolygon(points, pointCount, &upper, &lower)) {
2020 fillPolygon(upper.constData(), upper.size(), mode);
2021 fillPolygon(lower.constData(), lower.size(), mode);
2022 } else
2023 qWarning("Polygon too complex for filling.");
2024
2025 return;
2026 }
2027
2028 // Compose polygon fill..,
2029 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2030 ensureOutlineMapper();
2031 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
2032
2033 // scanconvert.
2034 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2035 &s->brushData);
2036 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
2037}
2038
2039/*!
2040 \reimp
2041*/
2042void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2043{
2044 Q_D(QRasterPaintEngine);
2045 QRasterPaintEngineState *s = state();
2046
2047#ifdef QT_DEBUG_DRAW
2048 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
2049 for (int i=0; i<pointCount; ++i)
2050 qDebug() << " - " << points[i];
2051#endif
2052 Q_ASSERT(pointCount >= 2);
2053
2054 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
2055 QRectF r(points[0], points[2]);
2056 drawRects(&r, 1);
2057 return;
2058 }
2059
2060 ensurePen();
2061 ensureBrush();
2062 if (mode != PolylineMode) {
2063 // Do the fill...
2064 if (s->brushData.blend) {
2065 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
2066 fillPolygon(points, pointCount, mode);
2067 d->outlineMapper->setCoordinateRounding(false);
2068 }
2069 }
2070
2071 // Do the outline...
2072 if (s->penData.blend) {
2073 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2074 strokePolygonCosmetic(points, pointCount, mode);
2075 else {
2076 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2077 QPaintEngineEx::stroke(vp, s->lastPen);
2078 }
2079 }
2080}
2081
2082/*!
2083 \reimp
2084*/
2085void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2086{
2087 Q_D(QRasterPaintEngine);
2088 QRasterPaintEngineState *s = state();
2089
2090#ifdef QT_DEBUG_DRAW
2091 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
2092 for (int i=0; i<pointCount; ++i)
2093 qDebug() << " - " << points[i];
2094#endif
2095 Q_ASSERT(pointCount >= 2);
2096 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
2097 QRect r(points[0].x(),
2098 points[0].y(),
2099 points[2].x() - points[0].x(),
2100 points[2].y() - points[0].y());
2101 drawRects(&r, 1);
2102 return;
2103 }
2104
2105 ensureState();
2106 ensurePen();
2107 if (!(s->flags.int_xform && s->flags.fast_pen && (!s->penData.blend || s->pen.brush().isOpaque()))) {
2108 // this calls the float version
2109 QPaintEngineEx::drawPolygon(points, pointCount, mode);
2110 return;
2111 }
2112
2113 // Do the fill
2114 if (mode != PolylineMode) {
2115 ensureBrush();
2116 if (s->brushData.blend) {
2117 // Compose polygon fill..,
2118 ensureOutlineMapper();
2119 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
2120 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
2121 d->outlineMapper->moveTo(*points);
2122 const QPoint *p = points;
2123 const QPoint *ep = points + pointCount - 1;
2124 do {
2125 d->outlineMapper->lineTo(*(++p));
2126 } while (p < ep);
2127 d->outlineMapper->endOutline();
2128
2129 // scanconvert.
2130 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2131 &s->brushData);
2132 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2133 d->outlineMapper->setCoordinateRounding(false);
2134 }
2135 }
2136
2137 // Do the outline...
2138 if (s->penData.blend) {
2139 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2140 strokePolygonCosmetic(points, pointCount, mode);
2141 else {
2142 int count = pointCount * 2;
2143 QVarLengthArray<qreal> fpoints(count);
2144#ifdef Q_WS_MAC
2145 for (int i=0; i<count; i+=2) {
2146 fpoints[i] = ((int *) points)[i+1];
2147 fpoints[i+1] = ((int *) points)[i];
2148 }
2149#else
2150 for (int i=0; i<count; ++i)
2151 fpoints[i] = ((int *) points)[i];
2152#endif
2153 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2154 QPaintEngineEx::stroke(vp, s->lastPen);
2155 }
2156 }
2157}
2158
2159/*!
2160 \internal
2161*/
2162void QRasterPaintEngine::strokePolygonCosmetic(const QPointF *points, int pointCount, PolygonDrawMode mode)
2163{
2164 Q_D(QRasterPaintEngine);
2165 QRasterPaintEngineState *s = state();
2166
2167 Q_ASSERT(s->penData.blend);
2168 Q_ASSERT(s->flags.fast_pen);
2169
2170 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2171
2172 // Use fast path for 0 width / trivial pens.
2173 QIntRect devRect;
2174 devRect.set(d->deviceRect);
2175
2176 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2177 ? LineDrawIncludeLastPixel
2178 : LineDrawNormal);
2179 int dashOffset = int(s->lastPen.dashOffset());
2180
2181 const QPointF offs(aliasedCoordinateDelta, aliasedCoordinateDelta);
2182
2183 // Draw all the line segments.
2184 for (int i=1; i<pointCount; ++i) {
2185
2186 QPointF lp1 = points[i-1] * s->matrix + offs;
2187 QPointF lp2 = points[i] * s->matrix + offs;
2188
2189 const QRectF brect(lp1, lp2);
2190 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2191 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2192 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2193 qFloor(lp2.x()), qFloor(lp2.y()),
2194 penBlend, &s->penData,
2195 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2196 devRect);
2197 } else {
2198 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2199 qFloor(lp2.x()), qFloor(lp2.y()),
2200 &s->lastPen,
2201 penBlend, &s->penData,
2202 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2203 devRect, &dashOffset);
2204 }
2205 }
2206
2207 // Polygons are implicitly closed.
2208 if (needs_closing) {
2209 QPointF lp1 = points[pointCount-1] * s->matrix + offs;
2210 QPointF lp2 = points[0] * s->matrix + offs;
2211
2212 const QRectF brect(lp1, lp2);
2213 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2214 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2215 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2216 qFloor(lp2.x()), qFloor(lp2.y()),
2217 penBlend, &s->penData,
2218 LineDrawIncludeLastPixel,
2219 devRect);
2220 } else {
2221 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2222 qFloor(lp2.x()), qFloor(lp2.y()),
2223 &s->lastPen,
2224 penBlend, &s->penData,
2225 LineDrawIncludeLastPixel,
2226 devRect, &dashOffset);
2227 }
2228 }
2229
2230}
2231
2232/*!
2233 \internal
2234*/
2235void QRasterPaintEngine::strokePolygonCosmetic(const QPoint *points, int pointCount, PolygonDrawMode mode)
2236{
2237 Q_D(QRasterPaintEngine);
2238 QRasterPaintEngineState *s = state();
2239
2240 // We assert here because this function is called from drawRects
2241 // and drawPolygon and they already do ensurePen(), so we skip that
2242 // here to avoid duplicate checks..
2243 Q_ASSERT(s->penData.blend);
2244
2245 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2246
2247 QIntRect devRect;
2248 devRect.set(d->deviceRect);
2249
2250 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2251 ? LineDrawIncludeLastPixel
2252 : LineDrawNormal);
2253
2254 int m11 = int(s->matrix.m11());
2255 int m22 = int(s->matrix.m22());
2256 int dx = int(s->matrix.dx());
2257 int dy = int(s->matrix.dy());
2258 int m13 = int(s->matrix.m13());
2259 int m23 = int(s->matrix.m23());
2260 bool affine = !m13 && !m23;
2261
2262 int dashOffset = int(s->lastPen.dashOffset());
2263
2264 if (affine) {
2265 // Draw all the line segments.
2266 for (int i=1; i<pointCount; ++i) {
2267 const QPoint lp1 = points[i-1] * s->matrix;
2268 const QPoint lp2 = points[i] * s->matrix;
2269 const QRect brect(lp1, lp2);
2270 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2271
2272 if (qpen_style(s->lastPen) == Qt::SolidLine)
2273 drawLine_midpoint_i(lp1.x(), lp1.y(),
2274 lp2.x(), lp2.y(),
2275 penBlend, &s->penData,
2276 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2277 devRect);
2278 else
2279 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2280 lp2.x(), lp2.y(),
2281 &s->lastPen,
2282 penBlend, &s->penData,
2283 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2284 devRect, &dashOffset);
2285
2286 }
2287
2288 // Polygons are implicitly closed.
2289 if (needs_closing) {
2290 const QPoint lp1 = points[pointCount - 1] * s->matrix;
2291 const QPoint lp2 = points[0] * s->matrix;
2292 const QRect brect(lp1, lp2);
2293 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2294
2295 if (qpen_style(s->lastPen) == Qt::SolidLine)
2296 drawLine_midpoint_i(lp1.x(), lp1.y(),
2297 lp2.x(), lp2.y(),
2298 penBlend, &s->penData, LineDrawIncludeLastPixel,
2299 devRect);
2300 else
2301 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2302 lp2.x(), lp2.y(),
2303 &s->lastPen,
2304 penBlend, &s->penData, LineDrawIncludeLastPixel,
2305 devRect, &dashOffset);
2306 }
2307 } else {
2308 // Draw all the line segments.
2309 for (int i=1; i<pointCount; ++i) {
2310 int x1 = points[i-1].x() * m11 + dx;
2311 int y1 = points[i-1].y() * m22 + dy;
2312 qreal w = m13*points[i-1].x() + m23*points[i-1].y() + 1.;
2313 w = 1/w;
2314 x1 = int(x1*w);
2315 y1 = int(y1*w);
2316 int x2 = points[i].x() * m11 + dx;
2317 int y2 = points[i].y() * m22 + dy;
2318 w = m13*points[i].x() + m23*points[i].y() + 1.;
2319 w = 1/w;
2320 x2 = int(x2*w);
2321 y2 = int(y2*w);
2322
2323 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2324 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2325 if (qpen_style(s->lastPen) == Qt::SolidLine)
2326 drawLine_midpoint_i(x1, y1, x2, y2,
2327 penBlend, &s->penData,
2328 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2329 devRect);
2330 else
2331 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2332 &s->lastPen,
2333 penBlend, &s->penData,
2334 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2335 devRect, &dashOffset);
2336
2337 }
2338
2339 int x1 = points[pointCount-1].x() * m11 + dx;
2340 int y1 = points[pointCount-1].y() * m22 + dy;
2341 qreal w = m13*points[pointCount-1].x() + m23*points[pointCount-1].y() + 1.;
2342 w = 1/w;
2343 x1 = int(x1*w);
2344 y1 = int(y1*w);
2345 int x2 = points[0].x() * m11 + dx;
2346 int y2 = points[0].y() * m22 + dy;
2347 w = m13*points[0].x() + m23*points[0].y() + 1.;
2348 w = 1/w;
2349 x2 = int(x2 * w);
2350 y2 = int(y2 * w);
2351 // Polygons are implicitly closed.
2352
2353 if (needs_closing) {
2354 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2355 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2356 if (qpen_style(s->lastPen) == Qt::SolidLine)
2357 drawLine_midpoint_i(x1, y1, x2, y2,
2358 penBlend, &s->penData, LineDrawIncludeLastPixel,
2359 devRect);
2360 else
2361 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2362 &s->lastPen,
2363 penBlend, &s->penData, LineDrawIncludeLastPixel,
2364 devRect, &dashOffset);
2365 }
2366 }
2367}
2368
2369/*!
2370 \internal
2371*/
2372void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2373{
2374#ifdef QT_DEBUG_DRAW
2375 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2376#endif
2377
2378 QPixmapData *pd = pixmap.pixmapData();
2379 if (pd->classId() == QPixmapData::RasterClass) {
2380 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2381 if (image.depth() == 1) {
2382 Q_D(QRasterPaintEngine);
2383 QRasterPaintEngineState *s = state();
2384 if (s->matrix.type() <= QTransform::TxTranslate) {
2385 ensurePen();
2386 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2387 } else {
2388 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2389 }
2390 } else {
2391 QRasterPaintEngine::drawImage(pos, image);
2392 }
2393 } else {
2394 const QImage image = pixmap.toImage();
2395 if (pixmap.depth() == 1) {
2396 Q_D(QRasterPaintEngine);
2397 QRasterPaintEngineState *s = state();
2398 if (s->matrix.type() <= QTransform::TxTranslate) {
2399 ensurePen();
2400 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2401 } else {
2402 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2403 }
2404 } else {
2405 QRasterPaintEngine::drawImage(pos, image);
2406 }
2407 }
2408}
2409
2410/*!
2411 \reimp
2412*/
2413void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2414{
2415#ifdef QT_DEBUG_DRAW
2416 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2417#endif
2418
2419 QPixmapData* pd = pixmap.pixmapData();
2420 if (pd->classId() == QPixmapData::RasterClass) {
2421 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2422 if (image.depth() == 1) {
2423 Q_D(QRasterPaintEngine);
2424 QRasterPaintEngineState *s = state();
2425 if (s->matrix.type() <= QTransform::TxTranslate
2426 && r.size() == sr.size()
2427 && r.size() == pixmap.size()) {
2428 ensurePen();
2429 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2430 return;
2431 } else {
2432 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2433 }
2434 } else {
2435 drawImage(r, image, sr);
2436 }
2437 } else {
2438 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2439 const QImage image = pd->toImage(clippedSource);
2440 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2441 if (image.depth() == 1) {
2442 Q_D(QRasterPaintEngine);
2443 QRasterPaintEngineState *s = state();
2444 if (s->matrix.type() <= QTransform::TxTranslate
2445 && r.size() == sr.size()
2446 && r.size() == pixmap.size()) {
2447 ensurePen();
2448 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2449 return;
2450 } else {
2451 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2452 }
2453 } else {
2454 drawImage(r, image, translatedSource);
2455 }
2456 }
2457}
2458
2459// assumes that rect has positive width and height
2460static inline const QRect toRect_normalized(const QRectF &rect)
2461{
2462 const int x = qRound(rect.x());
2463 const int y = qRound(rect.y());
2464 const int w = int(rect.width() + qreal(0.5));
2465 const int h = int(rect.height() + qreal(0.5));
2466
2467 return QRect(x, y, w, h);
2468}
2469
2470static inline int fast_ceil_positive(const qreal &v)
2471{
2472 const int iv = int(v);
2473 if (v - iv == 0)
2474 return iv;
2475 else
2476 return iv + 1;
2477}
2478
2479static inline const QRect toAlignedRect_positive(const QRectF &rect)
2480{
2481 const int xmin = int(rect.x());
2482 const int xmax = int(fast_ceil_positive(rect.right()));
2483 const int ymin = int(rect.y());
2484 const int ymax = int(fast_ceil_positive(rect.bottom()));
2485 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2486}
2487
2488/*!
2489 \internal
2490*/
2491void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2492{
2493#ifdef QT_DEBUG_DRAW
2494 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2495#endif
2496
2497 Q_D(QRasterPaintEngine);
2498 QRasterPaintEngineState *s = state();
2499
2500 if (s->matrix.type() > QTransform::TxTranslate) {
2501 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2502 img,
2503 QRectF(0, 0, img.width(), img.height()));
2504 } else {
2505
2506 const QClipData *clip = d->clip();
2507 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2508
2509 if (s->flags.fast_images) {
2510 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2511 if (func) {
2512 if (!clip) {
2513 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2514 return;
2515 } else if (clip->hasRectClip) {
2516 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2517 return;
2518 }
2519 }
2520 }
2521
2522
2523
2524 d->image_filler.clip = clip;
2525 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2526 if (!d->image_filler.blend)
2527 return;
2528 d->image_filler.dx = -pt.x();
2529 d->image_filler.dy = -pt.y();
2530 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2531
2532 fillRect_normalized(rr, &d->image_filler, d);
2533 }
2534
2535}
2536
2537QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2538{
2539 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2540}
2541
2542namespace {
2543 enum RotationType {
2544 Rotation90,
2545 Rotation180,
2546 Rotation270,
2547 NoRotation
2548 };
2549
2550 inline RotationType qRotationType(const QTransform &transform)
2551 {
2552 QTransform::TransformationType type = transform.type();
2553
2554 if (type > QTransform::TxRotate)
2555 return NoRotation;
2556
2557 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2558 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2559 return Rotation90;
2560
2561 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2562 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2563 return Rotation180;
2564
2565 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2566 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2567 return Rotation270;
2568
2569 return NoRotation;
2570 }
2571
2572 inline bool isPixelAligned(const QRectF &rect) {
2573 return QRectF(rect.toRect()) == rect;
2574 }
2575}
2576
2577/*!
2578 \reimp
2579*/
2580void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2581 Qt::ImageConversionFlags)
2582{
2583#ifdef QT_DEBUG_DRAW
2584 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2585#endif
2586
2587 if (r.isEmpty())
2588 return;
2589
2590 Q_D(QRasterPaintEngine);
2591 QRasterPaintEngineState *s = state();
2592 int sr_l = qFloor(sr.left());
2593 int sr_r = qCeil(sr.right()) - 1;
2594 int sr_t = qFloor(sr.top());
2595 int sr_b = qCeil(sr.bottom()) - 1;
2596
2597 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2598 // as fillRect will apply the aliased coordinate delta we need to
2599 // subtract it here as we don't use it for image drawing
2600 QTransform old = s->matrix;
2601 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2602
2603 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2604 QRgb color = img.pixel(sr_l, sr_t);
2605 switch (img.format()) {
2606 case QImage::Format_ARGB32_Premultiplied:
2607 case QImage::Format_ARGB8565_Premultiplied:
2608 case QImage::Format_ARGB6666_Premultiplied:
2609 case QImage::Format_ARGB8555_Premultiplied:
2610 case QImage::Format_ARGB4444_Premultiplied:
2611 // Combine premultiplied color with the opacity set on the painter.
2612 d->solid_color_filler.solid.color =
2613 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2614 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2615 break;
2616 default:
2617 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2618 break;
2619 }
2620
2621 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2622 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2623 return;
2624 }
2625
2626 d->solid_color_filler.clip = d->clip();
2627 d->solid_color_filler.adjustSpanMethods();
2628 fillRect(r, &d->solid_color_filler);
2629
2630 s->matrix = old;
2631 return;
2632 }
2633
2634 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2635
2636 const QClipData *clip = d->clip();
2637
2638 if (s->matrix.type() > QTransform::TxTranslate
2639 && !stretch_sr
2640 && (!clip || clip->hasRectClip)
2641 && s->intOpacity == 256
2642 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2643 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2644 && d->rasterBuffer->format == img.format()
2645 && (d->rasterBuffer->format == QImage::Format_RGB16
2646 || d->rasterBuffer->format == QImage::Format_RGB32
2647 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2648 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2649 {
2650 RotationType rotationType = qRotationType(s->matrix);
2651
2652 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2653 QRectF transformedTargetRect = s->matrix.mapRect(r);
2654
2655 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2656 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2657 {
2658 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2659 if (clippedTransformedTargetRect.isNull())
2660 return;
2661
2662 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2663
2664 QRect clippedSourceRect
2665 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2666 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2667
2668 uint dbpl = d->rasterBuffer->bytesPerLine();
2669 uint sbpl = img.bytesPerLine();
2670
2671 uchar *dst = d->rasterBuffer->buffer();
2672 uint bpp = img.depth() >> 3;
2673
2674 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2675 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2676
2677 uint cw = clippedSourceRect.width();
2678 uint ch = clippedSourceRect.height();
2679
2680 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2681
2682 return;
2683 }
2684 }
2685 }
2686
2687 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2688
2689 QRectF targetBounds = s->matrix.mapRect(r);
2690 bool exceedsPrecision = targetBounds.width() > 0xffff
2691 || targetBounds.height() > 0xffff;
2692
2693 if (s->flags.fast_images && !exceedsPrecision) {
2694 if (s->matrix.type() > QTransform::TxScale) {
2695 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2696 if (func && (!clip || clip->hasRectClip)) {
2697 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2698 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2699 s->matrix, s->intOpacity);
2700 return;
2701 }
2702 } else {
2703 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2704 if (func && (!clip || clip->hasRectClip)) {
2705 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2706 img.bits(), img.bytesPerLine(),
2707 qt_mapRect_non_normalizing(r, s->matrix), sr,
2708 !clip ? d->deviceRect : clip->clipRect,
2709 s->intOpacity);
2710 return;
2711 }
2712 }
2713 }
2714
2715 QTransform copy = s->matrix;
2716 copy.translate(r.x(), r.y());
2717 if (stretch_sr)
2718 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2719 copy.translate(-sr.x(), -sr.y());
2720
2721 d->image_filler_xform.clip = clip;
2722 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2723 if (!d->image_filler_xform.blend)
2724 return;
2725 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2726
2727 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2728 QRectF rr = s->matrix.mapRect(r);
2729
2730 const int x1 = qRound(rr.x());
2731 const int y1 = qRound(rr.y());
2732 const int x2 = qRound(rr.right());
2733 const int y2 = qRound(rr.bottom());
2734
2735 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2736 return;
2737 }
2738
2739#ifdef QT_FAST_SPANS
2740 ensureState();
2741 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2742 d->initializeRasterizer(&d->image_filler_xform);
2743 d->rasterizer->setAntialiased(s->flags.antialiased);
2744
2745 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2746
2747 const QRectF &rect = r.normalized();
2748 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2749 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2750
2751 if (s->flags.tx_noshear)
2752 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2753 else
2754 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2755 return;
2756 }
2757#endif
2758 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2759 QPainterPath path;
2760 path.addRect(r);
2761 QTransform m = s->matrix;
2762 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2763 m.m21(), m.m22(), m.m23(),
2764 m.m31() - offs, m.m32() - offs, m.m33());
2765 fillPath(path, &d->image_filler_xform);
2766 s->matrix = m;
2767 } else {
2768
2769 if (s->flags.fast_images) {
2770 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2771 if (func) {
2772 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2773 if (!clip) {
2774 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2775 return;
2776 } else if (clip->hasRectClip) {
2777 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2778 return;
2779 }
2780 }
2781 }
2782
2783 d->image_filler.clip = clip;
2784 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2785 if (!d->image_filler.blend)
2786 return;
2787 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2788 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2789
2790 QRectF rr = r;
2791 rr.translate(s->matrix.dx(), s->matrix.dy());
2792
2793 const int x1 = qRound(rr.x());
2794 const int y1 = qRound(rr.y());
2795 const int x2 = qRound(rr.right());
2796 const int y2 = qRound(rr.bottom());
2797
2798 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2799 }
2800}
2801
2802/*!
2803 \reimp
2804*/
2805void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2806{
2807#ifdef QT_DEBUG_DRAW
2808 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2809#endif
2810 Q_D(QRasterPaintEngine);
2811 QRasterPaintEngineState *s = state();
2812
2813 QImage image;
2814
2815 QPixmapData *pd = pixmap.pixmapData();
2816 if (pd->classId() == QPixmapData::RasterClass) {
2817 image = static_cast<QRasterPixmapData *>(pd)->image;
2818 } else {
2819 image = pixmap.toImage();
2820 }
2821
2822 if (image.depth() == 1)
2823 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2824
2825 if (s->matrix.type() > QTransform::TxTranslate) {
2826 QTransform copy = s->matrix;
2827 copy.translate(r.x(), r.y());
2828 copy.translate(-sr.x(), -sr.y());
2829 d->image_filler_xform.clip = d->clip();
2830 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2831 if (!d->image_filler_xform.blend)
2832 return;
2833 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2834
2835#ifdef QT_FAST_SPANS
2836 ensureState();
2837 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2838 d->initializeRasterizer(&d->image_filler_xform);
2839 d->rasterizer->setAntialiased(s->flags.antialiased);
2840
2841 const QRectF &rect = r.normalized();
2842 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2843 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2844 if (s->flags.tx_noshear)
2845 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2846 else
2847 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2848 return;
2849 }
2850#endif
2851 QPainterPath path;
2852 path.addRect(r);
2853 fillPath(path, &d->image_filler_xform);
2854 } else {
2855 d->image_filler.clip = d->clip();
2856
2857 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2858 if (!d->image_filler.blend)
2859 return;
2860 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2861 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2862
2863 QRectF rr = r;
2864 rr.translate(s->matrix.dx(), s->matrix.dy());
2865 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2866 }
2867}
2868
2869
2870//QWS hack
2871static inline bool monoVal(const uchar* s, int x)
2872{
2873 return (s[x>>3] << (x&7)) & 0x80;
2874}
2875
2876/*!
2877 \internal
2878*/
2879void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2880{
2881 Q_D(QRasterPaintEngine);
2882 QRasterPaintEngineState *s = state();
2883
2884 if (!s->penData.blend)
2885 return;
2886
2887 QRasterBuffer *rb = d->rasterBuffer.data();
2888
2889 const QRect rect(rx, ry, w, h);
2890 const QClipData *clip = d->clip();
2891 bool unclipped = false;
2892 if (clip) {
2893 // inlined QRect::intersects
2894 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2895 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2896
2897 if (clip->hasRectClip) {
2898 unclipped = rx > clip->xmin
2899 && rx + w < clip->xmax
2900 && ry > clip->ymin
2901 && ry + h < clip->ymax;
2902 }
2903
2904 if (!intersects)
2905 return;
2906 } else {
2907 // inlined QRect::intersects
2908 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2909 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2910 if (!intersects)
2911 return;
2912
2913 // inlined QRect::contains
2914 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2915 && rect.top() >= 0 && rect.bottom() < rb->height();
2916
2917 unclipped = contains && d->isUnclipped_normalized(rect);
2918 }
2919
2920 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2921 const uchar * scanline = static_cast<const uchar *>(src);
2922
2923 if (s->flags.fast_text) {
2924 if (unclipped) {
2925 if (depth == 1) {
2926 if (s->penData.bitmapBlit) {
2927 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2928 scanline, w, h, bpl);
2929 return;
2930 }
2931 } else if (depth == 8) {
2932 if (s->penData.alphamapBlit) {
2933 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2934 scanline, w, h, bpl, 0);
2935 return;
2936 }
2937 } else if (depth == 32) {
2938 // (A)RGB Alpha mask where the alpha component is not used.
2939 if (s->penData.alphaRGBBlit) {
2940 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2941 (const uint *) scanline, w, h, bpl / 4, 0);
2942 return;
2943 }
2944 }
2945 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2946 // (A)RGB Alpha mask where the alpha component is not used.
2947 if (!clip) {
2948 int nx = qMax(0, rx);
2949 int ny = qMax(0, ry);
2950
2951 // Move scanline pointer to compensate for moved x and y
2952 int xdiff = nx - rx;
2953 int ydiff = ny - ry;
2954 scanline += ydiff * bpl;
2955 scanline += xdiff * (depth == 32 ? 4 : 1);
2956
2957 w -= xdiff;
2958 h -= ydiff;
2959
2960 if (nx + w > d->rasterBuffer->width())
2961 w = d->rasterBuffer->width() - nx;
2962 if (ny + h > d->rasterBuffer->height())
2963 h = d->rasterBuffer->height() - ny;
2964
2965 rx = nx;
2966 ry = ny;
2967 }
2968 if (depth == 8 && s->penData.alphamapBlit) {
2969 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2970 scanline, w, h, bpl, clip);
2971 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2972 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2973 (const uint *) scanline, w, h, bpl / 4, clip);
2974 }
2975 return;
2976 }
2977 }
2978
2979 int x0 = 0;
2980 if (rx < 0) {
2981 x0 = -rx;
2982 w -= x0;
2983 }
2984
2985 int y0 = 0;
2986 if (ry < 0) {
2987 y0 = -ry;
2988 scanline += bpl * y0;
2989 h -= y0;
2990 }
2991
2992 w = qMin(w, rb->width() - qMax(0, rx));
2993 h = qMin(h, rb->height() - qMax(0, ry));
2994
2995 if (w <= 0 || h <= 0)
2996 return;
2997
2998 const int NSPANS = 256;
2999 QSpan spans[NSPANS];
3000 int current = 0;
3001
3002 const int x1 = x0 + w;
3003 const int y1 = y0 + h;
3004
3005 if (depth == 1) {
3006 for (int y = y0; y < y1; ++y) {
3007 for (int x = x0; x < x1; ) {
3008 if (!monoVal(scanline, x)) {
3009 ++x;
3010 continue;
3011 }
3012
3013 if (current == NSPANS) {
3014 blend(current, spans, &s->penData);
3015 current = 0;
3016 }
3017 spans[current].x = x + rx;
3018 spans[current].y = y + ry;
3019 spans[current].coverage = 255;
3020 int len = 1;
3021 ++x;
3022 // extend span until we find a different one.
3023 while (x < x1 && monoVal(scanline, x)) {
3024 ++x;
3025 ++len;
3026 }
3027 spans[current].len = len;
3028 ++current;
3029 }
3030 scanline += bpl;
3031 }
3032 } else if (depth == 8) {
3033 for (int y = y0; y < y1; ++y) {
3034 for (int x = x0; x < x1; ) {
3035 // Skip those with 0 coverage
3036 if (scanline[x] == 0) {
3037 ++x;
3038 continue;
3039 }
3040
3041 if (current == NSPANS) {
3042 blend(current, spans, &s->penData);
3043 current = 0;
3044 }
3045 int coverage = scanline[x];
3046 spans[current].x = x + rx;
3047 spans[current].y = y + ry;
3048 spans[current].coverage = coverage;
3049 int len = 1;
3050 ++x;
3051
3052 // extend span until we find a different one.
3053 while (x < x1 && scanline[x] == coverage) {
3054 ++x;
3055 ++len;
3056 }
3057 spans[current].len = len;
3058 ++current;
3059 }
3060 scanline += bpl;
3061 }
3062 } else { // 32-bit alpha...
3063 uint *sl = (uint *) src;
3064 for (int y = y0; y < y1; ++y) {
3065 for (int x = x0; x < x1; ) {
3066 // Skip those with 0 coverage
3067 if ((sl[x] & 0x00ffffff) == 0) {
3068 ++x;
3069 continue;
3070 }
3071
3072 if (current == NSPANS) {
3073 blend(current, spans, &s->penData);
3074 current = 0;
3075 }
3076 uint rgbCoverage = sl[x];
3077 int coverage = qGreen(rgbCoverage);
3078 spans[current].x = x + rx;
3079 spans[current].y = y + ry;
3080 spans[current].coverage = coverage;
3081 int len = 1;
3082 ++x;
3083
3084 // extend span until we find a different one.
3085 while (x < x1 && sl[x] == rgbCoverage) {
3086 ++x;
3087 ++len;
3088 }
3089 spans[current].len = len;
3090 ++current;
3091 }
3092 sl += bpl / sizeof(uint);
3093 }
3094 }
3095// qDebug() << "alphaPenBlt: num spans=" << current
3096// << "span:" << spans->x << spans->y << spans->len << spans->coverage;
3097 // Call span func for current set of spans.
3098 if (current != 0)
3099 blend(current, spans, &s->penData);
3100}
3101
3102void QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
3103 const QFixedPoint *positions, QFontEngine *fontEngine)
3104{
3105 Q_D(QRasterPaintEngine);
3106 QRasterPaintEngineState *s = state();
3107
3108 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
3109
3110 QImageTextureGlyphCache *cache =
3111 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
3112 if (!cache) {
3113 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
3114 fontEngine->setGlyphCache(0, cache);
3115 }
3116
3117 cache->populate(fontEngine, numGlyphs, glyphs, positions);
3118
3119 const QImage &image = cache->image();
3120 int bpl = image.bytesPerLine();
3121
3122 int depth = image.depth();
3123 int rightShift = 0;
3124 int leftShift = 0;
3125 if (depth == 32)
3126 leftShift = 2; // multiply by 4
3127 else if (depth == 1)
3128 rightShift = 3; // divide by 8
3129
3130 int margin = cache->glyphMargin();
3131
3132 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
3133
3134 const uchar *bits = image.bits();
3135 for (int i=0; i<numGlyphs; ++i) {
3136 const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]);
3137 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
3138 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
3139
3140// printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
3141// c.x, c.y,
3142// c.w, c.h,
3143// c.baseLineX, c.baseLineY,
3144// glyphs[i],
3145// x, y,
3146// positions[i].x.toInt(), positions[i].y.toInt());
3147
3148 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
3149 }
3150
3151 return;
3152}
3153
3154#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
3155void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
3156{
3157 Q_D(QRasterPaintEngine);
3158 QRasterPaintEngineState *s = state();
3159
3160 QFontEngine *fontEngine = ti.fontEngine;
3161 if (fontEngine->type() != QFontEngine::S60FontEngine) {
3162 QPaintEngineEx::drawTextItem(p, ti);
3163 return;
3164 }
3165
3166 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
3167
3168 QVarLengthArray<QFixedPoint> positions;
3169 QVarLengthArray<glyph_t> glyphs;
3170 QTransform matrix = s->matrix;
3171 matrix.translate(p.x(), p.y());
3172 if (matrix.type() == QTransform::TxScale)
3173 fe->setFontScale(matrix.m11());
3174 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3175
3176 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
3177
3178 for (int i=0; i<glyphs.size(); ++i) {
3179 TOpenFontCharMetrics tmetrics;
3180 const TUint8 *glyphBitmapBytes;
3181 TSize glyphBitmapSize;
3182 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
3183 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
3184 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
3185 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
3186 }
3187
3188 if (matrix.type() == QTransform::TxScale)
3189 fe->setFontScale(1.0);
3190
3191 return;
3192}
3193#endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
3194
3195/*!
3196 * Returns true if the rectangle is completely within the current clip
3197 * state of the paint engine.
3198 */
3199bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
3200{
3201 const QClipData *cl = clip();
3202 if (!cl) {
3203 // inline contains() for performance (we know the rects are normalized)
3204 const QRect &r1 = deviceRect;
3205 return (r.left() >= r1.left() && r.right() <= r1.right()
3206 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3207 }
3208
3209
3210 if (cl->hasRectClip) {
3211 // currently all painting functions clips to deviceRect internally
3212 if (cl->clipRect == deviceRect)
3213 return true;
3214
3215 // inline contains() for performance (we know the rects are normalized)
3216 const QRect &r1 = cl->clipRect;
3217 return (r.left() >= r1.left() && r.right() <= r1.right()
3218 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3219 } else {
3220 return qt_region_strictContains(cl->clipRegion, r);
3221 }
3222}
3223
3224bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
3225 int penWidth) const
3226{
3227 Q_Q(const QRasterPaintEngine);
3228 const QRasterPaintEngineState *s = q->state();
3229 const QClipData *cl = clip();
3230 if (!cl) {
3231 QRect r = rect.normalized();
3232 // inline contains() for performance (we know the rects are normalized)
3233 const QRect &r1 = deviceRect;
3234 return (r.left() >= r1.left() && r.right() <= r1.right()
3235 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3236 }
3237
3238
3239 // currently all painting functions that call this function clip to deviceRect internally
3240 if (cl->hasRectClip && cl->clipRect == deviceRect)
3241 return true;
3242
3243 if (s->flags.antialiased)
3244 ++penWidth;
3245
3246 QRect r = rect.normalized();
3247 if (penWidth > 0) {
3248 r.setX(r.x() - penWidth);
3249 r.setY(r.y() - penWidth);
3250 r.setWidth(r.width() + 2 * penWidth);
3251 r.setHeight(r.height() + 2 * penWidth);
3252 }
3253
3254 if (cl->hasRectClip) {
3255 // inline contains() for performance (we know the rects are normalized)
3256 const QRect &r1 = cl->clipRect;
3257 return (r.left() >= r1.left() && r.right() <= r1.right()
3258 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3259 } else {
3260 return qt_region_strictContains(cl->clipRegion, r);
3261 }
3262}
3263
3264inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3265 int penWidth) const
3266{
3267 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3268}
3269
3270inline ProcessSpans
3271QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3272 const QSpanData *data) const
3273{
3274 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3275}
3276
3277inline ProcessSpans
3278QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3279 const QSpanData *data) const
3280{
3281 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3282}
3283
3284inline ProcessSpans
3285QRasterPaintEnginePrivate::getPenFunc(const QRect &rect,
3286 const QSpanData *data) const
3287{
3288 Q_Q(const QRasterPaintEngine);
3289 const QRasterPaintEngineState *s = q->state();
3290
3291 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3292 return data->blend;
3293 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->pen.widthF());
3294 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3295}
3296
3297inline ProcessSpans
3298QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
3299 const QSpanData *data) const
3300{
3301 Q_Q(const QRasterPaintEngine);
3302 const QRasterPaintEngineState *s = q->state();
3303
3304 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3305 return data->blend;
3306 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3307 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3308}
3309
3310/*!
3311 \reimp
3312*/
3313void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3314{
3315 ensurePen();
3316 ensureState();
3317
3318 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3319 textItem->fontEngine());
3320}
3321
3322/*!
3323 \reimp
3324*/
3325void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3326{
3327 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3328 QRasterPaintEngineState *s = state();
3329
3330#ifdef QT_DEBUG_DRAW
3331 Q_D(QRasterPaintEngine);
3332 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3333 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3334 d->glyphCacheType);
3335#endif
3336
3337 ensurePen();
3338 ensureState();
3339
3340#if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3341
3342 bool drawCached = true;
3343
3344 if (s->matrix.type() >= QTransform::TxProject)
3345 drawCached = false;
3346
3347 // don't try to cache huge fonts
3348 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
3349 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
3350 drawCached = false;
3351
3352 // ### Remove the TestFontEngine and Box engine crap, in these
3353 // ### cases we should delegate painting to the font engine
3354 // ### directly...
3355
3356#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3357 QFontEngine::Type fontEngineType = ti.fontEngine->type();
3358 // qDebug() << "type" << fontEngineType << s->matrix.type();
3359 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
3360 || (s->matrix.type() <= QTransform::TxTranslate
3361 && (fontEngineType == QFontEngine::TestFontEngine
3362 || fontEngineType == QFontEngine::Box))) {
3363 drawCached = false;
3364 }
3365#else
3366 if (s->matrix.type() > QTransform::TxTranslate)
3367 drawCached = false;
3368#endif
3369 if (drawCached) {
3370 QRasterPaintEngineState *s = state();
3371
3372 QVarLengthArray<QFixedPoint> positions;
3373 QVarLengthArray<glyph_t> glyphs;
3374
3375 QTransform matrix = s->matrix;
3376 matrix.translate(p.x(), p.y());
3377
3378 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3379
3380 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3381 return;
3382 }
3383
3384#elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3385 if (s->matrix.type() <= QTransform::TxTranslate
3386 || (s->matrix.type() == QTransform::TxScale
3387 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3388 drawGlyphsS60(p, ti);
3389 return;
3390 }
3391#else // Q_WS_WIN || Q_WS_MAC
3392
3393 QFontEngine *fontEngine = ti.fontEngine;
3394
3395#if defined(Q_WS_QWS)
3396 if (fontEngine->type() == QFontEngine::Box) {
3397 fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
3398 return;
3399 }
3400
3401 if (s->matrix.type() < QTransform::TxScale
3402 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3403 || (fontEngine->type() == QFontEngine::Proxy
3404 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3405 )) {
3406 fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
3407 return;
3408 }
3409#endif // Q_WS_QWS
3410
3411#if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) || defined(Q_WS_PM)) && !defined(QT_NO_FREETYPE)
3412
3413#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3414 if (fontEngine->type() == QFontEngine::QPF2) {
3415 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3416 if (renderingEngine)
3417 fontEngine = renderingEngine;
3418 }
3419#endif
3420
3421 if (fontEngine->type() != QFontEngine::Freetype) {
3422 QPaintEngineEx::drawTextItem(p, ti);
3423 return;
3424 }
3425
3426 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3427
3428 QTransform matrix = s->matrix;
3429 matrix.translate(p.x(), p.y());
3430
3431 QVarLengthArray<QFixedPoint> positions;
3432 QVarLengthArray<glyph_t> glyphs;
3433 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3434 if (glyphs.size() == 0)
3435 return;
3436
3437 // only use subpixel antialiasing when drawing to widgets
3438 QFontEngineFT::GlyphFormat neededFormat =
3439 painter()->device()->devType() == QInternal::Widget
3440 ? fe->defaultGlyphFormat()
3441 : QFontEngineFT::Format_A8;
3442
3443 if (d_func()->mono_surface
3444 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
3445 )
3446 neededFormat = QFontEngineFT::Format_Mono;
3447
3448 if (neededFormat == QFontEngineFT::Format_None)
3449 neededFormat = QFontEngineFT::Format_A8;
3450
3451 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
3452 if (s->matrix.type() >= QTransform::TxScale) {
3453 if (s->matrix.isAffine())
3454 gset = fe->loadTransformedGlyphSet(s->matrix);
3455 else
3456 gset = 0;
3457
3458 }
3459
3460 if (!gset || gset->outline_drawing
3461 || !fe->loadGlyphs(gset, glyphs.data(), glyphs.size(), neededFormat))
3462 {
3463 QPaintEngine::drawTextItem(p, ti);
3464 return;
3465 }
3466
3467 QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
3468 FT_Face lockedFace = 0;
3469
3470 int depth;
3471 switch (neededFormat) {
3472 case QFontEngineFT::Format_Mono:
3473 depth = 1;
3474 break;
3475 case QFontEngineFT::Format_A8:
3476 depth = 8;
3477 break;
3478 case QFontEngineFT::Format_A32:
3479 depth = 32;
3480 break;
3481 default:
3482 Q_ASSERT(false);
3483 depth = 0;
3484 };
3485
3486 for(int i = 0; i < glyphs.size(); i++) {
3487 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i]);
3488
3489 if (!glyph || glyph->format != neededFormat) {
3490 if (!lockedFace)
3491 lockedFace = fe->lockFace();
3492 glyph = fe->loadGlyph(gset, glyphs[i], neededFormat);
3493 }
3494
3495 if (!glyph || !glyph->data)
3496 continue;
3497
3498 int pitch;
3499 switch (neededFormat) {
3500 case QFontEngineFT::Format_Mono:
3501 pitch = ((glyph->width + 31) & ~31) >> 3;
3502 break;
3503 case QFontEngineFT::Format_A8:
3504 pitch = (glyph->width + 3) & ~3;
3505 break;
3506 case QFontEngineFT::Format_A32:
3507 pitch = glyph->width * 4;
3508 break;
3509 default:
3510 Q_ASSERT(false);
3511 pitch = 0;
3512 };
3513
3514 alphaPenBlt(glyph->data, pitch, depth,
3515 qFloor(positions[i].x + offs) + glyph->x,
3516 qFloor(positions[i].y + offs) - glyph->y,
3517 glyph->width, glyph->height);
3518 }
3519 if (lockedFace)
3520 fe->unlockFace();
3521 return;
3522
3523#endif
3524#endif
3525
3526 QPaintEngineEx::drawTextItem(p, ti);
3527}
3528
3529/*!
3530 \reimp
3531*/
3532void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3533{
3534 Q_D(QRasterPaintEngine);
3535 QRasterPaintEngineState *s = state();
3536
3537 ensurePen();
3538 qreal pw = s->lastPen.widthF();
3539 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3540 QPaintEngineEx::drawPoints(points, pointCount);
3541
3542 } else {
3543 if (!s->penData.blend)
3544 return;
3545
3546 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3547 QT_FT_Span span = { 0, 1, 0, 255 };
3548 const QPointF *end = points + pointCount;
3549 qreal trans_x, trans_y;
3550 int x, y;
3551 int left = d->deviceRect.x();
3552 int right = left + d->deviceRect.width();
3553 int top = d->deviceRect.y();
3554 int bottom = top + d->deviceRect.height();
3555 int count = 0;
3556 while (points < end) {
3557 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3558 x = qFloor(trans_x);
3559 y = qFloor(trans_y);
3560 if (x >= left && x < right && y >= top && y < bottom) {
3561 if (count > 0) {
3562 const QT_FT_Span &last = array[count - 1];
3563 // spans must be sorted on y (primary) and x (secondary)
3564 if (y < last.y || (y == last.y && x < last.x)) {
3565 s->penData.blend(count, array.constData(), &s->penData);
3566 count = 0;
3567 }
3568 }
3569
3570 span.x = x;
3571 span.y = y;
3572 array[count++] = span;
3573 }
3574 ++points;
3575 }
3576
3577 if (count > 0)
3578 s->penData.blend(count, array.constData(), &s->penData);
3579 }
3580}
3581
3582
3583void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3584{
3585 Q_D(QRasterPaintEngine);
3586 QRasterPaintEngineState *s = state();
3587
3588 ensurePen();
3589 double pw = s->lastPen.widthF();
3590 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3591 QPaintEngineEx::drawPoints(points, pointCount);
3592
3593 } else {
3594 if (!s->penData.blend)
3595 return;
3596
3597 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3598 QT_FT_Span span = { 0, 1, 0, 255 };
3599 const QPoint *end = points + pointCount;
3600 qreal trans_x, trans_y;
3601 int x, y;
3602 int left = d->deviceRect.x();
3603 int right = left + d->deviceRect.width();
3604 int top = d->deviceRect.y();
3605 int bottom = top + d->deviceRect.height();
3606 int count = 0;
3607 while (points < end) {
3608 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3609 x = qFloor(trans_x);
3610 y = qFloor(trans_y);
3611 if (x >= left && x < right && y >= top && y < bottom) {
3612 if (count > 0) {
3613 const QT_FT_Span &last = array[count - 1];
3614 // spans must be sorted on y (primary) and x (secondary)
3615 if (y < last.y || (y == last.y && x < last.x)) {
3616 s->penData.blend(count, array.constData(), &s->penData);
3617 count = 0;
3618 }
3619 }
3620
3621 span.x = x;
3622 span.y = y;
3623 array[count++] = span;
3624 }
3625 ++points;
3626 }
3627
3628 if (count > 0)
3629 s->penData.blend(count, array.constData(), &s->penData);
3630 }
3631}
3632
3633/*!
3634 \reimp
3635*/
3636void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3637{
3638#ifdef QT_DEBUG_DRAW
3639 qDebug() << " - QRasterPaintEngine::drawLine()";
3640#endif
3641 Q_D(QRasterPaintEngine);
3642 QRasterPaintEngineState *s = state();
3643
3644 ensurePen();
3645 if (s->flags.fast_pen) {
3646 QIntRect bounds; bounds.set(d->deviceRect);
3647 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3648 ? LineDrawNormal
3649 : LineDrawIncludeLastPixel;
3650
3651 int m11 = int(s->matrix.m11());
3652 int m22 = int(s->matrix.m22());
3653 int dx = qFloor(s->matrix.dx() + aliasedCoordinateDelta);
3654 int dy = qFloor(s->matrix.dy() + aliasedCoordinateDelta);
3655 for (int i=0; i<lineCount; ++i) {
3656 int dashOffset = int(s->lastPen.dashOffset());
3657 if (s->flags.int_xform) {
3658 const QLine &l = lines[i];
3659 int x1 = l.x1() * m11 + dx;
3660 int y1 = l.y1() * m22 + dy;
3661 int x2 = l.x2() * m11 + dx;
3662 int y2 = l.y2() * m22 + dy;
3663
3664 const QRect brect(QPoint(x1, y1), QPoint(x2, y2));
3665 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3666 if (qpen_style(s->lastPen) == Qt::SolidLine)
3667 drawLine_midpoint_i(x1, y1, x2, y2,
3668 penBlend, &s->penData, mode, bounds);
3669 else
3670 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
3671 &s->lastPen, penBlend,
3672 &s->penData, mode, bounds,
3673 &dashOffset);
3674 } else {
3675 QLineF line = lines[i] * s->matrix;
3676 const QRectF brect(QPointF(line.x1(), line.y1()),
3677 QPointF(line.x2(), line.y2()));
3678 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3679 if (qpen_style(s->lastPen) == Qt::SolidLine)
3680 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3681 int(line.x2()), int(line.y2()),
3682 penBlend, &s->penData, mode, bounds);
3683 else
3684 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3685 int(line.x2()), int(line.y2()),
3686 &s->lastPen, penBlend,
3687 &s->penData, mode, bounds,
3688 &dashOffset);
3689 }
3690 }
3691 } else if (s->penData.blend) {
3692 QPaintEngineEx::drawLines(lines, lineCount);
3693 }
3694}
3695
3696void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3697 qreal width,
3698 int *dashIndex,
3699 qreal *dashOffset,
3700 bool *inDash)
3701{
3702 Q_Q(QRasterPaintEngine);
3703 QRasterPaintEngineState *s = q->state();
3704
3705 const QPen &pen = s->lastPen;
3706 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3707 const QVector<qreal> pattern = pen.dashPattern();
3708
3709 qreal length = line.length();
3710 Q_ASSERT(length > 0);
3711 while (length > 0) {
3712 const bool rasterize = *inDash;
3713 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3714 QLineF l = line;
3715
3716 if (dash >= length) {
3717 dash = length;
3718 *dashOffset += dash / width;
3719 length = 0;
3720 } else {
3721 *dashOffset = 0;
3722 *inDash = !(*inDash);
3723 if (++*dashIndex >= pattern.size())
3724 *dashIndex = 0;
3725 length -= dash;
3726 l.setLength(dash);
3727 line.setP1(l.p2());
3728 }
3729
3730 if (rasterize && dash > 0)
3731 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3732 }
3733}
3734
3735/*!
3736 \reimp
3737*/
3738void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3739{
3740#ifdef QT_DEBUG_DRAW
3741 qDebug() << " - QRasterPaintEngine::drawLine()";
3742#endif
3743 Q_D(QRasterPaintEngine);
3744 QRasterPaintEngineState *s = state();
3745
3746 ensurePen();
3747 if (!s->penData.blend)
3748 return;
3749 if (s->flags.fast_pen) {
3750 QIntRect bounds;
3751 bounds.set(d->deviceRect);
3752 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3753 ? LineDrawNormal
3754 : LineDrawIncludeLastPixel;
3755
3756 for (int i=0; i<lineCount; ++i) {
3757 int dashOffset = int(s->lastPen.dashOffset());
3758 QLineF line = (lines[i] * s->matrix).translated(aliasedCoordinateDelta, aliasedCoordinateDelta);
3759 const QRectF brect(QPointF(line.x1(), line.y1()),
3760 QPointF(line.x2(), line.y2()));
3761 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3762 if (qpen_style(s->lastPen) == Qt::SolidLine)
3763 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3764 int(line.x2()), int(line.y2()),
3765 penBlend, &s->penData, mode, bounds);
3766 else
3767 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3768 int(line.x2()), int(line.y2()),
3769 &s->lastPen,
3770 penBlend, &s->penData, mode,
3771 bounds, &dashOffset);
3772 }
3773 } else {
3774 QPaintEngineEx::drawLines(lines, lineCount);
3775 }
3776}
3777
3778
3779/*!
3780 \reimp
3781*/
3782void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3783{
3784 Q_D(QRasterPaintEngine);
3785 QRasterPaintEngineState *s = state();
3786
3787 ensurePen();
3788 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3789 || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased))
3790 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3791 && !rect.isEmpty()
3792 && s->matrix.type() <= QTransform::TxScale) // no shear
3793 {
3794 ensureBrush();
3795 const QRectF r = s->matrix.mapRect(rect);
3796 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3797 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3798 const QRect brect = QRect(int(r.x()), int(r.y()),
3799 int_dim(r.x(), r.width()),
3800 int_dim(r.y(), r.height()));
3801 if (brect == r) {
3802 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3803 &s->penData, &s->brushData);
3804 return;
3805 }
3806 }
3807 QPaintEngineEx::drawEllipse(rect);
3808}
3809
3810/*!
3811 \internal
3812*/
3813#ifdef Q_WS_MAC
3814void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3815{
3816 Q_D(QRasterPaintEngine);
3817 d->cgContext = ctx;
3818}
3819
3820/*!
3821 \internal
3822*/
3823CGContextRef QRasterPaintEngine::getCGContext() const
3824{
3825 Q_D(const QRasterPaintEngine);
3826 return d->cgContext;
3827}
3828#endif
3829
3830#ifdef Q_WS_WIN
3831/*!
3832 \internal
3833*/
3834void QRasterPaintEngine::setDC(HDC hdc) {
3835 Q_D(QRasterPaintEngine);
3836 d->hdc = hdc;
3837}
3838
3839/*!
3840 \internal
3841*/
3842HDC QRasterPaintEngine::getDC() const
3843{
3844 Q_D(const QRasterPaintEngine);
3845 return d->hdc;
3846}
3847
3848/*!
3849 \internal
3850*/
3851void QRasterPaintEngine::releaseDC(HDC) const
3852{
3853}
3854
3855#endif
3856
3857/*!
3858 \internal
3859*/
3860QPoint QRasterPaintEngine::coordinateOffset() const
3861{
3862 return QPoint(0, 0);
3863}
3864
3865/*!
3866 Draws the given color \a spans with the specified \a color. The \a
3867 count parameter specifies the number of spans.
3868
3869 The default implementation does nothing; reimplement this function
3870 to draw the given color \a spans with the specified \a color. Note
3871 that this function \e must be reimplemented if the framebuffer is
3872 not memory-mapped.
3873
3874 \sa drawBufferSpan()
3875*/
3876#if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3877void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3878{
3879 Q_UNUSED(spans);
3880 Q_UNUSED(count);
3881 Q_UNUSED(color);
3882 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3883 "a non memory-mapped device");
3884}
3885
3886/*!
3887 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3888
3889 Draws the given \a buffer.
3890
3891 The default implementation does nothing; reimplement this function
3892 to draw a buffer that contains more than one color. Note that this
3893 function \e must be reimplemented if the framebuffer is not
3894 memory-mapped.
3895
3896 The \a size parameter specifies the total size of the given \a
3897 buffer, while the \a length parameter specifies the number of
3898 pixels to draw. The buffer's position is given by (\a x, \a
3899 y). The provided \a alpha value is added to each pixel in the
3900 buffer when drawing.
3901
3902 \sa drawColorSpans()
3903*/
3904void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3905 int x, int y, int length, uint const_alpha)
3906{
3907 Q_UNUSED(buffer);
3908 Q_UNUSED(bufsize);
3909 Q_UNUSED(x);
3910 Q_UNUSED(y);
3911 Q_UNUSED(length);
3912 Q_UNUSED(const_alpha);
3913 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3914 "a non memory-mapped device");
3915}
3916#endif // Q_WS_QWS
3917
3918void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3919{
3920 Q_ASSERT(fg);
3921 if (!fg->blend)
3922 return;
3923 Q_D(QRasterPaintEngine);
3924
3925 Q_ASSERT(image.depth() == 1);
3926
3927 const int spanCount = 256;
3928 QT_FT_Span spans[spanCount];
3929 int n = 0;
3930
3931 // Boundaries
3932 int w = image.width();
3933 int h = image.height();
3934 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3935 int ymin = qMax(qRound(pos.y()), 0);
3936 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3937 int xmin = qMax(qRound(pos.x()), 0);
3938
3939 int x_offset = xmin - qRound(pos.x());
3940
3941 QImage::Format format = image.format();
3942 for (int y = ymin; y < ymax; ++y) {
3943 const uchar *src = image.scanLine(y - qRound(pos.y()));
3944 if (format == QImage::Format_MonoLSB) {
3945 for (int x = 0; x < xmax - xmin; ++x) {
3946 int src_x = x + x_offset;
3947 uchar pixel = src[src_x >> 3];
3948 if (!pixel) {
3949 x += 7 - (src_x%8);
3950 continue;
3951 }
3952 if (pixel & (0x1 << (src_x & 7))) {
3953 spans[n].x = xmin + x;
3954 spans[n].y = y;
3955 spans[n].coverage = 255;
3956 int len = 1;
3957 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3958 ++src_x;
3959 ++len;
3960 }
3961 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3962 x += len;
3963 ++n;
3964 if (n == spanCount) {
3965 fg->blend(n, spans, fg);
3966 n = 0;
3967 }
3968 }
3969 }
3970 } else {
3971 for (int x = 0; x < xmax - xmin; ++x) {
3972 int src_x = x + x_offset;
3973 uchar pixel = src[src_x >> 3];
3974 if (!pixel) {
3975 x += 7 - (src_x%8);
3976 continue;
3977 }
3978 if (pixel & (0x80 >> (x & 7))) {
3979 spans[n].x = xmin + x;
3980 spans[n].y = y;
3981 spans[n].coverage = 255;
3982 int len = 1;
3983 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3984 ++src_x;
3985 ++len;
3986 }
3987 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3988 x += len;
3989 ++n;
3990 if (n == spanCount) {
3991 fg->blend(n, spans, fg);
3992 n = 0;
3993 }
3994 }
3995 }
3996 }
3997 }
3998 if (n) {
3999 fg->blend(n, spans, fg);
4000 n = 0;
4001 }
4002}
4003
4004/*!
4005 \enum QRasterPaintEngine::ClipType
4006 \internal
4007
4008 \value RectClip Indicates that the currently set clip is a single rectangle.
4009 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
4010*/
4011
4012/*!
4013 \internal
4014 Returns the type of the clip currently set.
4015*/
4016QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
4017{
4018 Q_D(const QRasterPaintEngine);
4019
4020 const QClipData *clip = d->clip();
4021 if (!clip || clip->hasRectClip)
4022 return RectClip;
4023 else
4024 return ComplexClip;
4025}
4026
4027/*!
4028 \internal
4029 Returns the bounding rect of the currently set clip.
4030*/
4031QRect QRasterPaintEngine::clipBoundingRect() const
4032{
4033 Q_D(const QRasterPaintEngine);
4034
4035 const QClipData *clip = d->clip();
4036
4037 if (!clip)
4038 return d->deviceRect;
4039
4040 if (clip->hasRectClip)
4041 return clip->clipRect;
4042
4043 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
4044}
4045
4046static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
4047{
4048 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
4049
4050 QVarLengthArray<short, 4096> buffer;
4051
4052 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
4053 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
4054 result->initialize();
4055
4056 for (int y = 0; y < c1->clipSpanHeight; ++y) {
4057 const QSpan *c1_spans = c1ClipLines[y].spans;
4058 int c1_count = c1ClipLines[y].count;
4059 const QSpan *c2_spans = c2ClipLines[y].spans;
4060 int c2_count = c2ClipLines[y].count;
4061
4062 if (c1_count == 0 && c2_count == 0)
4063 continue;
4064 if (c1_count == 0) {
4065 result->appendSpans(c2_spans, c2_count);
4066 continue;
4067 } else if (c2_count == 0) {
4068 result->appendSpans(c1_spans, c1_count);
4069 continue;
4070 }
4071
4072 // we need to merge the two
4073
4074 // find required length
4075 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
4076 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
4077 buffer.resize(max);
4078 memset(buffer.data(), 0, buffer.size() * sizeof(short));
4079
4080 // Fill with old spans.
4081 for (int i = 0; i < c1_count; ++i) {
4082 const QSpan *cs = c1_spans + i;
4083 for (int j=cs->x; j<cs->x + cs->len; ++j)
4084 buffer[j] = cs->coverage;
4085 }
4086
4087 // Fill with new spans
4088 for (int i = 0; i < c2_count; ++i) {
4089 const QSpan *cs = c2_spans + i;
4090 for (int j = cs->x; j < cs->x + cs->len; ++j) {
4091 buffer[j] += cs->coverage;
4092 if (buffer[j] > 255)
4093 buffer[j] = 255;
4094 }
4095 }
4096
4097 int x = 0;
4098 while (x<max) {
4099
4100 // Skip to next span
4101 while (x < max && buffer[x] == 0) ++x;
4102 if (x >= max) break;
4103
4104 int sx = x;
4105 int coverage = buffer[x];
4106
4107 // Find length of span
4108 while (x < max && buffer[x] == coverage)
4109 ++x;
4110
4111 result->appendSpan(sx, x - sx, y, coverage);
4112 }
4113 }
4114}
4115
4116void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
4117{
4118 Q_Q(QRasterPaintEngine);
4119 QRasterPaintEngineState *s = q->state();
4120
4121 rasterizer->setAntialiased(s->flags.antialiased);
4122
4123 QRect clipRect(deviceRect);
4124 ProcessSpans blend;
4125 // ### get from optimized rectbased QClipData
4126
4127 const QClipData *c = clip();
4128 if (c) {
4129 const QRect r(QPoint(c->xmin, c->ymin),
4130 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
4131 clipRect = clipRect.intersected(r);
4132 blend = data->blend;
4133 } else {
4134 blend = data->unclipped_blend;
4135 }
4136
4137 rasterizer->setClipRect(clipRect);
4138 rasterizer->initialize(blend, data);
4139}
4140
4141void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4142 ProcessSpans callback,
4143 QSpanData *spanData, QRasterBuffer *rasterBuffer)
4144{
4145 if (!callback || !outline)
4146 return;
4147
4148 Q_Q(QRasterPaintEngine);
4149 QRasterPaintEngineState *s = q->state();
4150
4151 if (!s->flags.antialiased) {
4152 initializeRasterizer(spanData);
4153
4154 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4155 ? Qt::WindingFill
4156 : Qt::OddEvenFill;
4157
4158 rasterizer->rasterize(outline, fillRule);
4159 return;
4160 }
4161
4162 rasterize(outline, callback, (void *)spanData, rasterBuffer);
4163}
4164
4165extern "C" {
4166 int q_gray_rendered_spans(QT_FT_Raster raster);
4167}
4168
4169void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4170 ProcessSpans callback,
4171 void *userData, QRasterBuffer *)
4172{
4173 if (!callback || !outline)
4174 return;
4175
4176 Q_Q(QRasterPaintEngine);
4177 QRasterPaintEngineState *s = q->state();
4178
4179 if (!s->flags.antialiased) {
4180 rasterizer->setAntialiased(s->flags.antialiased);
4181 rasterizer->setClipRect(deviceRect);
4182 rasterizer->initialize(callback, userData);
4183
4184 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4185 ? Qt::WindingFill
4186 : Qt::OddEvenFill;
4187
4188 rasterizer->rasterize(outline, fillRule);
4189 return;
4190 }
4191
4192 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
4193 // minimize memory reallocations. However if initial size for
4194 // raster pool is changed for lower value, reallocations will
4195 // occur normally.
4196 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
4197 int rasterPoolSize = rasterPoolInitialSize;
4198 unsigned char *rasterPoolBase;
4199#if defined(Q_WS_WIN64)
4200 rasterPoolBase =
4201 // We make use of setjmp and longjmp in qgrayraster.c which requires
4202 // 16-byte alignment, hence we hardcode this requirement here..
4203 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4204#else
4205 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
4206 rasterPoolBase = rasterPoolOnStack;
4207#endif
4208 Q_CHECK_PTR(rasterPoolBase);
4209
4210 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4211
4212 void *data = userData;
4213
4214 QT_FT_BBox clip_box = { deviceRect.x(),
4215 deviceRect.y(),
4216 deviceRect.x() + deviceRect.width(),
4217 deviceRect.y() + deviceRect.height() };
4218
4219 QT_FT_Raster_Params rasterParams;
4220 rasterParams.target = 0;
4221 rasterParams.source = outline;
4222 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
4223 rasterParams.gray_spans = 0;
4224 rasterParams.black_spans = 0;
4225 rasterParams.bit_test = 0;
4226 rasterParams.bit_set = 0;
4227 rasterParams.user = data;
4228 rasterParams.clip_box = clip_box;
4229
4230 bool done = false;
4231 int error;
4232
4233 int rendered_spans = 0;
4234
4235 while (!done) {
4236
4237 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
4238 rasterParams.gray_spans = callback;
4239 rasterParams.skip_spans = rendered_spans;
4240 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
4241
4242 // Out of memory, reallocate some more and try again...
4243 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
4244 int new_size = rasterPoolSize * 2;
4245 if (new_size > 1024 * 1024) {
4246 qWarning("QPainter: Rasterization of primitive failed");
4247 break;
4248 }
4249
4250 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
4251
4252#if defined(Q_WS_WIN64)
4253 _aligned_free(rasterPoolBase);
4254#else
4255 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4256 free(rasterPoolBase);
4257#endif
4258
4259 rasterPoolSize = new_size;
4260 rasterPoolBase =
4261#if defined(Q_WS_WIN64)
4262 // We make use of setjmp and longjmp in qgrayraster.c which requires
4263 // 16-byte alignment, hence we hardcode this requirement here..
4264 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4265#else
4266 (unsigned char *) malloc(rasterPoolSize);
4267#endif
4268 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
4269
4270 qt_ft_grays_raster.raster_done(*grayRaster.data());
4271 qt_ft_grays_raster.raster_new(grayRaster.data());
4272 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4273 } else {
4274 done = true;
4275 }
4276 }
4277
4278#if defined(Q_WS_WIN64)
4279 _aligned_free(rasterPoolBase);
4280#else
4281 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4282 free(rasterPoolBase);
4283#endif
4284}
4285
4286void QRasterPaintEnginePrivate::recalculateFastImages()
4287{
4288 Q_Q(QRasterPaintEngine);
4289 QRasterPaintEngineState *s = q->state();
4290
4291 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
4292 && rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
4293 && s->matrix.type() <= QTransform::TxShear;
4294}
4295
4296
4297
4298QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
4299{
4300 Q_ASSERT(image.depth() == 1);
4301
4302 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
4303 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
4304
4305 QRgb fg = PREMUL(color.rgba());
4306 QRgb bg = 0;
4307
4308 int height = sourceImage.height();
4309 int width = sourceImage.width();
4310 for (int y=0; y<height; ++y) {
4311 uchar *source = sourceImage.scanLine(y);
4312 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
4313 if (!source || !target)
4314 QT_THROW(std::bad_alloc()); // we must have run out of memory
4315 for (int x=0; x < width; ++x)
4316 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
4317 }
4318 return dest;
4319}
4320
4321QRasterBuffer::~QRasterBuffer()
4322{
4323}
4324
4325void QRasterBuffer::init()
4326{
4327 compositionMode = QPainter::CompositionMode_SourceOver;
4328 monoDestinationWithClut = false;
4329 destColor0 = 0;
4330 destColor1 = 0;
4331}
4332
4333QImage::Format QRasterBuffer::prepare(QImage *image)
4334{
4335 m_buffer = (uchar *)image->bits();
4336 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
4337 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
4338 bytes_per_pixel = image->depth()/8;
4339 bytes_per_line = image->bytesPerLine();
4340
4341 format = image->format();
4342 drawHelper = qDrawHelper + format;
4343 if (image->depth() == 1 && image->colorTable().size() == 2) {
4344 monoDestinationWithClut = true;
4345 destColor0 = PREMUL(image->colorTable()[0]);
4346 destColor1 = PREMUL(image->colorTable()[1]);
4347 }
4348
4349 return format;
4350}
4351
4352void QRasterBuffer::resetBuffer(int val)
4353{
4354 memset(m_buffer, val, m_height*bytes_per_line);
4355}
4356
4357
4358#if defined(Q_WS_QWS)
4359void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
4360{
4361 m_buffer = reinterpret_cast<uchar*>(device->memory());
4362 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
4363 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
4364 bytes_per_pixel = device->depth() / 8;
4365 bytes_per_line = device->bytesPerLine();
4366 format = device->format();
4367#ifndef QT_NO_RASTERCALLBACKS
4368 if (!m_buffer)
4369 drawHelper = qDrawHelperCallback + format;
4370 else
4371#endif
4372 drawHelper = qDrawHelper + format;
4373}
4374
4375int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
4376{
4377 switch (m) {
4378 case PdmWidth:
4379 return widget->frameGeometry().width();
4380 case PdmHeight:
4381 return widget->frameGeometry().height();
4382 default:
4383 break;
4384 }
4385
4386 return qt_paint_device_metric(widget, m);
4387}
4388
4389int QCustomRasterPaintDevice::bytesPerLine() const
4390{
4391 return (width() * depth() + 7) / 8;
4392}
4393
4394#elif defined(Q_OS_SYMBIAN)
4395
4396void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
4397{
4398}
4399
4400#endif // Q_OS_SYMBIAN
4401
4402/*!
4403 \class QCustomRasterPaintDevice
4404 \preliminary
4405 \ingroup qws
4406 \since 4.2
4407
4408 \brief The QCustomRasterPaintDevice class is provided to activate
4409 hardware accelerated paint engines in Qt for Embedded Linux.
4410
4411 Note that this class is only available in \l{Qt for Embedded Linux}.
4412
4413 In \l{Qt for Embedded Linux}, painting is a pure software
4414 implementation. But starting with Qt 4.2, it is
4415 possible to add an accelerated graphics driver to take advantage
4416 of available hardware resources.
4417
4418 Hardware acceleration is accomplished by creating a custom screen
4419 driver, accelerating the copying from memory to the screen, and
4420 implementing a custom paint engine accelerating the various
4421 painting operations. Then a custom paint device (derived from the
4422 QCustomRasterPaintDevice class) and a custom window surface
4423 (derived from QWSWindowSurface) must be implemented to make
4424 \l{Qt for Embedded Linux} aware of the accelerated driver.
4425
4426 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
4427 documentation for details.
4428
4429 \sa QRasterPaintEngine, QPaintDevice
4430*/
4431
4432/*!
4433 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
4434
4435 Constructs a custom raster based paint device for the given
4436 top-level \a widget.
4437*/
4438
4439/*!
4440 \fn int QCustomRasterPaintDevice::bytesPerLine() const
4441
4442 Returns the number of bytes per line in the framebuffer. Note that
4443 this number might be larger than the framebuffer width.
4444*/
4445
4446/*!
4447 \fn int QCustomRasterPaintDevice::devType() const
4448 \internal
4449*/
4450
4451/*!
4452 \fn QImage::Format QCustomRasterPaintDevice::format() const
4453
4454 Returns the format of the device's memory buffet.
4455
4456 The default format is QImage::Format_ARGB32_Premultiplied. The
4457 only other valid format is QImage::Format_RGB16.
4458*/
4459
4460/*!
4461 \fn void * QCustomRasterPaintDevice::memory () const
4462
4463 Returns a pointer to the paint device's memory buffer, or 0 if no
4464 such buffer exists.
4465*/
4466
4467/*!
4468 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4469 \reimp
4470*/
4471
4472/*!
4473 \fn QSize QCustomRasterPaintDevice::size () const
4474 \internal
4475*/
4476
4477
4478QClipData::QClipData(int height)
4479{
4480 clipSpanHeight = height;
4481 m_clipLines = 0;
4482
4483 allocated = 0;
4484 m_spans = 0;
4485 xmin = xmax = ymin = ymax = 0;
4486 count = 0;
4487
4488 enabled = true;
4489 hasRectClip = hasRegionClip = false;
4490}
4491
4492QClipData::~QClipData()
4493{
4494 if (m_clipLines)
4495 free(m_clipLines);
4496 if (m_spans)
4497 free(m_spans);
4498}
4499
4500void QClipData::initialize()
4501{
4502 if (m_spans)
4503 return;
4504
4505 if (!m_clipLines)
4506 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4507
4508 Q_CHECK_PTR(m_clipLines);
4509 QT_TRY {
4510 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4511 allocated = clipSpanHeight;
4512 Q_CHECK_PTR(m_spans);
4513
4514 QT_TRY {
4515 if (hasRectClip) {
4516 int y = 0;
4517 while (y < ymin) {
4518 m_clipLines[y].spans = 0;
4519 m_clipLines[y].count = 0;
4520 ++y;
4521 }
4522
4523 const int len = clipRect.width();
4524 count = 0;
4525 while (y < ymax) {
4526 QSpan *span = m_spans + count;
4527 span->x = xmin;
4528 span->len = len;
4529 span->y = y;
4530 span->coverage = 255;
4531 ++count;
4532
4533 m_clipLines[y].spans = span;
4534 m_clipLines[y].count = 1;
4535 ++y;
4536 }
4537
4538 while (y < clipSpanHeight) {
4539 m_clipLines[y].spans = 0;
4540 m_clipLines[y].count = 0;
4541 ++y;
4542 }
4543 } else if (hasRegionClip) {
4544
4545 const QVector<QRect> rects = clipRegion.rects();
4546 const int numRects = rects.size();
4547
4548 { // resize
4549 const int maxSpans = (ymax - ymin) * numRects;
4550 if (maxSpans > allocated) {
4551 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4552 allocated = maxSpans;
4553 }
4554 }
4555
4556 int y = 0;
4557 int firstInBand = 0;
4558 count = 0;
4559 while (firstInBand < numRects) {
4560 const int currMinY = rects.at(firstInBand).y();
4561 const int currMaxY = currMinY + rects.at(firstInBand).height();
4562
4563 while (y < currMinY) {
4564 m_clipLines[y].spans = 0;
4565 m_clipLines[y].count = 0;
4566 ++y;
4567 }
4568
4569 int lastInBand = firstInBand;
4570 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4571 ++lastInBand;
4572
4573 while (y < currMaxY) {
4574
4575 m_clipLines[y].spans = m_spans + count;
4576 m_clipLines[y].count = lastInBand - firstInBand + 1;
4577
4578 for (int r = firstInBand; r <= lastInBand; ++r) {
4579 const QRect &currRect = rects.at(r);
4580 QSpan *span = m_spans + count;
4581 span->x = currRect.x();
4582 span->len = currRect.width();
4583 span->y = y;
4584 span->coverage = 255;
4585 ++count;
4586 }
4587 ++y;
4588 }
4589
4590 firstInBand = lastInBand + 1;
4591 }
4592
4593 Q_ASSERT(count <= allocated);
4594
4595 while (y < clipSpanHeight) {
4596 m_clipLines[y].spans = 0;
4597 m_clipLines[y].count = 0;
4598 ++y;
4599 }
4600
4601 }
4602 } QT_CATCH(...) {
4603 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4604 m_spans = 0;
4605 QT_RETHROW;
4606 }
4607 } QT_CATCH(...) {
4608 free(m_clipLines); // same for clipLines
4609 m_clipLines = 0;
4610 QT_RETHROW;
4611 }
4612}
4613
4614void QClipData::fixup()
4615{
4616 Q_ASSERT(m_spans);
4617
4618 if (count == 0) {
4619 ymin = ymax = xmin = xmax = 0;
4620 return;
4621 }
4622
4623// qDebug("QClipData::fixup: count=%d",count);
4624 int y = -1;
4625 ymin = m_spans[0].y;
4626 ymax = m_spans[count-1].y + 1;
4627 xmin = INT_MAX;
4628 xmax = 0;
4629
4630 bool isRect = true;
4631 int left = m_spans[0].x;
4632 int right = m_spans[0].x + m_spans[0].len;
4633
4634 for (int i = 0; i < count; ++i) {
4635 if (m_spans[i].y != y) {
4636 if (m_spans[i].y != y + 1 && y != -1) {
4637 isRect = false;
4638 }
4639 y = m_spans[i].y;
4640 m_clipLines[y].spans = m_spans+i;
4641 m_clipLines[y].count = 0;
4642// qDebug() << " new line: y=" << y;
4643 }
4644 ++m_clipLines[y].count;
4645 int sl = (int) m_spans[i].x;
4646 int sr = sl + m_spans[i].len;
4647
4648 xmin = qMin(xmin, (int)m_spans[i].x);
4649 xmax = qMax(xmax, (int)m_spans[i].x + m_spans[i].len);
4650
4651 if (sl != left || sr != right)
4652 isRect = false;
4653 }
4654// qDebug("xmin=%d,xmax=%d,ymin=%d,ymax=%d %s", xmin, xmax, ymin, ymax, isRect ? "rectangular" : "");
4655
4656 if (isRect) {
4657 hasRectClip = true;
4658 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4659 }
4660}
4661
4662/*
4663 Convert \a rect to clip spans.
4664 */
4665void QClipData::setClipRect(const QRect &rect)
4666{
4667 if (hasRectClip && rect == clipRect)
4668 return;
4669
4670// qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4671 hasRectClip = true;
4672 hasRegionClip = false;
4673 clipRect = rect;
4674
4675 xmin = rect.x();
4676 xmax = rect.x() + rect.width();
4677 ymin = qMin(rect.y(), clipSpanHeight);
4678 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4679
4680 if (m_spans) {
4681 free(m_spans);
4682 m_spans = 0;
4683 }
4684
4685// qDebug() << xmin << xmax << ymin << ymax;
4686}
4687
4688/*
4689 Convert \a region to clip spans.
4690 */
4691void QClipData::setClipRegion(const QRegion &region)
4692{
4693 if (region.rectCount() == 1) {
4694 setClipRect(region.rects().at(0));
4695 return;
4696 }
4697
4698 hasRegionClip = true;
4699 hasRectClip = false;
4700 clipRegion = region;
4701
4702 { // set bounding rect
4703 const QRect rect = region.boundingRect();
4704 xmin = rect.x();
4705 xmax = rect.x() + rect.width();
4706 ymin = rect.y();
4707 ymax = rect.y() + rect.height();
4708 }
4709
4710 if (m_spans) {
4711 free(m_spans);
4712 m_spans = 0;
4713 }
4714
4715}
4716
4717/*!
4718 \internal
4719 spans must be sorted on y
4720*/
4721static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4722 const QSpan *spans, const QSpan *end,
4723 QSpan **outSpans, int available)
4724{
4725 const_cast<QClipData *>(clip)->initialize();
4726
4727 QSpan *out = *outSpans;
4728
4729 const QSpan *clipSpans = clip->m_spans + *currentClip;
4730 const QSpan *clipEnd = clip->m_spans + clip->count;
4731
4732 while (available && spans < end ) {
4733 if (clipSpans >= clipEnd) {
4734 spans = end;
4735 break;
4736 }
4737 if (clipSpans->y > spans->y) {
4738 ++spans;
4739 continue;
4740 }
4741 if (spans->y != clipSpans->y) {
4742 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4743 clipSpans = clip->m_clipLines[spans->y].spans;
4744 else
4745 ++clipSpans;
4746 continue;
4747 }
4748 Q_ASSERT(spans->y == clipSpans->y);
4749
4750 int sx1 = spans->x;
4751 int sx2 = sx1 + spans->len;
4752 int cx1 = clipSpans->x;
4753 int cx2 = cx1 + clipSpans->len;
4754
4755 if (cx1 < sx1 && cx2 < sx1) {
4756 ++clipSpans;
4757 continue;
4758 } else if (sx1 < cx1 && sx2 < cx1) {
4759 ++spans;
4760 continue;
4761 }
4762 int x = qMax(sx1, cx1);
4763 int len = qMin(sx2, cx2) - x;
4764 if (len) {
4765 out->x = qMax(sx1, cx1);
4766 out->len = qMin(sx2, cx2) - out->x;
4767 out->y = spans->y;
4768 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4769 ++out;
4770 --available;
4771 }
4772 if (sx2 < cx2) {
4773 ++spans;
4774 } else {
4775 ++clipSpans;
4776 }
4777 }
4778
4779 *outSpans = out;
4780 *currentClip = clipSpans - clip->m_spans;
4781 return spans;
4782}
4783
4784static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4785{
4786// qDebug() << "qt_span_fill_clipped" << spanCount;
4787 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4788
4789 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4790
4791 const int NSPANS = 256;
4792 QSpan cspans[NSPANS];
4793 int currentClip = 0;
4794 const QSpan *end = spans + spanCount;
4795 while (spans < end) {
4796 QSpan *clipped = cspans;
4797 spans = qt_intersect_spans(fillData->clip, &currentClip, spans, end, &clipped, NSPANS);
4798// qDebug() << "processed " << processed << "clipped" << clipped-cspans
4799// << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4800
4801 if (clipped - cspans)
4802 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4803 }
4804}
4805
4806/*
4807 \internal
4808 Clip spans to \a{clip}-rectangle.
4809 Returns number of unclipped spans
4810*/
4811static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4812 const QRect &clip)
4813{
4814 const short minx = clip.left();
4815 const short miny = clip.top();
4816 const short maxx = clip.right();
4817 const short maxy = clip.bottom();
4818
4819 int n = 0;
4820 for (int i = 0; i < numSpans; ++i) {
4821 if (spans[i].y > maxy)
4822 break;
4823 if (spans[i].y < miny
4824 || spans[i].x > maxx
4825 || spans[i].x + spans[i].len <= minx) {
4826 continue;
4827 }
4828 if (spans[i].x < minx) {
4829 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4830 spans[n].x = minx;
4831 } else {
4832 spans[n].x = spans[i].x;
4833 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4834 }
4835 if (spans[n].len == 0)
4836 continue;
4837 spans[n].y = spans[i].y;
4838 spans[n].coverage = spans[i].coverage;
4839 ++n;
4840 }
4841 return n;
4842}
4843
4844
4845static void qt_span_fill_clipRect(int count, const QSpan *spans,
4846 void *userData)
4847{
4848 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4849 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4850
4851 Q_ASSERT(fillData->clip);
4852 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4853
4854 // hw: check if this const_cast<> is safe!!!
4855 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4856 fillData->clip->clipRect);
4857 if (count > 0)
4858 fillData->unclipped_blend(count, spans, fillData);
4859}
4860
4861static void qt_span_clip(int count, const QSpan *spans, void *userData)
4862{
4863 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4864
4865// qDebug() << " qt_span_clip: " << count << clipData->operation;
4866// for (int i = 0; i < qMin(count, 10); ++i) {
4867// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4868// }
4869
4870 switch (clipData->operation) {
4871
4872 case Qt::IntersectClip:
4873 {
4874 QClipData *newClip = clipData->newClip;
4875 newClip->initialize();
4876
4877 int currentClip = 0;
4878 const QSpan *end = spans + count;
4879 while (spans < end) {
4880 QSpan *newspans = newClip->m_spans + newClip->count;
4881 spans = qt_intersect_spans(clipData->oldClip, &currentClip, spans, end,
4882 &newspans, newClip->allocated - newClip->count);
4883 newClip->count = newspans - newClip->m_spans;
4884 if (spans < end) {
4885 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4886 newClip->allocated *= 2;
4887 }
4888 }
4889 }
4890 break;
4891
4892 case Qt::UniteClip:
4893 case Qt::ReplaceClip:
4894 clipData->newClip->appendSpans(spans, count);
4895 break;
4896 case Qt::NoClip:
4897 break;
4898 }
4899}
4900
4901#ifndef QT_NO_DEBUG
4902QImage QRasterBuffer::bufferImage() const
4903{
4904 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4905
4906 for (int y = 0; y < m_height; ++y) {
4907 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4908
4909 for (int x=0; x<m_width; ++x) {
4910 uint argb = span[x];
4911 image.setPixel(x, y, argb);
4912 }
4913 }
4914 return image;
4915}
4916#endif
4917
4918
4919void QRasterBuffer::flushToARGBImage(QImage *target) const
4920{
4921 int w = qMin(m_width, target->width());
4922 int h = qMin(m_height, target->height());
4923
4924 for (int y=0; y<h; ++y) {
4925 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4926 QRgb *dest = (QRgb *) target->scanLine(y);
4927 for (int x=0; x<w; ++x) {
4928 QRgb pixel = sourceLine[x];
4929 int alpha = qAlpha(pixel);
4930 if (!alpha) {
4931 dest[x] = 0;
4932 } else {
4933 dest[x] = (alpha << 24)
4934 | ((255*qRed(pixel)/alpha) << 16)
4935 | ((255*qGreen(pixel)/alpha) << 8)
4936 | ((255*qBlue(pixel)/alpha) << 0);
4937 }
4938 }
4939 }
4940}
4941
4942
4943class QGradientCache
4944{
4945 struct CacheInfo
4946 {
4947 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4948 stops(s), opacity(op), interpolationMode(mode) {}
4949 uint buffer[GRADIENT_STOPTABLE_SIZE];
4950 QGradientStops stops;
4951 int opacity;
4952 QGradient::InterpolationMode interpolationMode;
4953 };
4954
4955 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4956
4957public:
4958 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4959 quint64 hash_val = 0;
4960
4961 QGradientStops stops = gradient.stops();
4962 for (int i = 0; i < stops.size() && i <= 2; i++)
4963 hash_val += stops[i].second.rgba();
4964
4965 QMutexLocker lock(&mutex);
4966 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4967
4968 if (it == cache.constEnd())
4969 return addCacheElement(hash_val, gradient, opacity);
4970 else {
4971 do {
4972 const CacheInfo &cache_info = it.value();
4973 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4974 return cache_info.buffer;
4975 ++it;
4976 } while (it != cache.constEnd() && it.key() == hash_val);
4977 // an exact match for these stops and opacity was not found, create new cache
4978 return addCacheElement(hash_val, gradient, opacity);
4979 }
4980 }
4981
4982 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4983protected:
4984 inline int maxCacheSize() const { return 60; }
4985 inline void generateGradientColorTable(const QGradient& g,
4986 uint *colorTable,
4987 int size, int opacity) const;
4988 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4989 if (cache.size() == maxCacheSize()) {
4990 int elem_to_remove = qrand() % maxCacheSize();
4991 cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK
4992 }
4993 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4994 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4995 return cache.insert(hash_val, cache_entry).value().buffer;
4996 }
4997
4998 QGradientColorTableHash cache;
4999 QMutex mutex;
5000};
5001
5002void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
5003{
5004 QGradientStops stops = gradient.stops();
5005 int stopCount = stops.count();
5006 Q_ASSERT(stopCount > 0);
5007
5008 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
5009
5010 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
5011 if (stopCount == 1) {
5012 current_color = PREMUL(current_color);
5013 for (int i = 0; i < size; ++i)
5014 colorTable[i] = current_color;
5015 return;
5016 }
5017
5018 // The position where the gradient begins and ends
5019 qreal begin_pos = stops[0].first;
5020 qreal end_pos = stops[stopCount-1].first;
5021
5022 int pos = 0; // The position in the color table.
5023 uint next_color;
5024
5025 qreal incr = 1 / qreal(size); // the double increment.
5026 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
5027
5028 // Up to first point
5029 colorTable[pos++] = PREMUL(current_color);
5030 while (dpos <= begin_pos) {
5031 colorTable[pos] = colorTable[pos - 1];
5032 ++pos;
5033 dpos += incr;
5034 }
5035
5036 int current_stop = 0; // We always interpolate between current and current + 1.
5037
5038 qreal t; // position between current left and right stops
5039 qreal t_delta; // the t increment per entry in the color table
5040
5041 if (dpos < end_pos) {
5042 // Gradient area
5043 while (dpos > stops[current_stop+1].first)
5044 ++current_stop;
5045
5046 if (current_stop != 0)
5047 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
5048 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
5049
5050 if (colorInterpolation) {
5051 current_color = PREMUL(current_color);
5052 next_color = PREMUL(next_color);
5053 }
5054
5055 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
5056 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
5057 t = (dpos - stops[current_stop].first) * c;
5058 t_delta = incr * c;
5059
5060 while (true) {
5061 Q_ASSERT(current_stop < stopCount);
5062
5063 int dist = qRound(t);
5064 int idist = 256 - dist;
5065
5066 if (colorInterpolation)
5067 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
5068 else
5069 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
5070
5071 ++pos;
5072 dpos += incr;
5073
5074 if (dpos >= end_pos)
5075 break;
5076
5077 t += t_delta;
5078
5079 int skip = 0;
5080 while (dpos > stops[current_stop+skip+1].first)
5081 ++skip;
5082
5083 if (skip != 0) {
5084 current_stop += skip;
5085 if (skip == 1)
5086 current_color = next_color;
5087 else
5088 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
5089 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
5090
5091 if (colorInterpolation) {
5092 if (skip != 1)
5093 current_color = PREMUL(current_color);
5094 next_color = PREMUL(next_color);
5095 }
5096
5097 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
5098 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
5099 t = (dpos - stops[current_stop].first) * c;
5100 t_delta = incr * c;
5101 }
5102 }
5103 }
5104
5105 // After last point
5106 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
5107 while (pos < size - 1) {
5108 colorTable[pos] = current_color;
5109 ++pos;
5110 }
5111
5112 // Make sure the last color stop is represented at the end of the table
5113 colorTable[size - 1] = current_color;
5114}
5115
5116Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
5117
5118
5119void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
5120{
5121 rasterBuffer = rb;
5122#ifdef Q_WS_QWS
5123 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
5124#endif
5125 type = None;
5126 txop = 0;
5127 bilinear = false;
5128 m11 = m22 = m33 = 1.;
5129 m12 = m13 = m21 = m23 = dx = dy = 0.0;
5130 clip = pe ? pe->d_func()->clip() : 0;
5131}
5132
5133Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
5134
5135void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
5136{
5137 Qt::BrushStyle brushStyle = qbrush_style(brush);
5138 switch (brushStyle) {
5139 case Qt::SolidPattern: {
5140 type = Solid;
5141 QColor c = qbrush_color(brush);
5142 solid.color = PREMUL(ARGB_COMBINE_ALPHA(c.rgba(), alpha));
5143 if ((solid.color & 0xff000000) == 0
5144 && compositionMode == QPainter::CompositionMode_SourceOver) {
5145 type = None;
5146 }
5147 break;
5148 }
5149
5150 case Qt::LinearGradientPattern:
5151 {
5152 type = LinearGradient;
5153 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
5154 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5155 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5156 gradient.spread = g->spread();
5157
5158 QLinearGradientData &linearData = gradient.linear;
5159
5160 linearData.origin.x = g->start().x();
5161 linearData.origin.y = g->start().y();
5162 linearData.end.x = g->finalStop().x();
5163 linearData.end.y = g->finalStop().y();
5164 break;
5165 }
5166
5167 case Qt::RadialGradientPattern:
5168 {
5169 type = RadialGradient;
5170 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
5171 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5172 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5173 gradient.spread = g->spread();
5174
5175 QRadialGradientData &radialData = gradient.radial;
5176
5177 QPointF center = g->center();
5178 radialData.center.x = center.x();
5179 radialData.center.y = center.y();
5180 QPointF focal = g->focalPoint();
5181 radialData.focal.x = focal.x();
5182 radialData.focal.y = focal.y();
5183 radialData.radius = g->radius();
5184 }
5185 break;
5186
5187 case Qt::ConicalGradientPattern:
5188 {
5189 type = ConicalGradient;
5190 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
5191 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5192 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5193 gradient.spread = QGradient::RepeatSpread;
5194
5195 QConicalGradientData &conicalData = gradient.conical;
5196
5197 QPointF center = g->center();
5198 conicalData.center.x = center.x();
5199 conicalData.center.y = center.y();
5200 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
5201 }
5202 break;
5203
5204 case Qt::Dense1Pattern:
5205 case Qt::Dense2Pattern:
5206 case Qt::Dense3Pattern:
5207 case Qt::Dense4Pattern:
5208 case Qt::Dense5Pattern:
5209 case Qt::Dense6Pattern:
5210 case Qt::Dense7Pattern:
5211 case Qt::HorPattern:
5212 case Qt::VerPattern:
5213 case Qt::CrossPattern:
5214 case Qt::BDiagPattern:
5215 case Qt::FDiagPattern:
5216 case Qt::DiagCrossPattern:
5217 type = Texture;
5218 if (!tempImage)
5219 tempImage = new QImage();
5220 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
5221 initTexture(tempImage, alpha, QTextureData::Tiled);
5222 break;
5223 case Qt::TexturePattern:
5224 type = Texture;
5225 if (!tempImage)
5226 tempImage = new QImage();
5227
5228 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
5229 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
5230 else
5231 *tempImage = brush.textureImage();
5232 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
5233 break;
5234
5235 case Qt::NoBrush:
5236 default:
5237 type = None;
5238 break;
5239 }
5240 adjustSpanMethods();
5241}
5242
5243void QSpanData::adjustSpanMethods()
5244{
5245 bitmapBlit = 0;
5246 alphamapBlit = 0;
5247 alphaRGBBlit = 0;
5248
5249 fillRect = 0;
5250
5251 switch(type) {
5252 case None:
5253 unclipped_blend = 0;
5254 break;
5255 case Solid:
5256 unclipped_blend = rasterBuffer->drawHelper->blendColor;
5257 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
5258 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
5259 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
5260 fillRect = rasterBuffer->drawHelper->fillRect;
5261 break;
5262 case LinearGradient:
5263 case RadialGradient:
5264 case ConicalGradient:
5265 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
5266 break;
5267 case Texture:
5268#ifdef Q_WS_QWS
5269#ifndef QT_NO_RASTERCALLBACKS
5270 if (!rasterBuffer->buffer())
5271 unclipped_blend = qBlendTextureCallback;
5272 else
5273#endif
5274 unclipped_blend = qBlendTexture;
5275#else
5276 unclipped_blend = qBlendTexture;
5277#endif
5278 if (!texture.imageData)
5279 unclipped_blend = 0;
5280
5281 break;
5282 }
5283 // setup clipping
5284 if (!unclipped_blend) {
5285 blend = 0;
5286 } else if (!clip) {
5287 blend = unclipped_blend;
5288 } else if (clip->hasRectClip) {
5289 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
5290 } else {
5291 blend = qt_span_fill_clipped;
5292 }
5293}
5294
5295void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
5296{
5297 QTransform delta;
5298 // make sure we round off correctly in qdrawhelper.cpp
5299 delta.translate(1.0 / 65536, 1.0 / 65536);
5300
5301 QTransform inv = (delta * matrix).inverted();
5302 m11 = inv.m11();
5303 m12 = inv.m12();
5304 m13 = inv.m13();
5305 m21 = inv.m21();
5306 m22 = inv.m22();
5307 m23 = inv.m23();
5308 m33 = inv.m33();
5309 dx = inv.dx();
5310 dy = inv.dy();
5311 txop = inv.type();
5312 bilinear = bilin;
5313
5314 const bool affine = !m13 && !m23;
5315 fast_matrix = affine
5316 && m11 * m11 + m21 * m21 < 1e4
5317 && m12 * m12 + m22 * m22 < 1e4
5318 && qAbs(dx) < 1e4
5319 && qAbs(dy) < 1e4;
5320
5321 adjustSpanMethods();
5322}
5323
5324extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
5325
5326void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
5327{
5328 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
5329 if (!d || d->height == 0) {
5330 texture.imageData = 0;
5331 texture.width = 0;
5332 texture.height = 0;
5333 texture.x1 = 0;
5334 texture.y1 = 0;
5335 texture.x2 = 0;
5336 texture.y2 = 0;
5337 texture.bytesPerLine = 0;
5338 texture.format = QImage::Format_Invalid;
5339 texture.colorTable = 0;
5340 texture.hasAlpha = alpha != 256;
5341 } else {
5342 texture.imageData = d->data;
5343 texture.width = d->width;
5344 texture.height = d->height;
5345
5346 if (sourceRect.isNull()) {
5347 texture.x1 = 0;
5348 texture.y1 = 0;
5349 texture.x2 = texture.width;
5350 texture.y2 = texture.height;
5351 } else {
5352 texture.x1 = sourceRect.x();
5353 texture.y1 = sourceRect.y();
5354 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
5355 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
5356 }
5357
5358 texture.bytesPerLine = d->bytes_per_line;
5359
5360 texture.format = d->format;
5361 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
5362 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
5363 }
5364 texture.const_alpha = alpha;
5365 texture.type = _type;
5366
5367 adjustSpanMethods();
5368}
5369
5370#ifdef Q_WS_WIN
5371
5372
5373#endif
5374
5375
5376/*!
5377 \internal
5378
5379 Draws a line using the floating point midpoint algorithm. The line
5380 \a line is already in device coords at this point.
5381*/
5382
5383static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
5384 LineDrawMode style, const QIntRect &devRect)
5385{
5386#ifdef QT_DEBUG_DRAW
5387 qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2));
5388#endif
5389
5390 int x, y;
5391 int dx, dy, d, incrE, incrNE;
5392
5393 dx = x2 - x1;
5394 dy = y2 - y1;
5395
5396 const int NSPANS = 256;
5397 QT_FT_Span spans[NSPANS];
5398 int current = 0;
5399 bool ordered = true;
5400
5401 if (dy == 0) {
5402 // specialcase horizontal lines
5403 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5404 int start = qMax(devRect.x1, qMin(x1, x2));
5405 int stop = qMax(x1, x2) + 1;
5406 int stop_clipped = qMin(devRect.x2, stop);
5407 int len = stop_clipped - start;
5408 if (style == LineDrawNormal && stop == stop_clipped)
5409 len--;
5410 if (len > 0) {
5411 spans[0].x = ushort(start);
5412 spans[0].len = ushort(len);
5413 spans[0].y = y1;
5414 spans[0].coverage = 255;
5415 span_func(1, spans, data);
5416 }
5417 }
5418 return;
5419 } else if (dx == 0) {
5420 // specialcase vertical lines
5421 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5422 int start = qMax(devRect.y1, qMin(y1, y2));
5423 int stop = qMax(y1, y2) + 1;
5424 int stop_clipped = qMin(devRect.y2, stop);
5425 int len = stop_clipped - start;
5426 if (style == LineDrawNormal && stop == stop_clipped)
5427 len--;
5428 // hw: create spans directly instead to possibly avoid clipping
5429 if (len > 0)
5430 fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0);
5431 }
5432 return;
5433 }
5434
5435
5436 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5437
5438 if (x2 < x1) { /* if coordinates are out of order */
5439 qt_swap_int(x1, x2);
5440 dx = -dx;
5441
5442 qt_swap_int(y1, y2);
5443 dy = -dy;
5444 }
5445
5446 int x_lower_limit = - 128;
5447 if (x1 < x_lower_limit) {
5448 int cy = dy * (x_lower_limit - x1) / dx + y1;
5449 drawLine_midpoint_i(x_lower_limit, cy, x2, y2, span_func, data, style, devRect);
5450 return;
5451 }
5452
5453 if (style == LineDrawNormal)
5454 --x2;
5455
5456 // In the loops below we increment before call the span function so
5457 // we need to stop one pixel before
5458 x2 = qMin(x2, devRect.x2 - 1);
5459
5460 // completely clipped, so abort
5461 if (x2 <= x1) {
5462 return;
5463 }
5464
5465 int x = x1;
5466 int y = y1;
5467
5468 if (y2 <= y1)
5469 ordered = false;
5470
5471 {
5472 const int index = (ordered ? current : NSPANS - 1 - current);
5473 spans[index].coverage = 255;
5474 spans[index].x = x;
5475 spans[index].y = y;
5476
5477 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2)
5478 spans[index].len = 1;
5479 else
5480 spans[index].len = 0;
5481 }
5482
5483 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5484 y2 = qMin(y2, devRect.y2 - 1);
5485
5486 incrE = dy * 2;
5487 d = incrE - dx;
5488 incrNE = (dy - dx) * 2;
5489
5490 if (y > y2)
5491 goto flush_and_return;
5492
5493 while (x < x2) {
5494 ++x;
5495 if (d > 0) {
5496 if (spans[current].len > 0)
5497 ++current;
5498 if (current == NSPANS) {
5499 span_func(NSPANS, spans, data);
5500 current = 0;
5501 }
5502
5503 ++y;
5504 d += incrNE;
5505 if (y > y2)
5506 goto flush_and_return;
5507
5508 spans[current].len = 0;
5509 spans[current].coverage = 255;
5510 spans[current].x = x;
5511 spans[current].y = y;
5512 } else {
5513 d += incrE;
5514 if (x == devRect.x1)
5515 spans[current].x = devRect.x1;
5516 }
5517
5518 if (x < devRect.x1 || y < devRect.y1)
5519 continue;
5520
5521 Q_ASSERT(x<devRect.x2);
5522 Q_ASSERT(y<devRect.y2);
5523 Q_ASSERT(spans[current].y == y);
5524 spans[current].len++;
5525 }
5526 if (spans[current].len > 0) {
5527 ++current;
5528 }
5529 } else { // 0-45 and 180->225 (unit circle degrees)
5530
5531 y1 = qMin(y1, devRect.y2 - 1);
5532
5533 incrE = dy * 2;
5534 d = incrE + dx;
5535 incrNE = (dy + dx) * 2;
5536
5537 if (y < devRect.y1)
5538 goto flush_and_return;
5539
5540 while (x < x2) {
5541 ++x;
5542 if (d < 0) {
5543 if (spans[NSPANS - 1 - current].len > 0)
5544 ++current;
5545 if (current == NSPANS) {
5546 span_func(NSPANS, spans, data);
5547 current = 0;
5548 }
5549
5550 --y;
5551 d += incrNE;
5552 if (y < devRect.y1)
5553 goto flush_and_return;
5554
5555 const int index = NSPANS - 1 - current;
5556 spans[index].len = 0;
5557 spans[index].coverage = 255;
5558 spans[index].x = x;
5559 spans[index].y = y;
5560 } else {
5561 d += incrE;
5562 if (x == devRect.x1)
5563 spans[NSPANS - 1 - current].x = devRect.x1;
5564 }
5565
5566 if (x < devRect.x1 || y > y1)
5567 continue;
5568
5569 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5570 Q_ASSERT(spans[NSPANS - 1 - current].y == y);
5571 spans[NSPANS - 1 - current].len++;
5572 }
5573 if (spans[NSPANS - 1 - current].len > 0) {
5574 ++current;
5575 }
5576 }
5577
5578 } else {
5579
5580 // if y is the major axis:
5581
5582 if (y2 < y1) { /* if coordinates are out of order */
5583 qt_swap_int(y1, y2);
5584 dy = -dy;
5585
5586 qt_swap_int(x1, x2);
5587 dx = -dx;
5588 }
5589
5590 int y_lower_limit = - 128;
5591 if (y1 < y_lower_limit) {
5592 int cx = dx * (y_lower_limit - y1) / dy + x1;
5593 drawLine_midpoint_i(cx, y_lower_limit, x2, y2, span_func, data, style, devRect);
5594 return;
5595 }
5596
5597 if (style == LineDrawNormal)
5598 --y2;
5599
5600 // In the loops below we increment before call the span function so
5601 // we need to stop one pixel before
5602 y2 = qMin(y2, devRect.y2 - 1);
5603
5604 // completely clipped, so abort
5605 if (y2 <= y1) {
5606 return;
5607 }
5608
5609 x = x1;
5610 y = y1;
5611
5612 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
5613 Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2);
5614 if (current == NSPANS) {
5615 span_func(NSPANS, spans, data);
5616 current = 0;
5617 }
5618 spans[current].len = 1;
5619 spans[current].coverage = 255;
5620 spans[current].x = x;
5621 spans[current].y = y;
5622 ++current;
5623 }
5624
5625 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
5626 x2 = qMin(x2, devRect.x2 - 1);
5627 incrE = dx * 2;
5628 d = incrE - dy;
5629 incrNE = (dx - dy) * 2;
5630
5631 if (x > x2)
5632 goto flush_and_return;
5633
5634 while (y < y2) {
5635 if (d > 0) {
5636 ++x;
5637 d += incrNE;
5638 if (x > x2)
5639 goto flush_and_return;
5640 } else {
5641 d += incrE;
5642 }
5643 ++y;
5644 if (x < devRect.x1 || y < devRect.y1)
5645 continue;
5646 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5647 if (current == NSPANS) {
5648 span_func(NSPANS, spans, data);
5649 current = 0;
5650 }
5651 spans[current].len = 1;
5652 spans[current].coverage = 255;
5653 spans[current].x = x;
5654 spans[current].y = y;
5655 ++current;
5656 }
5657 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
5658 x1 = qMin(x1, devRect.x2 - 1);
5659 incrE = dx * 2;
5660 d = incrE + dy;
5661 incrNE = (dx + dy) * 2;
5662
5663 if (x < devRect.x1)
5664 goto flush_and_return;
5665
5666 while (y < y2) {
5667 if (d < 0) {
5668 --x;
5669 d += incrNE;
5670 if (x < devRect.x1)
5671 goto flush_and_return;
5672 } else {
5673 d += incrE;
5674 }
5675 ++y;
5676 if (y < devRect.y1 || x > x1)
5677 continue;
5678 Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2);
5679 if (current == NSPANS) {
5680 span_func(NSPANS, spans, data);
5681 current = 0;
5682 }
5683 spans[current].len = 1;
5684 spans[current].coverage = 255;
5685 spans[current].x = x;
5686 spans[current].y = y;
5687 ++current;
5688 }
5689 }
5690 }
5691flush_and_return:
5692 if (current > 0)
5693 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
5694}
5695
5696static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern)
5697{
5698 while (offset--) {
5699 if (--*currentOffset == 0) {
5700 *inDash = !*inDash;
5701 *dashIndex = ((*dashIndex + 1) % pattern.size());
5702 *currentOffset = int(pattern[*dashIndex]);
5703 }
5704 }
5705}
5706
5707static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
5708 QPen *pen,
5709 ProcessSpans span_func, QSpanData *data,
5710 LineDrawMode style, const QIntRect &devRect,
5711 int *patternOffset)
5712{
5713#ifdef QT_DEBUG_DRAW
5714 qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset;
5715#endif
5716
5717 int x, y;
5718 int dx, dy, d, incrE, incrNE;
5719
5720 dx = x2 - x1;
5721 dy = y2 - y1;
5722
5723 Q_ASSERT(*patternOffset >= 0);
5724
5725 const QVector<qreal> penPattern = pen->dashPattern();
5726 QVarLengthArray<qreal> pattern(penPattern.size());
5727
5728 int patternLength = 0;
5729 for (int i = 0; i < penPattern.size(); ++i)
5730 patternLength += qMax<qreal>(1.0, (penPattern.at(i)));
5731
5732 // pattern must be reversed if coordinates are out of order
5733 int reverseLength = -1;
5734 if (dy == 0 && x1 > x2)
5735 reverseLength = x1 - x2;
5736 else if (dx == 0 && y1 > y2)
5737 reverseLength = y1 - y2;
5738 else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis
5739 reverseLength = qAbs(dx);
5740 else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis
5741 reverseLength = qAbs(dy);
5742
5743 const bool reversed = (reverseLength > -1);
5744 if (reversed) { // reverse pattern
5745 for (int i = 0; i < penPattern.size(); ++i)
5746 pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i));
5747
5748 *patternOffset = (patternLength - 1 - *patternOffset);
5749 *patternOffset += patternLength - (reverseLength % patternLength);
5750 *patternOffset = *patternOffset % patternLength;
5751 } else {
5752 for (int i = 0; i < penPattern.size(); ++i)
5753 pattern[i] = qMax<qreal>(1.0, penPattern.at(i));
5754 }
5755
5756 int dashIndex = 0;
5757 bool inDash = !reversed;
5758 int currPattern = int(pattern[dashIndex]);
5759
5760 // adjust pattern for offset
5761 offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern);
5762
5763 const int NSPANS = 256;
5764 QT_FT_Span spans[NSPANS];
5765 int current = 0;
5766 bool ordered = true;
5767
5768 if (dy == 0) {
5769 // specialcase horizontal lines
5770 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5771 int start_unclipped = qMin(x1, x2);
5772 int start = qMax(devRect.x1, start_unclipped);
5773 int stop = qMax(x1, x2) + 1;
5774 int stop_clipped = qMin(devRect.x2, stop);
5775 int len = stop_clipped - start;
5776 if (style == LineDrawNormal && stop == stop_clipped)
5777 len--;
5778
5779 // adjust pattern for starting offset
5780 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5781
5782 if (len > 0) {
5783 int x = start;
5784 while (x < stop_clipped) {
5785 if (current == NSPANS) {
5786 span_func(NSPANS, spans, data);
5787 current = 0;
5788 }
5789 const int dash = qMin(currPattern, stop_clipped - x);
5790 if (inDash) {
5791 spans[current].x = ushort(x);
5792 spans[current].len = ushort(dash);
5793 spans[current].y = y1;
5794 spans[current].coverage = 255;
5795 ++current;
5796 }
5797 if (dash < currPattern) {
5798 currPattern -= dash;
5799 } else {
5800 dashIndex = (dashIndex + 1) % pattern.size();
5801 currPattern = int(pattern[dashIndex]);
5802 inDash = !inDash;
5803 }
5804 x += dash;
5805 }
5806 }
5807 }
5808 goto flush_and_return;
5809 } else if (dx == 0) {
5810 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5811 int start_unclipped = qMin(y1, y2);
5812 int start = qMax(devRect.y1, start_unclipped);
5813 int stop = qMax(y1, y2) + 1;
5814 int stop_clipped = qMin(devRect.y2, stop);
5815 if (style == LineDrawNormal && stop == stop_clipped)
5816 --stop;
5817 else
5818 stop = stop_clipped;
5819
5820 // adjust pattern for starting offset
5821 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5822
5823 // loop over dashes
5824 int y = start;
5825 while (y < stop) {
5826 const int dash = qMin(currPattern, stop - y);
5827 if (inDash) {
5828 for (int i = 0; i < dash; ++i) {
5829 if (current == NSPANS) {
5830 span_func(NSPANS, spans, data);
5831 current = 0;
5832 }
5833 spans[current].x = x1;
5834 spans[current].len = 1;
5835 spans[current].coverage = 255;
5836 spans[current].y = ushort(y + i);
5837 ++current;
5838 }
5839 }
5840 if (dash < currPattern) {
5841 currPattern -= dash;
5842 } else {
5843 dashIndex = (dashIndex + 1) % pattern.size();
5844 currPattern = int(pattern[dashIndex]);
5845 inDash = !inDash;
5846 }
5847 y += dash;
5848 }
5849 }
5850 goto flush_and_return;
5851 }
5852
5853 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5854
5855 if (x2 < x1) { /* if coordinates are out of order */
5856 qt_swap_int(x1, x2);
5857 dx = -dx;
5858
5859 qt_swap_int(y1, y2);
5860 dy = -dy;
5861 }
5862
5863 if (style == LineDrawNormal)
5864 --x2;
5865
5866 // In the loops below we increment before call the span function so
5867 // we need to stop one pixel before
5868 x2 = qMin(x2, devRect.x2 - 1);
5869
5870 // completely clipped, so abort
5871 if (x2 <= x1)
5872 goto flush_and_return;
5873
5874 int x = x1;
5875 int y = y1;
5876
5877 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) {
5878 Q_ASSERT(x < devRect.x2);
5879 if (inDash) {
5880 if (current == NSPANS) {
5881 span_func(NSPANS, spans, data);
5882 current = 0;
5883 }
5884 spans[current].len = 1;
5885 spans[current].coverage = 255;
5886 spans[current].x = x;
5887 spans[current].y = y;
5888 ++current;
5889 }
5890 if (--currPattern <= 0) {
5891 inDash = !inDash;
5892 dashIndex = (dashIndex + 1) % pattern.size();
5893 currPattern = int(pattern[dashIndex]);
5894 }
5895 }
5896
5897 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5898 y2 = qMin(y2, devRect.y2 - 1);
5899
5900 incrE = dy * 2;
5901 d = incrE - dx;
5902 incrNE = (dy - dx) * 2;
5903
5904 if (y > y2)
5905 goto flush_and_return;
5906
5907 while (x < x2) {
5908 if (d > 0) {
5909 ++y;
5910 d += incrNE;
5911 if (y > y2)
5912 goto flush_and_return;
5913 } else {
5914 d += incrE;
5915 }
5916 ++x;
5917
5918 const bool skip = x < devRect.x1 || y < devRect.y1;
5919 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5920 if (inDash && !skip) {
5921 if (current == NSPANS) {
5922 span_func(NSPANS, spans, data);
5923 current = 0;
5924 }
5925 spans[current].len = 1;
5926 spans[current].coverage = 255;
5927 spans[current].x = x;
5928 spans[current].y = y;
5929 ++current;
5930 }
5931 if (--currPattern <= 0) {
5932 inDash = !inDash;
5933 dashIndex = (dashIndex + 1) % pattern.size();
5934 currPattern = int(pattern[dashIndex]);
5935 }
5936 }
5937 } else { // 0-45 and 180->225 (unit circle degrees)
5938 y1 = qMin(y1, devRect.y2 - 1);
5939
5940 incrE = dy * 2;
5941 d = incrE + dx;
5942 incrNE = (dy + dx) * 2;
5943
5944 if (y < devRect.y1)
5945 goto flush_and_return;
5946
5947 while (x < x2) {
5948 if (d < 0) {
5949 if (current > 0) {
5950 span_func(current, spans, data);
5951 current = 0;
5952 }
5953
5954 --y;
5955 d += incrNE;
5956 if (y < devRect.y1)
5957 goto flush_and_return;
5958 } else {
5959 d += incrE;
5960 }
5961 ++x;
5962
5963 const bool skip = x < devRect.x1 || y > y1;
5964 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5965 if (inDash && !skip) {
5966 if (current == NSPANS) {
5967 span_func(NSPANS, spans, data);
5968 current = 0;
5969 }
5970 spans[current].len = 1;
5971 spans[current].coverage = 255;
5972 spans[current].x = x;
5973 spans[current].y = y;
5974 ++current;
5975 }
5976 if (--currPattern <= 0) {
5977 inDash = !inDash;
5978 dashIndex = (dashIndex + 1) % pattern.size();
5979 currPattern = int(pattern[dashIndex]);
5980 }
5981 }
5982 }
5983 } else {
5984
5985 // if y is the major axis:
5986
5987 if (y2 < y1) { /* if coordinates are out of order */
5988 qt_swap_int(y1, y2);
5989 dy = -dy;
5990
5991 qt_swap_int(x1, x2);
5992 dx = -dx;
5993 }
5994
5995 if (style == LineDrawNormal)
5996 --y2;
5997
5998 // In the loops below we increment before call the span function so
5999 // we need to stop one pixel before
6000 y2 = qMin(y2, devRect.y2 - 1);
6001
6002 // completely clipped, so abort
6003 if (y2 <= y1)
6004 goto flush_and_return;
6005
6006 x = x1;
6007 y = y1;
6008
6009 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
6010 Q_ASSERT(x < devRect.x2);
6011 if (inDash) {
6012 if (current == NSPANS) {
6013 span_func(NSPANS, spans, data);
6014 current = 0;
6015 }
6016 spans[current].len = 1;
6017 spans[current].coverage = 255;
6018 spans[current].x = x;
6019 spans[current].y = y;
6020 ++current;
6021 }
6022 if (--currPattern <= 0) {
6023 inDash = !inDash;
6024 dashIndex = (dashIndex + 1) % pattern.size();
6025 currPattern = int(pattern[dashIndex]);
6026 }
6027 }
6028
6029 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
6030 x2 = qMin(x2, devRect.x2 - 1);
6031 incrE = dx * 2;
6032 d = incrE - dy;
6033 incrNE = (dx - dy) * 2;
6034
6035 if (x > x2)
6036 goto flush_and_return;
6037
6038 while (y < y2) {
6039 if (d > 0) {
6040 ++x;
6041 d += incrNE;
6042 if (x > x2)
6043 goto flush_and_return;
6044 } else {
6045 d += incrE;
6046 }
6047 ++y;
6048 const bool skip = x < devRect.x1 || y < devRect.y1;
6049 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
6050 if (inDash && !skip) {
6051 if (current == NSPANS) {
6052 span_func(NSPANS, spans, data);
6053 current = 0;
6054 }
6055 spans[current].len = 1;
6056 spans[current].coverage = 255;
6057 spans[current].x = x;
6058 spans[current].y = y;
6059 ++current;
6060 }
6061 if (--currPattern <= 0) {
6062 inDash = !inDash;
6063 dashIndex = (dashIndex + 1) % pattern.size();
6064 currPattern = int(pattern[dashIndex]);
6065 }
6066 }
6067 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
6068 x1 = qMin(x1, devRect.x2 - 1);
6069 incrE = dx * 2;
6070 d = incrE + dy;
6071 incrNE = (dx + dy) * 2;
6072
6073 if (x < devRect.x1)
6074 goto flush_and_return;
6075
6076 while (y < y2) {
6077 if (d < 0) {
6078 --x;
6079 d += incrNE;
6080 if (x < devRect.x1)
6081 goto flush_and_return;
6082 } else {
6083 d += incrE;
6084 }
6085 ++y;
6086 const bool skip = y < devRect.y1 || x > x1;
6087 Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2));
6088 if (inDash && !skip) {
6089 if (current == NSPANS) {
6090 span_func(NSPANS, spans, data);
6091 current = 0;
6092 }
6093 spans[current].len = 1;
6094 spans[current].coverage = 255;
6095 spans[current].x = x;
6096 spans[current].y = y;
6097 ++current;
6098 }
6099 if (--currPattern <= 0) {
6100 inDash = !inDash;
6101 dashIndex = (dashIndex + 1) % pattern.size();
6102 currPattern = int(pattern[dashIndex]);
6103 }
6104 }
6105 }
6106 }
6107flush_and_return:
6108 if (current > 0)
6109 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
6110
6111 // adjust offset
6112 if (reversed) {
6113 *patternOffset = (patternLength - 1 - *patternOffset);
6114 } else {
6115 *patternOffset = 0;
6116 for (int i = 0; i <= dashIndex; ++i)
6117 *patternOffset += int(pattern[i]);
6118 *patternOffset += patternLength - currPattern - 1;
6119 *patternOffset = (*patternOffset % patternLength);
6120 }
6121}
6122
6123/*!
6124 \internal
6125 \a x and \a y is relative to the midpoint of \a rect.
6126*/
6127static inline void drawEllipsePoints(int x, int y, int length,
6128 const QRect &rect,
6129 const QRect &clip,
6130 ProcessSpans pen_func, ProcessSpans brush_func,
6131 QSpanData *pen_data, QSpanData *brush_data)
6132{
6133 if (length == 0)
6134 return;
6135
6136 QT_FT_Span outline[4];
6137 const int midx = rect.x() + (rect.width() + 1) / 2;
6138 const int midy = rect.y() + (rect.height() + 1) / 2;
6139
6140 x = x + midx;
6141 y = midy - y;
6142
6143 // topleft
6144 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
6145 outline[0].len = qMin(length, x - outline[0].x);
6146 outline[0].y = y;
6147 outline[0].coverage = 255;
6148
6149 // topright
6150 outline[1].x = x;
6151 outline[1].len = length;
6152 outline[1].y = y;
6153 outline[1].coverage = 255;
6154
6155 // bottomleft
6156 outline[2].x = outline[0].x;
6157 outline[2].len = outline[0].len;
6158 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
6159 outline[2].coverage = 255;
6160
6161 // bottomright
6162 outline[3].x = x;
6163 outline[3].len = length;
6164 outline[3].y = outline[2].y;
6165 outline[3].coverage = 255;
6166
6167 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
6168 QT_FT_Span fill[2];
6169
6170 // top fill
6171 fill[0].x = outline[0].x + outline[0].len - 1;
6172 fill[0].len = qMax(0, outline[1].x - fill[0].x);
6173 fill[0].y = outline[1].y;
6174 fill[0].coverage = 255;
6175
6176 // bottom fill
6177 fill[1].x = outline[2].x + outline[2].len - 1;
6178 fill[1].len = qMax(0, outline[3].x - fill[1].x);
6179 fill[1].y = outline[3].y;
6180 fill[1].coverage = 255;
6181
6182 int n = (fill[0].y >= fill[1].y ? 1 : 2);
6183 n = qt_intersect_spans(fill, n, clip);
6184 if (n > 0)
6185 brush_func(n, fill, brush_data);
6186 }
6187 if (pen_func) {
6188 int n = (outline[1].y >= outline[2].y ? 2 : 4);
6189 n = qt_intersect_spans(outline, n, clip);
6190 if (n > 0)
6191 pen_func(n, outline, pen_data);
6192 }
6193}
6194
6195/*!
6196 \internal
6197 Draws an ellipse using the integer point midpoint algorithm.
6198*/
6199static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
6200 ProcessSpans pen_func, ProcessSpans brush_func,
6201 QSpanData *pen_data, QSpanData *brush_data)
6202{
6203 const qreal a = qreal(rect.width()) / 2;
6204 const qreal b = qreal(rect.height()) / 2;
6205 qreal d = b*b - (a*a*b) + 0.25*a*a;
6206
6207 int x = 0;
6208 int y = (rect.height() + 1) / 2;
6209 int startx = x;
6210
6211 // region 1
6212 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
6213 if (d < 0) { // select E
6214 d += b*b*(2*x + 3);
6215 ++x;
6216 } else { // select SE
6217 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
6218 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6219 pen_func, brush_func, pen_data, brush_data);
6220 startx = ++x;
6221 --y;
6222 }
6223 }
6224 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6225 pen_func, brush_func, pen_data, brush_data);
6226
6227 // region 2
6228 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
6229 const int miny = rect.height() & 0x1;
6230 while (y > miny) {
6231 if (d < 0) { // select SE
6232 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
6233 ++x;
6234 } else { // select S
6235 d += a*a*(-2*y + 3);
6236 }
6237 --y;
6238 drawEllipsePoints(x, y, 1, rect, clip,
6239 pen_func, brush_func, pen_data, brush_data);
6240 }
6241}
6242
6243/*!
6244 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
6245 \overload
6246
6247 Draws the first \a pointCount points in the buffer \a points
6248
6249 The default implementation converts the first \a pointCount QPoints in \a points
6250 to QPointFs and calls the floating point version of drawPoints.
6251*/
6252
6253/*!
6254 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
6255 \overload
6256
6257 Reimplement this function to draw the largest ellipse that can be
6258 contained within rectangle \a rect.
6259*/
6260
6261#ifdef QT_DEBUG_DRAW
6262void dumpClip(int width, int height, const QClipData *clip)
6263{
6264 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
6265 clipImg.fill(0xffff0000);
6266
6267 int x0 = width;
6268 int x1 = 0;
6269 int y0 = height;
6270 int y1 = 0;
6271
6272 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
6273
6274 for (int i = 0; i < clip->count; ++i) {
6275 const QSpan *span = ((QClipData *) clip)->spans() + i;
6276 for (int j = 0; j < span->len; ++j)
6277 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
6278 x0 = qMin(x0, int(span->x));
6279 x1 = qMax(x1, int(span->x + span->len - 1));
6280
6281 y0 = qMin(y0, int(span->y));
6282 y1 = qMax(y1, int(span->y));
6283 }
6284
6285 static int counter = 0;
6286
6287 Q_ASSERT(y0 >= 0);
6288 Q_ASSERT(x0 >= 0);
6289 Q_ASSERT(y1 >= 0);
6290 Q_ASSERT(x1 >= 0);
6291
6292 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
6293 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));
6294}
6295#endif
6296
6297
6298QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.