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

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 17 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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