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

Last change on this file since 574 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

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