Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpaintengine_x11.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QtCore/qrandom.h>
6
7#include <private/qpixmapcache_p.h>
8#include <private/qpaintengine_p.h>
9#include <private/qpainterpath_p.h>
10#include <private/qdrawhelper_p.h>
11#include <private/qfontengineglyphcache_p.h>
12
13#if QT_CONFIG(fontconfig)
14#include <private/qfontengine_ft_p.h>
15#endif
16
19#include "qtessellator_p.h"
20#include "qpixmap_x11_p.h"
22#include "qt_x11_p.h"
23#include "qxcbexport.h"
25
26QT_BEGIN_NAMESPACE
27
28using namespace Qt::StringLiterals;
29
30#if QT_CONFIG(xrender)
31
32class QXRenderTessellator : public QTessellator
33{
34public:
35 QXRenderTessellator() : traps(0), allocated(0), size(0) {}
36 ~QXRenderTessellator() { free(traps); }
37 XTrapezoid *traps;
38 int allocated;
39 int size;
40 void addTrap(const Trapezoid &trap) override;
41 QRect tessellate(const QPointF *points, int nPoints, bool winding) {
42 size = 0;
43 setWinding(winding);
44 return QTessellator::tessellate(points, nPoints).toRect();
45 }
46 void done() {
47 if (allocated > 64) {
48 free(traps);
49 traps = 0;
50 allocated = 0;
51 }
52 }
53};
54
55void QXRenderTessellator::addTrap(const Trapezoid &trap)
56{
57 if (size == allocated) {
58 allocated = qMax(2*allocated, 64);
59 traps = q_check_ptr((XTrapezoid *)realloc(traps, allocated * sizeof(XTrapezoid)));
60 }
61 traps[size].top = Q27Dot5ToXFixed(trap.top);
62 traps[size].bottom = Q27Dot5ToXFixed(trap.bottom);
63 traps[size].left.p1.x = Q27Dot5ToXFixed(trap.topLeft->x);
64 traps[size].left.p1.y = Q27Dot5ToXFixed(trap.topLeft->y);
65 traps[size].left.p2.x = Q27Dot5ToXFixed(trap.bottomLeft->x);
66 traps[size].left.p2.y = Q27Dot5ToXFixed(trap.bottomLeft->y);
67 traps[size].right.p1.x = Q27Dot5ToXFixed(trap.topRight->x);
68 traps[size].right.p1.y = Q27Dot5ToXFixed(trap.topRight->y);
69 traps[size].right.p2.x = Q27Dot5ToXFixed(trap.bottomRight->x);
70 traps[size].right.p2.y = Q27Dot5ToXFixed(trap.bottomRight->y);
71 ++size;
72}
73
74#endif // QT_CONFIG(xrender)
75
77{
78 Q_DECLARE_PUBLIC(QX11PaintEngine)
79public:
81 {
82 opacity = 1.0;
83 scrn = -1;
84 hd = 0;
85 picture = 0;
86 gc = gc_brush = 0;
87 dpy = 0;
88 xinfo = 0;
90 has_clipping = false;
91 render_hints = 0;
92 xform_scale = 1;
93#if QT_CONFIG(xrender)
94 tessellator = 0;
95#endif
96 }
97 enum GCMode {
100 };
101
102 void init();
103 void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode,
104 QPaintEngine::PolygonDrawMode mode);
105 void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode,
106 QPaintEngine::PolygonDrawMode mode);
107 void fillPath(const QPainterPath &path, GCMode gcmode, bool transform);
108 void strokePolygon_dev(const QPointF *points, int pointCount, bool close);
109 void strokePolygon_translated(const QPointF *points, int pointCount, bool close);
110 void setupAdaptedOrigin(const QPoint &p);
113 use_path_fallback = has_alpha_brush
114 || has_alpha_pen
115 || has_custom_pen
116 || has_complex_xform
117 || (render_hints & QPainter::Antialiasing);
118 }
120 adjust_coords = false;
121 }
122 void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly);
124 inline bool isCosmeticPen() const {
125 return cpen.isCosmetic();
126 }
127
128 Display *dpy;
129 int scrn;
131 unsigned long hd;
132 QPixmap brush_pm;
133#if QT_CONFIG(xrender)
134 unsigned long picture;
135 unsigned long current_brush;
138#else
139 unsigned long picture;
140#endif
141 GC gc;
143
149
168
173
175 {
177 };
179
181#if QT_CONFIG(xrender)
183#endif
184};
185
186#if QT_CONFIG(xrender)
187class QXRenderGlyphCache : public QFontEngineGlyphCache
188{
189public:
190 QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix);
191 ~QXRenderGlyphCache();
192
193 bool addGlyphs(const QTextItemInt &ti,
194 const QVarLengthArray<glyph_t> &glyphs,
195 const QVarLengthArray<QFixedPoint> &positions);
196 bool draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti);
197
198 inline GlyphSet glyphSet();
199 inline int glyphBufferSize(const QFontEngineFT::Glyph &glyph) const;
200
201 inline QImage::Format imageFormat() const;
202 inline const XRenderPictFormat *renderPictFormat() const;
203
204 static inline QFontEngine::GlyphFormat glyphFormatForDepth(QFontEngine *fontEngine, int depth);
205 static inline Glyph glyphId(glyph_t glyph, QFixed subPixelPosition);
206
207 static inline bool isValidCoordinate(const QFixedPoint &fp);
208
209private:
210 QXcbX11Info xinfo;
211 GlyphSet gset;
212 QSet<Glyph> cachedGlyphs;
213};
214#endif // QT_CONFIG(xrender)
215
216extern QPixmap qt_pixmapForBrush(int brushStyle, bool invert); //in qbrush.cpp
217extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
218
219extern "C" {
220Q_XCB_EXPORT Drawable qt_x11Handle(const QPaintDevice *pd)
221{
222 if (!pd)
223 return 0;
224
225// if (pd->devType() == QInternal::Widget)
226// return static_cast<const QWidget *>(pd)->handle();
227
228 if (pd->devType() == QInternal::Pixmap)
229 return qt_x11PixmapHandle(*static_cast<const QPixmap *>(pd));
230
231 return 0;
232}
233}
234
235static const QXcbX11Info *qt_x11Info(const QPaintDevice *pd)
236{
237 if (!pd)
238 return 0;
239
240// if (pd->devType() == QInternal::Widget)
241// return &static_cast<const QWidget *>(pd)->x11Info();
242
243 if (pd->devType() == QInternal::Pixmap)
244 return &qt_x11Info(*static_cast<const QPixmap *>(pd));
245
246 return 0;
247}
248
249// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
250static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
251
252#undef X11 // defined in qt_x11_p.h
253extern "C" {
254/*!
255 Returns the X11 specific pen GC for the painter \a p. Note that
256 QPainter::begin() must be called before this function returns a
257 valid GC.
258*/
259GC Q_XCB_EXPORT qt_x11_get_pen_gc(QPainter *p)
260{
261 if (p && p->paintEngine()
262 && p->paintEngine()->isActive()
263 && p->paintEngine()->type() == QPaintEngine::X11) {
264 return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc;
265 }
266 return 0;
267}
268
269/*!
270 Returns the X11 specific brush GC for the painter \a p. Note that
271 QPainter::begin() must be called before this function returns a
272 valid GC.
273*/
274GC Q_XCB_EXPORT qt_x11_get_brush_gc(QPainter *p)
275{
276 if (p && p->paintEngine()
277 && p->paintEngine()->isActive()
278 && p->paintEngine()->type() == QPaintEngine::X11) {
279 return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc_brush;
280 }
281 return 0;
282}
283}
284#define X11 qt_x11Data
285
286// internal helper. Converts an integer value to an unique string token
287template <typename T>
289{
290 inline HexString(const T t)
291 : val(t)
292 {}
293
294 inline void write(QChar *&dest) const
295 {
296 const ushort hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
297 const char *c = reinterpret_cast<const char *>(&val);
298 for (uint i = 0; i < sizeof(T); ++i) {
299 *dest++ = hexChars[*c & 0xf];
300 *dest++ = hexChars[(*c & 0xf0) >> 4];
301 ++c;
302 }
303 }
304 const T val;
305};
306
307// specialization to enable fast concatenating of our string tokens to a string
308template <typename T>
310{
311 typedef HexString<T> type;
312 enum { ExactSize = true };
313 static int size(const HexString<T> &) { return sizeof(T) * 2; }
314 static inline void appendTo(const HexString<T> &str, QChar *&out) { str.write(out); }
316};
317
318#if QT_CONFIG(xrender)
319static const int compositionModeToRenderOp[QPainter::CompositionMode_Xor + 1] = {
320 PictOpOver, //CompositionMode_SourceOver,
321 PictOpOverReverse, //CompositionMode_DestinationOver,
322 PictOpClear, //CompositionMode_Clear,
323 PictOpSrc, //CompositionMode_Source,
324 PictOpDst, //CompositionMode_Destination,
325 PictOpIn, //CompositionMode_SourceIn,
326 PictOpInReverse, //CompositionMode_DestinationIn,
327 PictOpOut, //CompositionMode_SourceOut,
328 PictOpOutReverse, //CompositionMode_DestinationOut,
329 PictOpAtop, //CompositionMode_SourceAtop,
330 PictOpAtopReverse, //CompositionMode_DestinationAtop,
331 PictOpXor //CompositionMode_Xor
332};
333
334static inline int qpainterOpToXrender(QPainter::CompositionMode mode)
335{
336 Q_ASSERT(mode <= QPainter::CompositionMode_Xor);
337 return compositionModeToRenderOp[mode];
338}
339
340static inline bool complexPictOp(int op)
341{
342 return op != PictOpOver && op != PictOpSrc;
343}
344#endif
345
346static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2,
347#if QT_CONFIG(xrender)
349#else
350 Qt::HANDLE picture,
351#endif
352 const QRegion &r)
353{
354// int num;
355// XRectangle *rects = (XRectangle *)qt_getClipRects(r, num);
356 QList<XRectangle> rects = qt_region_to_xrectangles(r);
357 int num = rects.size();
358
359 if (gc)
360 XSetClipRectangles( dpy, gc, 0, 0, rects.data(), num, Unsorted );
361 if (gc2)
362 XSetClipRectangles( dpy, gc2, 0, 0, rects.data(), num, Unsorted );
363
364#if QT_CONFIG(xrender)
365 if (picture)
366 XRenderSetPictureClipRectangles(dpy, picture, 0, 0, rects.data(), num);
367#else
368 Q_UNUSED(picture);
369#endif // QT_CONFIG(xrender)
370}
371
372
373static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2,
374#if QT_CONFIG(xrender)
376#else
377 Qt::HANDLE picture
378#endif
379 )
380{
381 if (gc)
382 XSetClipMask(dpy, gc, XNone);
383 if (gc2)
384 XSetClipMask(dpy, gc2, XNone);
385
386#if QT_CONFIG(xrender)
387 if (picture) {
388 XRenderPictureAttributes attrs;
389 attrs.clip_mask = XNone;
390 XRenderChangePicture (dpy, picture, CPClipMask, &attrs);
391 }
392#else
393 Q_UNUSED(picture);
394#endif // QT_CONFIG(xrender)
395}
396
397
398#define DITHER_SIZE 16
400 { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
401 { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
402 { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
403 { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
404 { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
405 { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
406 { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
407 { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
408 { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
409 { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
410 { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
411 { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
412 { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
413 { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
414 { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
415 { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
416};
417
418static QPixmap qt_patternForAlpha(uchar alpha, int screen)
419{
420 QPixmap pm;
421 QString key = "$qt-alpha-brush$"_L1
422 % HexString<uchar>(alpha)
423 % HexString<int>(screen);
424
425 if (!QPixmapCache::find(key, &pm)) {
426 // #### why not use a mono image here????
427 QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
428 pattern.fill(0xffffffff);
429 for (int y = 0; y < DITHER_SIZE; ++y) {
430 for (int x = 0; x < DITHER_SIZE; ++x) {
431 if (base_dither_matrix[x][y] <= alpha)
432 pattern.setPixel(x, y, 0x00000000);
433 }
434 }
435 pm = QBitmap::fromImage(pattern);
436 qt_x11SetScreen(pm, screen);
437 //pm.x11SetScreen(screen);
438 QPixmapCache::insert(key, pm);
439 }
440 return pm;
441}
442
443
444#if QT_CONFIG(xrender)
445static Picture getPatternFill(int screen, const QBrush &b)
446{
447 if (!X11->use_xrender)
448 return XNone;
449
450 XRenderColor color = X11->preMultiply(b.color());
451 XRenderColor bg_color;
452
453 bg_color = X11->preMultiply(QColor(0, 0, 0, 0));
454
455 for (int i = 0; i < X11->pattern_fill_count; ++i) {
456 if (X11->pattern_fills[i].screen == screen
457 && X11->pattern_fills[i].opaque == false
458 && X11->pattern_fills[i].style == b.style()
459 && X11->pattern_fills[i].color.alpha == color.alpha
460 && X11->pattern_fills[i].color.red == color.red
461 && X11->pattern_fills[i].color.green == color.green
462 && X11->pattern_fills[i].color.blue == color.blue
463 && X11->pattern_fills[i].bg_color.alpha == bg_color.alpha
464 && X11->pattern_fills[i].bg_color.red == bg_color.red
465 && X11->pattern_fills[i].bg_color.green == bg_color.green
466 && X11->pattern_fills[i].bg_color.blue == bg_color.blue)
467 return X11->pattern_fills[i].picture;
468 }
469 // none found, replace one
470 int i = QRandomGenerator::global()->generate() % 16;
471
472 if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) {
473 XRenderFreePicture (QXcbX11Info::display(), X11->pattern_fills[i].picture);
474 X11->pattern_fills[i].picture = 0;
475 }
476
477 if (!X11->pattern_fills[i].picture) {
478 Pixmap pixmap = XCreatePixmap (QXcbX11Info::display(), RootWindow (QXcbX11Info::display(), screen), 8, 8, 32);
479 XRenderPictureAttributes attrs;
480 attrs.repeat = True;
481 X11->pattern_fills[i].picture = XRenderCreatePicture (QXcbX11Info::display(), pixmap,
482 XRenderFindStandardFormat(QXcbX11Info::display(), PictStandardARGB32),
483 CPRepeat, &attrs);
484 XFreePixmap (QXcbX11Info::display(), pixmap);
485 }
486
487 X11->pattern_fills[i].screen = screen;
488 X11->pattern_fills[i].color = color;
489 X11->pattern_fills[i].bg_color = bg_color;
490 X11->pattern_fills[i].opaque = false;
491 X11->pattern_fills[i].style = b.style();
492
493 XRenderFillRectangle(QXcbX11Info::display(), PictOpSrc, X11->pattern_fills[i].picture, &bg_color, 0, 0, 8, 8);
494
495 QPixmap pattern(qt_pixmapForBrush(b.style(), true));
496 XRenderPictureAttributes attrs;
497 attrs.repeat = true;
498 XRenderChangePicture(QXcbX11Info::display(), qt_x11PictureHandle(pattern), CPRepeat, &attrs);
499
500 Picture fill_fg = X11->getSolidFill(screen, b.color());
501 XRenderComposite(QXcbX11Info::display(), PictOpOver, fill_fg, qt_x11PictureHandle(pattern),
502 X11->pattern_fills[i].picture,
503 0, 0, 0, 0, 0, 0, 8, 8);
504
505 return X11->pattern_fills[i].picture;
506}
507
508static void qt_render_bitmap(Display *dpy, int scrn, Picture src, Picture dst,
509 int sx, int sy, int x, int y, int sw, int sh,
510 const QPen &pen)
511{
512 Picture fill_fg = X11->getSolidFill(scrn, pen.color());
513 XRenderComposite(dpy, PictOpOver,
514 fill_fg, src, dst, sx, sy, sx, sy, x, y, sw, sh);
515}
516#endif
517
519{
520 dpy = 0;
521 scrn = 0;
522 hd = 0;
523 picture = 0;
524 xinfo = 0;
525#if QT_CONFIG(xrender)
526 current_brush = 0;
527 composition_mode = PictOpOver;
528 tessellator = new QXRenderTessellator;
529#endif
530}
531
533{
534 if (adapted_pen_origin)
535 XSetTSOrigin(dpy, gc, p.x(), p.y());
536 if (adapted_brush_origin)
537 XSetTSOrigin(dpy, gc_brush, p.x(), p.y());
538}
539
541{
542 if (adapted_pen_origin)
543 XSetTSOrigin(dpy, gc, 0, 0);
544 if (adapted_brush_origin)
545 XSetTSOrigin(dpy, gc_brush, 0, 0);
546}
547
548void QX11PaintEnginePrivate::clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
549{
550 int clipped_count = 0;
551 qt_float_point *clipped_points = 0;
552 polygonClipper.clipPolygon((qt_float_point *) poly.data(), poly.size(),
553 &clipped_points, &clipped_count);
554 clipped_poly->resize(clipped_count);
555 for (int i=0; i<clipped_count; ++i)
556 (*clipped_poly)[i] = *((QPointF *)(&clipped_points[i]));
557}
558
560{
561 Q_Q(QX11PaintEngine);
562 QPainter *painter = q->state ? q->state->painter() : nullptr;
563 if (painter && painter->hasClipping()) {
564 if (q->testDirty(QPaintEngine::DirtyTransform))
565 q->updateMatrix(q->state->transform());
566 QPolygonF clip_poly_dev(matrix.map(painter->clipPath().toFillPolygon()));
567 QPolygonF clipped_poly_dev;
568 clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
569 q->updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
570 } else {
571 q->updateClipRegion_dev(QRegion(), Qt::NoClip);
572 }
573}
574
576{
577 QPaintEngine::PaintEngineFeatures features =
578 QPaintEngine::PrimitiveTransform
579 | QPaintEngine::PatternBrush
580 | QPaintEngine::AlphaBlend
581 | QPaintEngine::PainterPaths
582 | QPaintEngine::RasterOpModes;
583
584 if (X11->use_xrender) {
585 features |= QPaintEngine::Antialiasing;
586 features |= QPaintEngine::PorterDuff;
587 features |= QPaintEngine::MaskedBrush;
588#if 0
589 if (X11->xrender_version > 10) {
590 features |= QPaintEngine::LinearGradientFill;
591 // ###
592 }
593#endif
594 }
595
596 return features;
597}
598
599/*
600 * QX11PaintEngine members
601 */
602
603QX11PaintEngine::QX11PaintEngine()
604 : QPaintEngine(*(new QX11PaintEnginePrivate), qt_decide_features())
605{
606 Q_D(QX11PaintEngine);
607 d->init();
608}
609
612{
613 Q_D(QX11PaintEngine);
614 d->init();
615}
616
618{
619#if QT_CONFIG(xrender)
620 Q_D(QX11PaintEngine);
621 delete d->tessellator;
622#endif
623}
624
625bool QX11PaintEngine::begin(QPaintDevice *pdev)
626{
627 Q_D(QX11PaintEngine);
628 d->xinfo = qt_x11Info(pdev);
629#if QT_CONFIG(xrender)
630 if (pdev->devType() == QInternal::Pixmap) {
631 const QPixmap *pm = static_cast<const QPixmap *>(pdev);
632 QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(pm->handle());
633 if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
634 data->convertToARGB32();
635 d->picture = qt_x11PictureHandle(*static_cast<const QPixmap *>(pdev));
636 }
637#else
638 d->picture = 0;
639#endif
640 d->hd = qt_x11Handle(pdev);
641
642 Q_ASSERT(d->xinfo != 0);
643 d->dpy = d->xinfo->display(); // get display variable
644 d->scrn = d->xinfo->screen(); // get screen variable
645
646 d->crgn = QRegion();
647 d->gc = XCreateGC(d->dpy, d->hd, 0, 0);
648 d->gc_brush = XCreateGC(d->dpy, d->hd, 0, 0);
649 d->has_alpha_brush = false;
650 d->has_alpha_pen = false;
651 d->has_clipping = false;
652 d->has_complex_xform = false;
653 d->has_scaling_xform = false;
654 d->has_non_scaling_xform = true;
655 d->xform_scale = 1;
656 d->has_custom_pen = false;
657 d->matrix = QTransform();
658 d->pdev_depth = d->pdev->depth();
659 d->render_hints = 0;
660 d->txop = QTransform::TxNone;
661 d->use_path_fallback = false;
662#if QT_CONFIG(xrender)
663 d->composition_mode = PictOpOver;
664#endif
665 d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
666 d->opacity = 1;
667
668 QX11PlatformPixmap *x11pm = paintDevice()->devType() == QInternal::Pixmap ? qt_x11Pixmap(*static_cast<QPixmap *>(paintDevice())) : nullptr;
669 d->use_sysclip = paintDevice()->devType() == QInternal::Widget || (x11pm ? x11pm->isBackingStore() : false);
670
671 // Set up the polygon clipper. Note: This will only work in
672 // polyline mode as long as we have a buffer zone, since a
673 // polyline may be clipped into several non-connected polylines.
674 const int BUFFERZONE = 1000;
675 QRect devClipRect(-BUFFERZONE, -BUFFERZONE,
676 pdev->width() + 2*BUFFERZONE, pdev->height() + 2*BUFFERZONE);
677 d->polygonClipper.setBoundingRect(devClipRect);
678
679 setSystemClip(systemClip());
680 d->systemStateChanged();
681
682 qt_x11SetDefaultScreen(d->xinfo->screen());
683
684 updatePen(QPen(Qt::black));
685 updateBrush(QBrush(Qt::white), QPoint());
686
687 setDirty(QPaintEngine::DirtyClipRegion);
688 setDirty(QPaintEngine::DirtyPen);
689 setDirty(QPaintEngine::DirtyBrush);
690 setDirty(QPaintEngine::DirtyBackground);
691
692 setActive(true);
693 return true;
694}
695
697{
698 Q_D(QX11PaintEngine);
699
700#if QT_CONFIG(xrender)
701 if (d->picture) {
702 // reset clipping/subwindow mode on our render picture
703 XRenderPictureAttributes attrs;
704 attrs.subwindow_mode = ClipByChildren;
705 attrs.clip_mask = XNone;
706 XRenderChangePicture(d->dpy, d->picture, CPClipMask|CPSubwindowMode, &attrs);
707 }
708#endif
709
710 if (d->gc_brush && d->pdev->painters < 2) {
711 XFreeGC(d->dpy, d->gc_brush);
712 d->gc_brush = 0;
713 }
714
715 if (d->gc && d->pdev->painters < 2) {
716 XFreeGC(d->dpy, d->gc);
717 d->gc = 0;
718 }
719
720 // Restore system clip for alien widgets painting outside the paint event.
721// if (d->pdev->devType() == QInternal::Widget && !static_cast<QWidget *>(d->pdev)->internalWinId())
722 d->currentClipDevice = nullptr;
723 setSystemClip(QRegion());
724
725 setActive(false);
726 return true;
727}
728
729static bool clipLine(QLineF *line, const QRect &rect)
730{
731 qreal x1 = line->x1();
732 qreal x2 = line->x2();
733 qreal y1 = line->y1();
734 qreal y2 = line->y2();
735
736 qreal left = rect.x();
737 qreal right = rect.x() + rect.width() - 1;
738 qreal top = rect.y();
739 qreal bottom = rect.y() + rect.height() - 1;
740
741 enum { Left, Right, Top, Bottom };
742 // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
743 int p1 = ((x1 < left) << Left)
744 | ((x1 > right) << Right)
745 | ((y1 < top) << Top)
746 | ((y1 > bottom) << Bottom);
747 int p2 = ((x2 < left) << Left)
748 | ((x2 > right) << Right)
749 | ((y2 < top) << Top)
750 | ((y2 > bottom) << Bottom);
751
752 if (p1 & p2)
753 // completely outside
754 return false;
755
756 if (p1 | p2) {
757 qreal dx = x2 - x1;
758 qreal dy = y2 - y1;
759
760 // clip x coordinates
761 if (x1 < left) {
762 y1 += dy/dx * (left - x1);
763 x1 = left;
764 } else if (x1 > right) {
765 y1 -= dy/dx * (x1 - right);
766 x1 = right;
767 }
768 if (x2 < left) {
769 y2 += dy/dx * (left - x2);
770 x2 = left;
771 } else if (x2 > right) {
772 y2 -= dy/dx * (x2 - right);
773 x2 = right;
774 }
775 p1 = ((y1 < top) << Top)
776 | ((y1 > bottom) << Bottom);
777 p2 = ((y2 < top) << Top)
778 | ((y2 > bottom) << Bottom);
779 if (p1 & p2)
780 return false;
781 // clip y coordinates
782 if (y1 < top) {
783 x1 += dx/dy * (top - y1);
784 y1 = top;
785 } else if (y1 > bottom) {
786 x1 -= dx/dy * (y1 - bottom);
787 y1 = bottom;
788 }
789 if (y2 < top) {
790 x2 += dx/dy * (top - y2);
791 y2 = top;
792 } else if (y2 > bottom) {
793 x2 -= dx/dy * (y2 - bottom);
794 y2 = bottom;
795 }
796 *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
797 }
798 return true;
799}
800
801void QX11PaintEngine::drawLines(const QLine *lines, int lineCount)
802{
803 Q_ASSERT(lines);
804 Q_ASSERT(lineCount);
805 Q_D(QX11PaintEngine);
806
807 if (d->has_alpha_brush
808 || d->has_alpha_pen
809 || d->has_custom_pen
810 || (d->cpen.widthF() > 0 && d->has_complex_xform
811 && !d->has_non_scaling_xform)
812 || (d->render_hints & QPainter::Antialiasing)) {
813 for (int i = 0; i < lineCount; ++i) {
814 QPainterPath path(lines[i].p1());
815 path.lineTo(lines[i].p2());
816 drawPath(path);
817 }
818 return;
819 }
820
821 if (d->has_pen) {
822 for (int i = 0; i < lineCount; ++i) {
823 QLineF linef;
824 if (d->txop == QTransform::TxNone) {
825 linef = lines[i];
826 } else {
827 linef = d->matrix.map(QLineF(lines[i]));
828 }
829 if (clipLine(&linef, d->polygonClipper.boundingRect())) {
830 int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
831 int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
832 int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
833 int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
834
835 XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
836 }
837 }
838 }
839}
840
841void QX11PaintEngine::drawLines(const QLineF *lines, int lineCount)
842{
843 Q_ASSERT(lines);
844 Q_ASSERT(lineCount);
845 Q_D(QX11PaintEngine);
846
847 if (d->has_alpha_brush
848 || d->has_alpha_pen
849 || d->has_custom_pen
850 || (d->cpen.widthF() > 0 && d->has_complex_xform
851 && !d->has_non_scaling_xform)
852 || (d->render_hints & QPainter::Antialiasing)) {
853 for (int i = 0; i < lineCount; ++i) {
854 QPainterPath path(lines[i].p1());
855 path.lineTo(lines[i].p2());
856 drawPath(path);
857 }
858 return;
859 }
860
861 if (d->has_pen) {
862 for (int i = 0; i < lineCount; ++i) {
863 QLineF linef = d->matrix.map(lines[i]);
864 if (clipLine(&linef, d->polygonClipper.boundingRect())) {
865 int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
866 int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
867 int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
868 int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
869
870 XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
871 }
872 }
873 }
874}
875
876static inline QLine clipStraightLine(const QRect &clip, const QLine &l)
877{
878 if (l.p1().x() == l.p2().x()) {
879 int x = qBound(clip.left(), l.p1().x(), clip.right());
880 int y1 = qBound(clip.top(), l.p1().y(), clip.bottom());
881 int y2 = qBound(clip.top(), l.p2().y(), clip.bottom());
882
883 return QLine(x, y1, x, y2);
884 } else {
885 Q_ASSERT(l.p1().y() == l.p2().y());
886
887 int x1 = qBound(clip.left(), l.p1().x(), clip.right());
888 int x2 = qBound(clip.left(), l.p2().x(), clip.right());
889 int y = qBound(clip.top(), l.p1().y(), clip.bottom());
890
891 return QLine(x1, y, x2, y);
892 }
893}
894
895void QX11PaintEngine::drawRects(const QRectF *rects, int rectCount)
896{
897 Q_D(QX11PaintEngine);
898 Q_ASSERT(rects);
899 Q_ASSERT(rectCount);
900
901 if (rectCount != 1
902 || d->has_pen
903 || d->has_alpha_brush
904 || d->has_complex_xform
905 || d->has_custom_pen
906 || d->cbrush.style() != Qt::SolidPattern
907#if QT_CONFIG(xrender)
908 || complexPictOp(d->composition_mode)
909#endif
910 )
911 {
912 QPaintEngine::drawRects(rects, rectCount);
913 return;
914 }
915
916 QPoint alignedOffset;
917 if (d->txop == QTransform::TxTranslate) {
918 QPointF offset(d->matrix.dx(), d->matrix.dy());
919 alignedOffset = offset.toPoint();
920 if (offset != QPointF(alignedOffset)) {
921 QPaintEngine::drawRects(rects, rectCount);
922 return;
923 }
924 }
925
926 const QRectF& r = rects[0];
927 QRect alignedRect = r.toAlignedRect();
928 if (r != QRectF(alignedRect)) {
929 QPaintEngine::drawRects(rects, rectCount);
930 return;
931 }
932 alignedRect.translate(alignedOffset);
933
934 QRect clip(d->polygonClipper.boundingRect());
935 alignedRect = alignedRect.intersected(clip);
936 if (alignedRect.isEmpty())
937 return;
938
939 // simple-case:
940 // the rectangle is pixel-aligned
941 // the fill brush is just a solid non-alpha color
942 // the painter transform is only integer translation
943 // ignore: antialiasing and just XFillRectangles directly
944 XRectangle xrect;
945 xrect.x = short(alignedRect.x());
946 xrect.y = short(alignedRect.y());
947 xrect.width = ushort(alignedRect.width());
948 xrect.height = ushort(alignedRect.height());
949 XFillRectangles(d->dpy, d->hd, d->gc_brush, &xrect, 1);
950}
951
952void QX11PaintEngine::drawRects(const QRect *rects, int rectCount)
953{
954 Q_D(QX11PaintEngine);
955 Q_ASSERT(rects);
956 Q_ASSERT(rectCount);
957
958 if (d->has_alpha_pen
959 || d->has_complex_xform
960 || d->has_custom_pen
961 || (d->render_hints & QPainter::Antialiasing))
962 {
963 for (int i = 0; i < rectCount; ++i) {
964 QPainterPath path;
965 path.addRect(rects[i]);
966 drawPath(path);
967 }
968 return;
969 }
970
971 QRect clip(d->polygonClipper.boundingRect());
972 QPoint offset(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
973#if QT_CONFIG(xrender)
974 ::Picture pict = d->picture;
975
976 if (X11->use_xrender && pict && d->has_brush && d->pdev_depth != 1
977 && (d->has_texture || d->has_alpha_brush || complexPictOp(d->composition_mode)))
978 {
979 XRenderColor xc;
980 if (!d->has_texture && !d->has_pattern)
981 xc = X11->preMultiply(d->cbrush.color());
982
983 for (int i = 0; i < rectCount; ++i) {
984 QRect r(rects[i]);
985 if (d->txop == QTransform::TxTranslate)
986 r.translate(offset);
987
988 if (r.width() == 0 || r.height() == 0) {
989 if (d->has_pen) {
990 const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
991 XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
992 }
993 continue;
994 }
995
996 r = r.intersected(clip);
997 if (r.isEmpty())
998 continue;
999 if (d->has_texture || d->has_pattern) {
1000 XRenderComposite(d->dpy, d->composition_mode, d->current_brush, 0, pict,
1001 qRound(r.x() - d->bg_origin.x()), qRound(r.y() - d->bg_origin.y()),
1002 0, 0, r.x(), r.y(), r.width(), r.height());
1003 } else {
1004 XRenderFillRectangle(d->dpy, d->composition_mode, pict, &xc, r.x(), r.y(), r.width(), r.height());
1005 }
1006 if (d->has_pen)
1007 XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
1008 }
1009 } else
1010#endif // QT_CONFIG(xrender)
1011 {
1012 if (d->has_brush && d->has_pen) {
1013 for (int i = 0; i < rectCount; ++i) {
1014 QRect r(rects[i]);
1015 if (d->txop == QTransform::TxTranslate)
1016 r.translate(offset);
1017
1018 if (r.width() == 0 || r.height() == 0) {
1019 const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
1020 XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
1021 continue;
1022 }
1023
1024 r = r.intersected(clip);
1025 if (r.isEmpty())
1026 continue;
1027 d->setupAdaptedOrigin(r.topLeft());
1028 XFillRectangle(d->dpy, d->hd, d->gc_brush, r.x(), r.y(), r.width(), r.height());
1029 XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
1030 }
1031 d->resetAdaptedOrigin();
1032 } else {
1033 QVarLengthArray<XRectangle> xrects(rectCount);
1034 int numClipped = rectCount;
1035 for (int i = 0; i < rectCount; ++i) {
1036 QRect r(rects[i]);
1037 if (d->txop == QTransform::TxTranslate)
1038 r.translate(offset);
1039
1040 if (r.width() == 0 || r.height() == 0) {
1041 --numClipped;
1042 if (d->has_pen) {
1043 const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
1044 XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
1045 }
1046 continue;
1047 }
1048
1049 r = r.intersected(clip);
1050 if (r.isEmpty()) {
1051 --numClipped;
1052 continue;
1053 }
1054 xrects[i].x = short(r.x());
1055 xrects[i].y = short(r.y());
1056 xrects[i].width = ushort(r.width());
1057 xrects[i].height = ushort(r.height());
1058 }
1059 if (numClipped) {
1060 d->setupAdaptedOrigin(rects[0].topLeft());
1061 if (d->has_brush)
1062 XFillRectangles(d->dpy, d->hd, d->gc_brush, xrects.data(), numClipped);
1063 else if (d->has_pen)
1064 XDrawRectangles(d->dpy, d->hd, d->gc, xrects.data(), numClipped);
1065 d->resetAdaptedOrigin();
1066 }
1067 }
1068 }
1069}
1070
1071static inline void setCapStyle(int cap_style, GC gc)
1072{
1073 ulong mask = GCCapStyle;
1074 XGCValues vals;
1075 vals.cap_style = cap_style;
1076 XChangeGC(QXcbX11Info::display(), gc, mask, &vals);
1077}
1078
1079void QX11PaintEngine::drawPoints(const QPoint *points, int pointCount)
1080{
1081 Q_ASSERT(points);
1082 Q_ASSERT(pointCount);
1083 Q_D(QX11PaintEngine);
1084
1085 if (!d->has_pen)
1086 return;
1087
1088 // use the same test here as in drawPath to ensure that we don't use the path fallback
1089 // and end up in XDrawLines for pens with width <= 1
1090 if (d->cpen.widthF() > 1.0f
1091 || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
1092 || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
1093 {
1094 Qt::PenCapStyle capStyle = d->cpen.capStyle();
1095 if (capStyle == Qt::FlatCap) {
1096 setCapStyle(CapProjecting, d->gc);
1097 d->cpen.setCapStyle(Qt::SquareCap);
1098 }
1099 const QPoint *end = points + pointCount;
1100 while (points < end) {
1101 QPainterPath path;
1102 path.moveTo(*points);
1103 path.lineTo(points->x()+.005, points->y());
1104 drawPath(path);
1105 ++points;
1106 }
1107
1108 if (capStyle == Qt::FlatCap) {
1109 setCapStyle(CapButt, d->gc);
1110 d->cpen.setCapStyle(capStyle);
1111 }
1112 return;
1113 }
1114
1115 static const int BUF_SIZE = 1024;
1116 XPoint xPoints[BUF_SIZE];
1117 int i = 0, j = 0;
1118 while (i < pointCount) {
1119 while (i < pointCount && j < BUF_SIZE) {
1120 const QPoint &xformed = d->matrix.map(points[i]);
1121 int x = xformed.x();
1122 int y = xformed.y();
1123 if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
1124 xPoints[j].x = x;
1125 xPoints[j].y = y;
1126 ++j;
1127 }
1128 ++i;
1129 }
1130 if (j)
1131 XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
1132
1133 j = 0;
1134 }
1135}
1136
1137void QX11PaintEngine::drawPoints(const QPointF *points, int pointCount)
1138{
1139 Q_ASSERT(points);
1140 Q_ASSERT(pointCount);
1141 Q_D(QX11PaintEngine);
1142
1143 if (!d->has_pen)
1144 return;
1145
1146 // use the same test here as in drawPath to ensure that we don't use the path fallback
1147 // and end up in XDrawLines for pens with width <= 1
1148 if (d->cpen.widthF() > 1.0f
1149 || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
1150 || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
1151 {
1152 Qt::PenCapStyle capStyle = d->cpen.capStyle();
1153 if (capStyle == Qt::FlatCap) {
1154 setCapStyle(CapProjecting, d->gc);
1155 d->cpen.setCapStyle(Qt::SquareCap);
1156 }
1157
1158 const QPointF *end = points + pointCount;
1159 while (points < end) {
1160 QPainterPath path;
1161 path.moveTo(*points);
1162 path.lineTo(points->x() + 0.005, points->y());
1163 drawPath(path);
1164 ++points;
1165 }
1166 if (capStyle == Qt::FlatCap) {
1167 setCapStyle(CapButt, d->gc);
1168 d->cpen.setCapStyle(capStyle);
1169 }
1170 return;
1171 }
1172
1173 static const int BUF_SIZE = 1024;
1174 XPoint xPoints[BUF_SIZE];
1175 int i = 0, j = 0;
1176 while (i < pointCount) {
1177 while (i < pointCount && j < BUF_SIZE) {
1178 const QPointF &xformed = d->matrix.map(points[i]);
1179 int x = qFloor(xformed.x());
1180 int y = qFloor(xformed.y());
1181
1182 if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
1183 xPoints[j].x = x;
1184 xPoints[j].y = y;
1185 ++j;
1186 }
1187 ++i;
1188 }
1189 if (j)
1190 XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
1191
1192 j = 0;
1193 }
1194}
1195
1197{
1198#if QT_CONFIG(xrender)
1199 if (X11->use_xrender)
1200 return QPainter::Antialiasing;
1201#endif
1202 return QFlag(0);
1203}
1204
1205void QX11PaintEngine::updateState(const QPaintEngineState &state)
1206{
1207 Q_D(QX11PaintEngine);
1208 QPaintEngine::DirtyFlags flags = state.state();
1209
1210
1211 if (flags & DirtyOpacity) {
1212 d->opacity = state.opacity();
1213 // Force update pen/brush as to get proper alpha colors propagated
1214 flags |= DirtyPen;
1215 flags |= DirtyBrush;
1216 }
1217
1218 if (flags & DirtyTransform) updateMatrix(state.transform());
1219 if (flags & DirtyPen) updatePen(state.pen());
1220 if (flags & (DirtyBrush | DirtyBrushOrigin)) updateBrush(state.brush(), state.brushOrigin());
1221 if (flags & DirtyFont) updateFont(state.font());
1222
1223 if (state.state() & DirtyClipEnabled) {
1224 if (state.isClipEnabled()) {
1225 QPolygonF clip_poly_dev(d->matrix.map(painter()->clipPath().toFillPolygon()));
1226 QPolygonF clipped_poly_dev;
1227 d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
1228 updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
1229 } else {
1230 updateClipRegion_dev(QRegion(), Qt::NoClip);
1231 }
1232 }
1233
1234 if (flags & DirtyClipPath) {
1235 QPolygonF clip_poly_dev(d->matrix.map(state.clipPath().toFillPolygon()));
1236 QPolygonF clipped_poly_dev;
1237 d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
1238 updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon(), state.clipPath().fillRule()),
1239 state.clipOperation());
1240 } else if (flags & DirtyClipRegion) {
1241 extern QPainterPath qt_regionToPath(const QRegion &region);
1242 QPainterPath clip_path = qt_regionToPath(state.clipRegion());
1243 QPolygonF clip_poly_dev(d->matrix.map(clip_path.toFillPolygon()));
1244 QPolygonF clipped_poly_dev;
1245 d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
1246 updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), state.clipOperation());
1247 }
1248 if (flags & DirtyHints) updateRenderHints(state.renderHints());
1249 if (flags & DirtyCompositionMode) {
1250 int function = GXcopy;
1251 if (state.compositionMode() >= QPainter::RasterOp_SourceOrDestination) {
1252 switch (state.compositionMode()) {
1253 case QPainter::RasterOp_SourceOrDestination:
1254 function = GXor;
1255 break;
1256 case QPainter::RasterOp_SourceAndDestination:
1257 function = GXand;
1258 break;
1259 case QPainter::RasterOp_SourceXorDestination:
1260 function = GXxor;
1261 break;
1262 case QPainter::RasterOp_NotSourceAndNotDestination:
1263 function = GXnor;
1264 break;
1265 case QPainter::RasterOp_NotSourceOrNotDestination:
1266 function = GXnand;
1267 break;
1268 case QPainter::RasterOp_NotSourceXorDestination:
1269 function = GXequiv;
1270 break;
1271 case QPainter::RasterOp_NotSource:
1272 function = GXcopyInverted;
1273 break;
1274 case QPainter::RasterOp_SourceAndNotDestination:
1275 function = GXandReverse;
1276 break;
1277 case QPainter::RasterOp_NotSourceAndDestination:
1278 function = GXandInverted;
1279 break;
1280 default:
1281 function = GXcopy;
1282 }
1283 }
1284#if QT_CONFIG(xrender)
1285 else {
1286 d->composition_mode =
1287 qpainterOpToXrender(state.compositionMode());
1288 }
1289#endif
1290 XSetFunction(X11->display, d->gc, function);
1291 XSetFunction(X11->display, d->gc_brush, function);
1292 }
1293 d->decidePathFallback();
1294 d->decideCoordAdjust();
1295}
1296
1297void QX11PaintEngine::updateRenderHints(QPainter::RenderHints hints)
1298{
1299 Q_D(QX11PaintEngine);
1300 d->render_hints = hints;
1301
1302#if QT_CONFIG(xrender)
1303 if (X11->use_xrender && d->picture) {
1304 XRenderPictureAttributes attrs;
1305 attrs.poly_edge = (hints & QPainter::Antialiasing) ? PolyEdgeSmooth : PolyEdgeSharp;
1306 XRenderChangePicture(d->dpy, d->picture, CPPolyEdge, &attrs);
1307 }
1308#endif
1309}
1310
1311void QX11PaintEngine::updatePen(const QPen &pen)
1312{
1313 Q_D(QX11PaintEngine);
1314 d->cpen = pen;
1315 int cp = CapButt;
1316 int jn = JoinMiter;
1317 int ps = pen.style();
1318
1319 if (d->opacity < 1.0) {
1320 QColor c = d->cpen.color();
1321 c.setAlpha(qRound(c.alpha()*d->opacity));
1322 d->cpen.setColor(c);
1323 }
1324
1325 d->has_pen = (ps != Qt::NoPen);
1326 d->has_alpha_pen = (pen.color().alpha() != 255);
1327
1328 switch (pen.capStyle()) {
1329 case Qt::SquareCap:
1330 cp = CapProjecting;
1331 break;
1332 case Qt::RoundCap:
1333 cp = CapRound;
1334 break;
1335 case Qt::FlatCap:
1336 default:
1337 cp = CapButt;
1338 break;
1339 }
1340 switch (pen.joinStyle()) {
1341 case Qt::BevelJoin:
1342 jn = JoinBevel;
1343 break;
1344 case Qt::RoundJoin:
1345 jn = JoinRound;
1346 break;
1347 case Qt::MiterJoin:
1348 default:
1349 jn = JoinMiter;
1350 break;
1351 }
1352
1353 d->adapted_pen_origin = false;
1354
1355 char dashes[10]; // custom pen dashes
1356 int dash_len = 0; // length of dash list
1357 int xStyle = LineSolid;
1358
1359 /*
1360 We are emulating Windows here. Windows treats cpen.width() == 1
1361 (or 0) as a very special case. The fudge variable unifies this
1362 case with the general case.
1363 */
1364 qreal pen_width = pen.widthF();
1365 int scale = qRound(pen_width < 1 ? 1 : pen_width);
1366 int space = (pen_width < 1 && pen_width > 0 ? 1 : (2 * scale));
1367 int dot = 1 * scale;
1368 int dash = 4 * scale;
1369
1370 d->has_custom_pen = false;
1371
1372 switch (ps) {
1373 case Qt::NoPen:
1374 case Qt::SolidLine:
1375 xStyle = LineSolid;
1376 break;
1377 case Qt::DashLine:
1378 dashes[0] = dash;
1379 dashes[1] = space;
1380 dash_len = 2;
1381 xStyle = LineOnOffDash;
1382 break;
1383 case Qt::DotLine:
1384 dashes[0] = dot;
1385 dashes[1] = space;
1386 dash_len = 2;
1387 xStyle = LineOnOffDash;
1388 break;
1389 case Qt::DashDotLine:
1390 dashes[0] = dash;
1391 dashes[1] = space;
1392 dashes[2] = dot;
1393 dashes[3] = space;
1394 dash_len = 4;
1395 xStyle = LineOnOffDash;
1396 break;
1397 case Qt::DashDotDotLine:
1398 dashes[0] = dash;
1399 dashes[1] = space;
1400 dashes[2] = dot;
1401 dashes[3] = space;
1402 dashes[4] = dot;
1403 dashes[5] = space;
1404 dash_len = 6;
1405 xStyle = LineOnOffDash;
1406 break;
1407 case Qt::CustomDashLine:
1408 d->has_custom_pen = true;
1409 break;
1410 }
1411
1412 ulong mask = GCForeground | GCBackground | GCGraphicsExposures | GCLineWidth
1413 | GCCapStyle | GCJoinStyle | GCLineStyle;
1414 XGCValues vals;
1415 vals.graphics_exposures = false;
1416 if (d->pdev_depth == 1) {
1417 vals.foreground = qGray(pen.color().rgb()) > 127 ? 0 : 1;
1418 vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
1419 } else if (d->pdev->devType() == QInternal::Pixmap && d->pdev_depth == 32
1420 && X11->use_xrender) {
1421 vals.foreground = pen.color().rgba();
1422 vals.background = QColor(Qt::transparent).rgba();
1423 } else {
1424 QXcbColormap cmap = QXcbColormap::instance(d->scrn);
1425 vals.foreground = cmap.pixel(pen.color());
1426 vals.background = cmap.pixel(QColor(Qt::transparent));
1427 }
1428
1429
1430 vals.line_width = qRound(pen.widthF());
1431 vals.cap_style = cp;
1432 vals.join_style = jn;
1433 vals.line_style = xStyle;
1434
1435 XChangeGC(d->dpy, d->gc, mask, &vals);
1436
1437 if (dash_len) { // make dash list
1438 XSetDashes(d->dpy, d->gc, 0, dashes, dash_len);
1439 }
1440
1441 if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
1442 QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
1443 if (!sysClip.isEmpty())
1444 x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
1445 else
1446 x11ClearClipRegion(d->dpy, d->gc, 0, d->picture);
1447 }
1448}
1449
1450void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
1451{
1452 Q_D(QX11PaintEngine);
1453 d->cbrush = brush;
1454 d->bg_origin = origin;
1455 d->adapted_brush_origin = false;
1456#if QT_CONFIG(xrender)
1457 d->current_brush = 0;
1458#endif
1459 if (d->opacity < 1.0) {
1460 QColor c = d->cbrush.color();
1461 c.setAlpha(qRound(c.alpha()*d->opacity));
1462 d->cbrush.setColor(c);
1463 }
1464
1465 int s = FillSolid;
1466 int bs = d->cbrush.style();
1467 d->has_brush = (bs != Qt::NoBrush);
1468 d->has_pattern = bs >= Qt::Dense1Pattern && bs <= Qt::DiagCrossPattern;
1469 d->has_texture = bs == Qt::TexturePattern;
1470 d->has_alpha_brush = brush.color().alpha() != 255;
1471 d->has_alpha_texture = d->has_texture && d->cbrush.texture().hasAlphaChannel();
1472
1473 ulong mask = GCForeground | GCBackground | GCGraphicsExposures
1474 | GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle;
1475 XGCValues vals;
1476 vals.graphics_exposures = false;
1477 if (d->pdev_depth == 1) {
1478 vals.foreground = qGray(d->cbrush.color().rgb()) > 127 ? 0 : 1;
1479 vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
1480 } else if (X11->use_xrender && d->pdev->devType() == QInternal::Pixmap
1481 && d->pdev_depth == 32) {
1482 vals.foreground = d->cbrush.color().rgba();
1483 vals.background = QColor(Qt::transparent).rgba();
1484 } else {
1485 QXcbColormap cmap = QXcbColormap::instance(d->scrn);
1486 vals.foreground = cmap.pixel(d->cbrush.color());
1487 vals.background = cmap.pixel(QColor(Qt::transparent));
1488
1489 if (!X11->use_xrender && d->has_brush && !d->has_pattern && !brush.isOpaque()) {
1490 QPixmap pattern = qt_patternForAlpha(brush.color().alpha(), d->scrn);
1491 mask |= GCStipple;
1492 vals.stipple = qt_x11PixmapHandle(pattern);
1493 s = FillStippled;
1494 d->adapted_brush_origin = true;
1495 }
1496 }
1497 vals.cap_style = CapButt;
1498 vals.join_style = JoinMiter;
1499 vals.line_style = LineSolid;
1500
1501 if (d->has_pattern || d->has_texture) {
1502 if (bs == Qt::TexturePattern) {
1503 d->brush_pm = qt_toX11Pixmap(d->cbrush.texture());
1504#if QT_CONFIG(xrender)
1505 if (X11->use_xrender) {
1506 XRenderPictureAttributes attrs;
1507 attrs.repeat = true;
1508 XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->brush_pm), CPRepeat, &attrs);
1509 QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
1510 if (data->mask_picture)
1511 XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
1512 }
1513#endif
1514 } else {
1515 d->brush_pm = qt_toX11Pixmap(qt_pixmapForBrush(bs, true));
1516 }
1517 qt_x11SetScreen(d->brush_pm, d->scrn);
1518 if (d->brush_pm.depth() == 1) {
1519 mask |= GCStipple;
1520 vals.stipple = qt_x11PixmapHandle(d->brush_pm);
1521 s = FillStippled;
1522#if QT_CONFIG(xrender)
1523 if (X11->use_xrender) {
1524 d->bitmap_texture = QPixmap(d->brush_pm.size());
1525 d->bitmap_texture.fill(Qt::transparent);
1526 d->bitmap_texture = qt_toX11Pixmap(d->bitmap_texture);
1527 qt_x11SetScreen(d->bitmap_texture, d->scrn);
1528
1529 ::Picture src = X11->getSolidFill(d->scrn, d->cbrush.color());
1530 XRenderComposite(d->dpy, PictOpSrc, src, qt_x11PictureHandle(d->brush_pm),
1531 qt_x11PictureHandle(d->bitmap_texture),
1532 0, 0, d->brush_pm.width(), d->brush_pm.height(),
1533 0, 0, d->brush_pm.width(), d->brush_pm.height());
1534
1535 XRenderPictureAttributes attrs;
1536 attrs.repeat = true;
1537 XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->bitmap_texture), CPRepeat, &attrs);
1538
1539 d->current_brush = qt_x11PictureHandle(d->bitmap_texture);
1540 }
1541#endif
1542 } else {
1543 mask |= GCTile;
1544#if QT_CONFIG(xrender)
1545 if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
1546 d->brush_pm.detach();
1547 QX11PlatformPixmap *brushData = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
1548 brushData->convertToARGB32();
1549 }
1550#endif
1551 vals.tile = (d->brush_pm.depth() == d->pdev_depth
1552 ? qt_x11PixmapHandle(d->brush_pm)
1553 : static_cast<QX11PlatformPixmap*>(d->brush_pm.handle())->x11ConvertToDefaultDepth());
1554 s = FillTiled;
1555#if QT_CONFIG(xrender)
1556 d->current_brush = qt_x11PictureHandle(d->cbrush.texture());
1557#endif
1558 }
1559
1560 mask |= GCTileStipXOrigin | GCTileStipYOrigin;
1561 vals.ts_x_origin = qRound(origin.x());
1562 vals.ts_y_origin = qRound(origin.y());
1563 }
1564#if QT_CONFIG(xrender)
1565 else if (d->has_alpha_brush) {
1566 d->current_brush = X11->getSolidFill(d->scrn, d->cbrush.color());
1567 }
1568#endif
1569
1570 vals.fill_style = s;
1571 XChangeGC(d->dpy, d->gc_brush, mask, &vals);
1572 if (!d->has_clipping) {
1573 QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
1574 if (!sysClip.isEmpty())
1575 x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
1576 else
1577 x11ClearClipRegion(d->dpy, d->gc_brush, 0, d->picture);
1578 }
1579}
1580
1581void QX11PaintEngine::drawEllipse(const QRectF &rect)
1582{
1583 QRect aligned = rect.toAlignedRect();
1584 if (aligned == rect)
1585 drawEllipse(aligned);
1586 else
1587 QPaintEngine::drawEllipse(rect);
1588}
1589
1590void QX11PaintEngine::drawEllipse(const QRect &rect)
1591{
1592 if (rect.isEmpty()) {
1593 drawRects(&rect, 1);
1594 return;
1595 }
1596
1597 Q_D(QX11PaintEngine);
1598 QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1);
1599 QRect r(rect);
1600 if (d->txop < QTransform::TxRotate) {
1601 r = d->matrix.mapRect(rect);
1602 } else if (d->txop == QTransform::TxRotate && rect.width() == rect.height()) {
1603 QPainterPath path;
1604 path.addEllipse(rect);
1605 r = d->matrix.map(path).boundingRect().toRect();
1606 }
1607
1608 if (d->has_alpha_brush || d->has_alpha_pen || d->has_custom_pen || (d->render_hints & QPainter::Antialiasing)
1609 || d->has_alpha_texture || devclip.intersected(r) != r
1610 || (d->has_complex_xform
1611 && !(d->has_non_scaling_xform && rect.width() == rect.height())))
1612 {
1613 QPainterPath path;
1614 path.addEllipse(rect);
1615 drawPath(path);
1616 return;
1617 }
1618
1619 int x = r.x();
1620 int y = r.y();
1621 int w = r.width();
1622 int h = r.height();
1623 if (w < 1 || h < 1)
1624 return;
1625 if (w == 1 && h == 1) {
1626 XDrawPoint(d->dpy, d->hd, d->has_pen ? d->gc : d->gc_brush, x, y);
1627 return;
1628 }
1629 d->setupAdaptedOrigin(rect.topLeft());
1630 if (d->has_brush) { // draw filled ellipse
1631 XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64);
1632 if (!d->has_pen) // make smoother outline
1633 XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
1634 }
1635 if (d->has_pen) // draw outline
1636 XDrawArc(d->dpy, d->hd, d->gc, x, y, w, h, 0, 360*64);
1637 d->resetAdaptedOrigin();
1638}
1639
1640
1641
1642void QX11PaintEnginePrivate::fillPolygon_translated(const QPointF *polygonPoints, int pointCount,
1643 QX11PaintEnginePrivate::GCMode gcMode,
1644 QPaintEngine::PolygonDrawMode mode)
1645{
1646
1647 QVarLengthArray<QPointF> translated_points(pointCount);
1648 QPointF offset(matrix.dx(), matrix.dy());
1649
1650 qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
1651 if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing))
1652 offset += QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
1653
1654 for (int i = 0; i < pointCount; ++i) {
1655 translated_points[i] = polygonPoints[i] + offset;
1656
1657 translated_points[i].rx() = qRound(translated_points[i].x()) + offs;
1658 translated_points[i].ry() = qRound(translated_points[i].y()) + offs;
1659 }
1660
1661 fillPolygon_dev(translated_points.data(), pointCount, gcMode, mode);
1662}
1663
1664#if QT_CONFIG(xrender)
1665static void qt_XRenderCompositeTrapezoids(Display *dpy,
1666 int op,
1667 Picture src,
1668 Picture dst,
1669 _Xconst XRenderPictFormat *maskFormat,
1670 int xSrc,
1671 int ySrc,
1672 const XTrapezoid *traps, int size)
1673{
1674 const int MAX_TRAPS = 50000;
1675 while (size) {
1676 int to_draw = size;
1677 if (to_draw > MAX_TRAPS)
1678 to_draw = MAX_TRAPS;
1679 XRenderCompositeTrapezoids(dpy, op, src, dst,
1680 maskFormat,
1681 xSrc, ySrc,
1682 traps, to_draw);
1683 size -= to_draw;
1684 traps += to_draw;
1685 }
1686}
1687#endif
1688
1689void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
1690 QX11PaintEnginePrivate::GCMode gcMode,
1691 QPaintEngine::PolygonDrawMode mode)
1692{
1693 Q_Q(QX11PaintEngine);
1694
1695 int clippedCount = 0;
1696 qt_float_point *clippedPoints = 0;
1697
1698#if QT_CONFIG(xrender)
1699 //can change if we switch to pen if gcMode != BrushGC
1700 bool has_fill_texture = has_texture;
1701 bool has_fill_pattern = has_pattern;
1702 ::Picture src;
1703#endif
1704 QBrush fill;
1705 GC fill_gc;
1706 if (gcMode == BrushGC) {
1707 fill = cbrush;
1708 fill_gc = gc_brush;
1709#if QT_CONFIG(xrender)
1710 if (current_brush)
1711 src = current_brush;
1712 else
1713 src = X11->getSolidFill(scrn, fill.color());
1714#endif
1715 } else {
1716 fill = QBrush(cpen.brush());
1717 fill_gc = gc;
1718#if QT_CONFIG(xrender)
1719 //we use the pens brush
1720 has_fill_texture = (fill.style() == Qt::TexturePattern);
1721 has_fill_pattern = (fill.style() >= Qt::Dense1Pattern && fill.style() <= Qt::DiagCrossPattern);
1722 if (has_fill_texture)
1723 src = qt_x11PictureHandle(fill.texture());
1724 else if (has_fill_pattern)
1725 src = getPatternFill(scrn, fill);
1726 else
1727 src = X11->getSolidFill(scrn, fill.color());
1728#endif
1729 }
1730
1731 polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
1732 &clippedPoints, &clippedCount);
1733
1734#if QT_CONFIG(xrender)
1735 bool solid_fill = fill.color().alpha() == 255;
1736 if (has_fill_texture && fill.texture().depth() == 1 && solid_fill) {
1737 has_fill_texture = false;
1738 has_fill_pattern = true;
1739 }
1740
1741 bool antialias = render_hints & QPainter::Antialiasing;
1742
1743 if (X11->use_xrender
1744 && picture
1745 && !has_fill_pattern
1746 && (clippedCount > 0)
1747 && (fill.style() != Qt::NoBrush)
1748 && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush))
1749 {
1750 tessellator->tessellate((QPointF *)clippedPoints, clippedCount,
1751 mode == QPaintEngine::WindingMode);
1752 if (tessellator->size > 0) {
1753 XRenderPictureAttributes attrs;
1754 attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp;
1755 XRenderChangePicture(dpy, picture, CPPolyEdge, &attrs);
1756 int x_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.x) - bg_origin.x());
1757 int y_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.y) - bg_origin.y());
1758 qt_XRenderCompositeTrapezoids(dpy, composition_mode, src, picture,
1759 antialias
1760 ? XRenderFindStandardFormat(dpy, PictStandardA8)
1761 : XRenderFindStandardFormat(dpy, PictStandardA1),
1762 x_offset, y_offset,
1763 tessellator->traps, tessellator->size);
1764 tessellator->done();
1765 }
1766 } else
1767#endif
1768 if (fill.style() != Qt::NoBrush) {
1769 if (clippedCount > 200000) {
1770 QPolygon poly;
1771 for (int i = 0; i < clippedCount; ++i)
1772 poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y));
1773
1774 const QRect bounds = poly.boundingRect();
1775 const QRect aligned = bounds
1776 & QRect(QPoint(), QSize(pdev->width(), pdev->height()));
1777
1778 QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied);
1779 img.fill(0);
1780
1781 QPainter painter(&img);
1782 painter.translate(-aligned.x(), -aligned.y());
1783 painter.setPen(Qt::NoPen);
1784 painter.setBrush(fill);
1785 if (gcMode == BrushGC)
1786 painter.setBrushOrigin(q->painter()->brushOriginF());
1787 painter.drawPolygon(poly);
1788 painter.end();
1789
1790 q->drawImage(aligned, img, img.rect(), Qt::AutoColor);
1791 } else if (clippedCount > 0) {
1792 QVarLengthArray<XPoint> xpoints(clippedCount);
1793 for (int i = 0; i < clippedCount; ++i) {
1794 xpoints[i].x = qFloor(clippedPoints[i].x);
1795 xpoints[i].y = qFloor(clippedPoints[i].y);
1796 }
1797 if (mode == QPaintEngine::WindingMode)
1798 XSetFillRule(dpy, fill_gc, WindingRule);
1799 setupAdaptedOrigin(QPoint(xpoints[0].x, xpoints[0].y));
1800 XFillPolygon(dpy, hd, fill_gc,
1801 xpoints.data(), clippedCount,
1802 mode == QPaintEngine::ConvexMode ? Convex : Complex, CoordModeOrigin);
1804 if (mode == QPaintEngine::WindingMode)
1805 XSetFillRule(dpy, fill_gc, EvenOddRule);
1806 }
1807 }
1808}
1809
1810void QX11PaintEnginePrivate::strokePolygon_translated(const QPointF *polygonPoints, int pointCount, bool close)
1811{
1812 QVarLengthArray<QPointF> translated_points(pointCount);
1813 QPointF offset(matrix.dx(), matrix.dy());
1814 for (int i = 0; i < pointCount; ++i)
1815 translated_points[i] = polygonPoints[i] + offset;
1816 strokePolygon_dev(translated_points.data(), pointCount, close);
1817}
1818
1819void QX11PaintEnginePrivate::strokePolygon_dev(const QPointF *polygonPoints, int pointCount, bool close)
1820{
1821 int clippedCount = 0;
1822 qt_float_point *clippedPoints = 0;
1823 polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
1824 &clippedPoints, &clippedCount, close);
1825
1826 if (clippedCount > 0) {
1827 QVarLengthArray<XPoint> xpoints(clippedCount);
1828 for (int i = 0; i < clippedCount; ++i) {
1829 xpoints[i].x = qRound(clippedPoints[i].x + aliasedCoordinateDelta);
1830 xpoints[i].y = qRound(clippedPoints[i].y + aliasedCoordinateDelta);
1831 }
1832 uint numberPoints = qMin(clippedCount, xlibMaxLinePoints);
1833 XPoint *pts = xpoints.data();
1834 XDrawLines(dpy, hd, gc, pts, numberPoints, CoordModeOrigin);
1835 pts += numberPoints;
1836 clippedCount -= numberPoints;
1837 numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
1838 while (clippedCount) {
1839 XDrawLines(dpy, hd, gc, pts-1, numberPoints+1, CoordModeOrigin);
1840 pts += numberPoints;
1841 clippedCount -= numberPoints;
1842 numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
1843 }
1844 }
1845}
1846
1847void QX11PaintEngine::drawPolygon(const QPointF *polygonPoints, int pointCount, PolygonDrawMode mode)
1848{
1849 Q_D(QX11PaintEngine);
1850
1851 if (d->use_path_fallback) {
1852 QPainterPath path(polygonPoints[0]);
1853 for (int i = 1; i < pointCount; ++i)
1854 path.lineTo(polygonPoints[i]);
1855 if (mode == PolylineMode) {
1856 QBrush oldBrush = d->cbrush;
1857 d->cbrush = QBrush(Qt::NoBrush);
1858 path.setFillRule(Qt::WindingFill);
1859 drawPath(path);
1860 d->cbrush = oldBrush;
1861 } else {
1862 path.setFillRule(mode == OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill);
1863 path.closeSubpath();
1864 drawPath(path);
1865 }
1866 return;
1867 }
1868 if (mode != PolylineMode && d->has_brush)
1869 d->fillPolygon_translated(polygonPoints, pointCount, QX11PaintEnginePrivate::BrushGC, mode);
1870
1871 if (d->has_pen)
1872 d->strokePolygon_translated(polygonPoints, pointCount, mode != PolylineMode);
1873}
1874
1875
1876void QX11PaintEnginePrivate::fillPath(const QPainterPath &path, QX11PaintEnginePrivate::GCMode gc_mode, bool transform)
1877{
1878 qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
1879
1880 QPainterPath clippedPath;
1881 QPainterPath clipPath;
1882 clipPath.addRect(polygonClipper.boundingRect());
1883
1884 if (transform)
1885 clippedPath = (path*matrix).intersected(clipPath);
1886 else
1887 clippedPath = path.intersected(clipPath);
1888
1889 QList<QPolygonF> polys = clippedPath.toFillPolygons();
1890 for (int i = 0; i < polys.size(); ++i) {
1891 QVarLengthArray<QPointF> translated_points(polys.at(i).size());
1892
1893 for (int j = 0; j < polys.at(i).size(); ++j) {
1894 translated_points[j] = polys.at(i).at(j);
1895 if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing)) {
1896 translated_points[j].rx() = qRound(translated_points[j].rx() + aliasedCoordinateDelta) + offs;
1897 translated_points[j].ry() = qRound(translated_points[j].ry() + aliasedCoordinateDelta) + offs;
1898 }
1899 }
1900
1901 fillPolygon_dev(translated_points.data(), polys.at(i).size(), gc_mode,
1902 path.fillRule() == Qt::OddEvenFill ? QPaintEngine::OddEvenMode : QPaintEngine::WindingMode);
1903 }
1904}
1905
1906void QX11PaintEngine::drawPath(const QPainterPath &path)
1907{
1908 Q_D(QX11PaintEngine);
1909 if (path.isEmpty())
1910 return;
1911
1912 if (d->has_brush)
1913 d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true);
1914 if (d->has_pen
1915 && ((X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
1916 || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate
1917 && !d->has_non_scaling_xform)
1918 || (d->cpen.style() == Qt::CustomDashLine))) {
1919 QPainterPathStroker stroker;
1920 if (d->cpen.style() == Qt::CustomDashLine) {
1921 stroker.setDashPattern(d->cpen.dashPattern());
1922 stroker.setDashOffset(d->cpen.dashOffset());
1923 } else {
1924 stroker.setDashPattern(d->cpen.style());
1925 }
1926 stroker.setCapStyle(d->cpen.capStyle());
1927 stroker.setJoinStyle(d->cpen.joinStyle());
1928 QPainterPath stroke;
1929 qreal width = d->cpen.widthF();
1930 QPolygonF poly;
1931 QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height());
1932 // necessary to get aliased alphablended primitives to be drawn correctly
1933 if (d->isCosmeticPen() || d->has_scaling_xform) {
1934 if (d->isCosmeticPen())
1935 stroker.setWidth(width == 0 ? 1 : width);
1936 else
1937 stroker.setWidth(width * d->xform_scale);
1938 stroker.d_ptr->stroker.setClipRect(deviceRect);
1939 stroke = stroker.createStroke(path * d->matrix);
1940 if (stroke.isEmpty())
1941 return;
1942 stroke.setFillRule(Qt::WindingFill);
1943 d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false);
1944 } else {
1945 stroker.setWidth(width);
1946 stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect));
1947 stroke = stroker.createStroke(path);
1948 if (stroke.isEmpty())
1949 return;
1950 stroke.setFillRule(Qt::WindingFill);
1951 d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, true);
1952 }
1953 } else if (d->has_pen) {
1954 // if we have a cosmetic pen - use XDrawLine() for speed
1955 QList<QPolygonF> polys = path.toSubpathPolygons(d->matrix);
1956 for (int i = 0; i < polys.size(); ++i)
1957 d->strokePolygon_dev(polys.at(i).data(), polys.at(i).size(), false);
1958 }
1959}
1960
1961Q_GUI_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image,
1962 Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
1963{
1964 Q_ASSERT(image.format() == QImage::Format_RGB32);
1965 Q_ASSERT(image.depth() == 32);
1966
1967 XImage *xi;
1968 // Note: this code assumes either RGB or BGR, 8 bpc server layouts
1969 const uint red_mask = (uint) visual->red_mask;
1970 bool bgr_layout = (red_mask == 0xff);
1971
1972 const int w = rect.width();
1973 const int h = rect.height();
1974
1975 QImage im;
1976 int image_byte_order = ImageByteOrder(QXcbX11Info::display());
1977 if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout))
1978 || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)
1979 || (image_byte_order == LSBFirst && bgr_layout))
1980 {
1981 im = image.copy(rect);
1982 const qsizetype iw = im.bytesPerLine() / 4;
1983 uint *data = (uint *)im.bits();
1984 for (int i=0; i < h; i++) {
1985 uint *p = data;
1986 uint *end = p + w;
1987 if (bgr_layout && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
1988 while (p < end) {
1989 *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff);
1990 p++;
1991 }
1992 } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
1993 || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
1994 while (p < end) {
1995 *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
1996 | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
1997 p++;
1998 }
1999 } else if ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
2000 || (image_byte_order == LSBFirst && bgr_layout))
2001 {
2002 while (p < end) {
2003 *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
2004 | ((*p ) & 0xff00ff00);
2005 p++;
2006 }
2007 }
2008 data += iw;
2009 }
2010 xi = XCreateImage(dpy, visual, depth, ZPixmap,
2011 0, (char *) im.bits(), w, h, 32, im.bytesPerLine());
2012 } else {
2013 xi = XCreateImage(dpy, visual, depth, ZPixmap,
2014 0, (char *) image.scanLine(rect.y())+rect.x()*sizeof(uint), w, h, 32, image.bytesPerLine());
2015 }
2016 XPutImage(dpy, hd, gc, xi, 0, 0, pos.x(), pos.y(), w, h);
2017 xi->data = 0; // QImage owns these bits
2018 XDestroyImage(xi);
2019}
2020
2021void QX11PaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags)
2022{
2023 Q_D(QX11PaintEngine);
2024
2025 if (image.format() == QImage::Format_RGB32
2026 && d->pdev_depth >= 24 && image.depth() == 32
2027 && r.size() == sr.size())
2028 {
2029 int sx = qRound(sr.x());
2030 int sy = qRound(sr.y());
2031 int x = qRound(r.x());
2032 int y = qRound(r.y());
2033 int w = qRound(r.width());
2034 int h = qRound(r.height());
2035
2036 qt_x11_drawImage(QRect(sx, sy, w, h), QPoint(x, y), image, d->hd, d->gc, d->dpy,
2037 (Visual *)d->xinfo->visual(), d->pdev_depth);
2038 } else {
2039 QPaintEngine::drawImage(r, image, sr, flags);
2040 }
2041}
2042
2043void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRectF &_sr)
2044{
2045 Q_D(QX11PaintEngine);
2046 QRectF sr = _sr;
2047 int x = qRound(r.x());
2048 int y = qRound(r.y());
2049 int sx = qRound(sr.x());
2050 int sy = qRound(sr.y());
2051 int sw = qRound(sr.width());
2052 int sh = qRound(sr.height());
2053
2054 QPixmap pixmap = qt_toX11Pixmap(px);
2055 if (pixmap.isNull())
2056 return;
2057
2058 if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
2059 || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
2060 qt_x11SetScreen(pixmap, d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
2061 }
2062
2063 qt_x11SetDefaultScreen(qt_x11Info(pixmap).screen());
2064
2065#if QT_CONFIG(xrender)
2066 ::Picture src_pict = qt_x11PictureHandle(pixmap);
2067 if (src_pict && d->picture) {
2068 const int pDepth = pixmap.depth();
2069 if (pDepth == 1 && (d->has_alpha_pen)) {
2070 qt_render_bitmap(d->dpy, d->scrn, src_pict, d->picture,
2071 sx, sy, x, y, sw, sh, d->cpen);
2072 return;
2073 } else if (pDepth != 1 && (pDepth == 32 || pDepth != d->pdev_depth)) {
2074 XRenderComposite(d->dpy, d->composition_mode,
2075 src_pict, 0, d->picture, sx, sy, 0, 0, x, y, sw, sh);
2076 return;
2077 }
2078 }
2079#endif
2080
2081 bool mono_src = pixmap.depth() == 1;
2082 bool mono_dst = d->pdev_depth == 1;
2083 bool restore_clip = false;
2084
2085 if (static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) { // pixmap has a mask
2086 QBitmap comb(sw, sh);
2087 GC cgc = XCreateGC(d->dpy, qt_x11PixmapHandle(comb), 0, 0);
2088 XSetForeground(d->dpy, cgc, 0);
2089 XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
2090 XSetBackground(d->dpy, cgc, 0);
2091 XSetForeground(d->dpy, cgc, 1);
2092 if (!d->crgn.isEmpty()) {
2093 QList<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
2094 XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
2095 } else if (d->has_clipping) {
2096 XSetClipRectangles(d->dpy, cgc, 0, 0, 0, 0, Unsorted);
2097 }
2098 XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
2099 XSetTSOrigin(d->dpy, cgc, -sx, -sy);
2100 XSetStipple(d->dpy, cgc,
2101 static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask);
2102 XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
2103 XFreeGC(d->dpy, cgc);
2104
2105 XSetClipOrigin(d->dpy, d->gc, x, y);
2106 XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(comb));
2107 restore_clip = true;
2108 }
2109
2110 if (mono_src) {
2111 if (!d->crgn.isEmpty()) {
2112 Pixmap comb = XCreatePixmap(d->dpy, d->hd, sw, sh, 1);
2113 GC cgc = XCreateGC(d->dpy, comb, 0, 0);
2114 XSetForeground(d->dpy, cgc, 0);
2115 XFillRectangle(d->dpy, comb, cgc, 0, 0, sw, sh);
2116 QList<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
2117 XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
2118 XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), comb, cgc, sx, sy, sw, sh, 0, 0);
2119 XFreeGC(d->dpy, cgc);
2120
2121 XSetClipMask(d->dpy, d->gc, comb);
2122 XSetClipOrigin(d->dpy, d->gc, x, y);
2123 XFreePixmap(d->dpy, comb);
2124 } else {
2125 XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
2126 XSetClipOrigin(d->dpy, d->gc, x - sx, y - sy);
2127 }
2128
2129 if (mono_dst) {
2130 XSetForeground(d->dpy, d->gc, qGray(d->cpen.color().rgb()) > 127 ? 0 : 1);
2131 } else {
2132 QXcbColormap cmap = QXcbColormap::instance(d->scrn);
2133 XSetForeground(d->dpy, d->gc, cmap.pixel(d->cpen.color()));
2134 }
2135 XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh);
2136 restore_clip = true;
2137 } else if (mono_dst && !mono_src) {
2138 QBitmap bitmap = QBitmap::fromPixmap(pixmap);
2139 XCopyArea(d->dpy, qt_x11PixmapHandle(bitmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
2140 } else {
2141 XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
2142 }
2143
2144 if (d->pdev->devType() == QInternal::Pixmap) {
2145 const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
2146 Pixmap src_mask = static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask;
2147 Pixmap dst_mask = static_cast<QX11PlatformPixmap*>(px->handle())->x11_mask;
2148 if (dst_mask) {
2149 GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
2150 XSetClipOrigin(d->dpy, cgc, x, y);
2151 XSetClipMask(d->dpy, cgc, src_mask);
2152 if (src_mask) { // copy src mask into dst mask
2153 XCopyArea(d->dpy, src_mask, dst_mask, cgc, sx, sy, sw, sh, x, y);
2154 } else { // no src mask, but make sure the area copied is opaque in dest
2155 XSetBackground(d->dpy, cgc, 0);
2156 XSetForeground(d->dpy, cgc, 1);
2157 XFillRectangle(d->dpy, dst_mask, cgc, x, y, sw, sh);
2158 }
2159 XFreeGC(d->dpy, cgc);
2160 }
2161 }
2162
2163 if (restore_clip) {
2164 XSetClipOrigin(d->dpy, d->gc, 0, 0);
2165 QList<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
2166 if (rects.isEmpty())
2167 XSetClipMask(d->dpy, d->gc, XNone);
2168 else
2169 XSetClipRectangles(d->dpy, d->gc, 0, 0, rects.data(), rects.size(), Unsorted);
2170 }
2171}
2172
2173void QX11PaintEngine::updateMatrix(const QTransform &mtx)
2174{
2175 Q_D(QX11PaintEngine);
2176 d->txop = mtx.type();
2177 d->matrix = mtx;
2178
2179 d->has_complex_xform = (d->txop > QTransform::TxTranslate);
2180
2181 extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
2182 bool scaling = qt_scaleForTransform(d->matrix, &d->xform_scale);
2183 d->has_scaling_xform = scaling && d->xform_scale != 1.0;
2184 d->has_non_scaling_xform = scaling && d->xform_scale == 1.0;
2185}
2186
2187/*
2188 NB! the clip region is expected to be in dev coordinates
2189*/
2190void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
2191{
2192 Q_D(QX11PaintEngine);
2193 QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
2194 if (op == Qt::NoClip) {
2195 d->has_clipping = false;
2196 d->crgn = sysClip;
2197 if (!sysClip.isEmpty()) {
2198 x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, sysClip);
2199 } else {
2200 x11ClearClipRegion(d->dpy, d->gc, d->gc_brush, d->picture);
2201 }
2202 return;
2203 }
2204
2205 switch (op) {
2206 case Qt::IntersectClip:
2207 if (d->has_clipping) {
2208 d->crgn &= clipRegion;
2209 break;
2210 }
2211 // fall through
2212 case Qt::ReplaceClip:
2213 if (!sysClip.isEmpty())
2214 d->crgn = clipRegion.intersected(sysClip);
2215 else
2216 d->crgn = clipRegion;
2217 break;
2218// case Qt::UniteClip:
2219// d->crgn |= clipRegion;
2220// if (!sysClip.isEmpty())
2221// d->crgn = d->crgn.intersected(sysClip);
2222// break;
2223 default:
2224 break;
2225 }
2226 d->has_clipping = true;
2227 x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, d->crgn);
2228}
2229
2230void QX11PaintEngine::updateFont(const QFont &)
2231{
2232}
2233
2235{
2236 Q_D(const QX11PaintEngine);
2237 Q_ASSERT(isActive());
2238 Q_ASSERT(d->hd);
2239 return d->hd;
2240}
2241
2242extern void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &,
2243 qreal, qreal);
2244
2245void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
2246{
2247 int x = qRound(r.x());
2248 int y = qRound(r.y());
2249 int w = qRound(r.width());
2250 int h = qRound(r.height());
2251 int sx = qRound(p.x());
2252 int sy = qRound(p.y());
2253
2254 bool mono_src = pixmap.depth() == 1;
2255 Q_D(QX11PaintEngine);
2256
2257 if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
2258 || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
2259 QPixmap* p = const_cast<QPixmap *>(&pixmap);
2260 qt_x11SetScreen(*p, d->xinfo ? d->xinfo->screen() : DefaultScreen(QXcbX11Info::display()));
2261 }
2262
2264
2265#if QT_CONFIG(xrender)
2266 if (X11->use_xrender && d->picture && qt_x11PictureHandle(pixmap)) {
2267 const int numTiles = (w / pixmap.width()) * (h / pixmap.height());
2268 if (numTiles < 100) {
2269 // this is essentially qt_draw_tile(), inlined for
2270 // the XRenderComposite call
2271 int yPos, xPos, drawH, drawW, yOff, xOff;
2272 yPos = y;
2273 yOff = sy;
2274 while (yPos < y + h) {
2275 drawH = pixmap.height() - yOff; // Cropping first row
2276 if (yPos + drawH > y + h) // Cropping last row
2277 drawH = y + h - yPos;
2278 xPos = x;
2279 xOff = sx;
2280 while (xPos < x + w) {
2281 drawW = pixmap.width() - xOff; // Cropping first column
2282 if (xPos + drawW > x + w) // Cropping last column
2283 drawW = x + w - xPos;
2284 if (mono_src) {
2285 qt_render_bitmap(d->dpy, d->scrn, qt_x11PictureHandle(pixmap), d->picture,
2286 xOff, yOff, xPos, yPos, drawW, drawH, d->cpen);
2287 } else {
2288 XRenderComposite(d->dpy, d->composition_mode,
2289 qt_x11PictureHandle(pixmap), XNone, d->picture,
2290 xOff, yOff, 0, 0, xPos, yPos, drawW, drawH);
2291 }
2292 xPos += drawW;
2293 xOff = 0;
2294 }
2295 yPos += drawH;
2296 yOff = 0;
2297 }
2298 } else {
2299 w = qMin(w, d->pdev->width() - x);
2300 h = qMin(h, d->pdev->height() - y);
2301 if (w <= 0 || h <= 0)
2302 return;
2303
2304 const int pw = w + sx;
2305 const int ph = h + sy;
2306 QPixmap pm(pw, ph);
2307 if (pixmap.hasAlpha() || mono_src)
2308 pm.fill(Qt::transparent);
2309
2310 const int mode = pixmap.hasAlpha() ? PictOpOver : PictOpSrc;
2311 const ::Picture pmPicture = qt_x11PictureHandle(pm);
2312
2313 // first tile
2314 XRenderComposite(d->dpy, mode,
2315 qt_x11PictureHandle(pixmap), XNone, pmPicture,
2316 0, 0, 0, 0, 0, 0, qMin(pw, pixmap.width()), qMin(ph, pixmap.height()));
2317
2318 // first row of tiles
2319 int xPos = pixmap.width();
2320 const int sh = qMin(ph, pixmap.height());
2321 while (xPos < pw) {
2322 const int sw = qMin(xPos, pw - xPos);
2323 XRenderComposite(d->dpy, mode,
2324 pmPicture, XNone, pmPicture,
2325 0, 0, 0, 0, xPos, 0, sw, sh);
2326 xPos *= 2;
2327 }
2328
2329 // remaining rows
2330 int yPos = pixmap.height();
2331 const int sw = pw;
2332 while (yPos < ph) {
2333 const int sh = qMin(yPos, ph - yPos);
2334 XRenderComposite(d->dpy, mode,
2335 pmPicture, XNone, pmPicture,
2336 0, 0, 0, 0, 0, yPos, sw, sh);
2337 yPos *= 2;
2338 }
2339
2340 // composite
2341 if (mono_src)
2342 qt_render_bitmap(d->dpy, d->scrn, pmPicture, d->picture,
2343 sx, sy, x, y, w, h, d->cpen);
2344 else
2345 XRenderComposite(d->dpy, d->composition_mode,
2346 pmPicture, XNone, d->picture,
2347 sx, sy, 0, 0, x, y, w, h);
2348 }
2349 } else
2350#endif // QT_CONFIG(xrender)
2351 if (pixmap.depth() > 1 && !static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) {
2352 XSetTile(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
2353 XSetFillStyle(d->dpy, d->gc, FillTiled);
2354 XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
2355 XFillRectangle(d->dpy, d->hd, d->gc, x, y, w, h);
2356 XSetTSOrigin(d->dpy, d->gc, 0, 0);
2357 XSetFillStyle(d->dpy, d->gc, FillSolid);
2358 } else {
2359 qt_draw_tile(this, x, y, w, h, pixmap, sx, sy);
2360 }
2361}
2362
2363bool QX11PaintEngine::drawCachedGlyphs(const QTransform &transform, const QTextItemInt &ti)
2364{
2365#if QT_CONFIG(xrender)
2366 Q_D(QX11PaintEngine);
2367 Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
2368
2369 if (!X11->use_xrender)
2370 return false;
2371
2372 QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
2373 QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
2374
2375 if (!set || set->outline_drawing)
2376 return false;
2377
2378 QFontEngine::GlyphFormat glyphFormat = QXRenderGlyphCache::glyphFormatForDepth(ft, d->pdev_depth);
2379
2380 QXRenderGlyphCache *cache = static_cast<QXRenderGlyphCache *>(ft->glyphCache(set, glyphFormat, transform));
2381 if (!cache) {
2382 cache = new QXRenderGlyphCache(QXcbX11Info(), glyphFormat, transform);
2383 ft->setGlyphCache(set, cache);
2384 }
2385
2386 return cache->draw(X11->getSolidFill(d->scrn, d->cpen.color()), d->picture, transform, ti);
2387#else // !QT_CONFIG(xrender)
2388 return false;
2389#endif // QT_CONFIG(xrender)
2390}
2391
2392void QX11PaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
2393{
2394 Q_D(QX11PaintEngine);
2395 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
2396
2397 switch (ti.fontEngine->type()) {
2398 case QFontEngine::TestFontEngine:
2399 case QFontEngine::Box:
2400 d->drawBoxTextItem(p, ti);
2401 break;
2402#if QT_CONFIG(fontconfig)
2403 case QFontEngine::Freetype:
2404 drawFreetype(p, ti);
2405 break;
2406#endif
2407 default:
2408 Q_ASSERT(false);
2409 }
2410}
2411
2412#if QT_CONFIG(fontconfig)
2413static bool path_for_glyphs(QPainterPath *path,
2414 const QVarLengthArray<glyph_t> &glyphs,
2415 const QVarLengthArray<QFixedPoint> &positions,
2416 const QFontEngineFT *ft)
2417{
2418 bool result = true;
2419 *path = QPainterPath();
2420 path->setFillRule(Qt::WindingFill);
2421 ft->lockFace();
2422 int i = 0;
2423 while (i < glyphs.size()) {
2424 QFontEngineFT::Glyph *glyph = ft->loadGlyph(glyphs[i], QFixedPoint(), QFontEngineFT::Format_Mono);
2425 // #### fix case where we don't get a glyph
2426 if (!glyph || glyph->format != QFontEngineFT::Format_Mono) {
2427 result = false;
2428 break;
2429 }
2430
2431 int n = 0;
2432 int h = glyph->height;
2433 int xp = qRound(positions[i].x);
2434 int yp = qRound(positions[i].y);
2435
2436 xp += glyph->x;
2437 yp += -glyph->y + glyph->height;
2438 int pitch = ((glyph->width + 31) & ~31) >> 3;
2439
2440 uchar *src = glyph->data;
2441 while (h--) {
2442 for (int x = 0; x < glyph->width; ++x) {
2443 bool set = src[x >> 3] & (0x80 >> (x & 7));
2444 if (set) {
2445 QRect r(xp + x, yp - h, 1, 1);
2446 while (x+1 < glyph->width && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) {
2447 ++x;
2448 r.setRight(r.right()+1);
2449 }
2450
2451 path->addRect(r);
2452 ++n;
2453 }
2454 }
2455 src += pitch;
2456 }
2457 ++i;
2458 }
2459 ft->unlockFace();
2460 return result;
2461}
2462
2463void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti)
2464{
2465 Q_D(QX11PaintEngine);
2466
2467 if (!ti.glyphs.numGlyphs)
2468 return;
2469
2470 if (!d->cpen.isSolid()) {
2471 QPaintEngine::drawTextItem(p, ti);
2472 return;
2473 }
2474
2475 const bool xrenderPath = (X11->use_xrender
2476 && !(d->pdev->devType() == QInternal::Pixmap
2477 && static_cast<const QPixmap *>(d->pdev)->handle()->pixelType() == QPlatformPixmap::BitmapType));
2478
2479 if (xrenderPath) {
2480 QTransform transform = d->matrix;
2481 transform.translate(p.x(), p.y());
2482
2483 if (drawCachedGlyphs(transform, ti))
2484 return;
2485 }
2486
2487 QTransform transform;
2488 transform.translate(p.x(), p.y());
2489
2490 QVarLengthArray<QFixedPoint> positions;
2491 QVarLengthArray<glyph_t> glyphs;
2492 ti.fontEngine->getGlyphPositions(ti.glyphs, transform, ti.flags, glyphs, positions);
2493
2494 if (glyphs.count() == 0)
2495 return;
2496
2497 QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
2498 QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
2499 QPainterPath path;
2500
2501 if (!set || set->outline_drawing || !path_for_glyphs(&path, glyphs, positions, ft)) {
2502 QPaintEngine::drawTextItem(p, ti);
2503 return;
2504 }
2505
2506 if (path.elementCount() <= 1)
2507 return;
2508
2509 Q_ASSERT((path.elementCount() % 5) == 0);
2510 if (d->txop >= QTransform::TxScale) {
2511 painter()->save();
2512 painter()->setBrush(d->cpen.brush());
2513 painter()->setPen(Qt::NoPen);
2514 painter()->drawPath(path);
2515 painter()->restore();
2516 return;
2517 }
2518
2519 const int rectcount = 256;
2520 XRectangle rects[rectcount];
2521 int num_rects = 0;
2522
2523 QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
2524 QRect clip(d->polygonClipper.boundingRect());
2525 for (int i=0; i < path.elementCount(); i+=5) {
2526 int x = qRound(path.elementAt(i).x);
2527 int y = qRound(path.elementAt(i).y);
2528 int w = qRound(path.elementAt(i+1).x) - x;
2529 int h = qRound(path.elementAt(i+2).y) - y;
2530
2531 QRect rect = QRect(x + delta.x(), y + delta.y(), w, h);
2532 rect = rect.intersected(clip);
2533 if (rect.isEmpty())
2534 continue;
2535
2536 rects[num_rects].x = short(rect.x());
2537 rects[num_rects].y = short(rect.y());
2538 rects[num_rects].width = ushort(rect.width());
2539 rects[num_rects].height = ushort(rect.height());
2540 ++num_rects;
2541 if (num_rects == rectcount) {
2542 XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
2543 num_rects = 0;
2544 }
2545 }
2546 if (num_rects > 0)
2547 XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
2548}
2549#endif // QT_CONFIG(fontconfig)
2550
2551#if QT_CONFIG(xrender)
2552QXRenderGlyphCache::QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix)
2553 : QFontEngineGlyphCache(format, matrix)
2554 , xinfo(x)
2555 , gset(XNone)
2556{}
2557
2558QXRenderGlyphCache::~QXRenderGlyphCache()
2559{
2560 if (gset != XNone)
2561 XRenderFreeGlyphSet(xinfo.display(), gset);
2562}
2563
2564bool QXRenderGlyphCache::addGlyphs(const QTextItemInt &ti,
2565 const QVarLengthArray<glyph_t> &glyphs,
2566 const QVarLengthArray<QFixedPoint> &positions)
2567{
2568 Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
2569
2570 QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
2571 QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform());
2572
2573 XGlyphInfo xglyphinfo;
2574
2575 for (int i = 0; i < glyphs.size(); ++i) {
2576 const QFixed sppx = ft->subPixelPositionForX(positions[i].x);
2577 const QFixedPoint spp(sppx, 0);
2578 QFontEngineFT::Glyph *glyph = set->getGlyph(glyphs[i], spp);
2579 Glyph xglyphid = qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyphs[i], spp));
2580
2581 if (glyph && glyph->format == glyphFormat()) {
2582 if (cachedGlyphs.contains(xglyphid)) {
2583 continue;
2584 } else {
2585 set->setGlyph(glyphs[i], spp, nullptr);
2586 delete glyph;
2587 glyph = 0;
2588 }
2589 }
2590
2591 glyph = ft->loadGlyphFor(glyphs[i], spp, glyphFormat(), transform(), QColor());
2592
2593 if (glyph == 0 || glyph->format != glyphFormat())
2594 return false;
2595
2596 if (glyph->format == QFontEngine::Format_Mono) {
2597 // Must convert bitmap from msb to lsb bit order
2598 QImage img(glyph->data, glyph->width, glyph->height, QImage::Format_Mono);
2599 img = img.convertToFormat(QImage::Format_MonoLSB);
2600 memcpy(glyph->data, img.constBits(), static_cast<size_t>(img.sizeInBytes()));
2601 }
2602
2603 set->setGlyph(glyphs[i], spp, glyph);
2604 Q_ASSERT(glyph->data || glyph->width == 0 || glyph->height == 0);
2605
2606 xglyphinfo.width = glyph->width;
2607 xglyphinfo.height = glyph->height;
2608 xglyphinfo.x = -glyph->x;
2609 xglyphinfo.y = glyph->y;
2610 xglyphinfo.xOff = glyph->advance;
2611 xglyphinfo.yOff = 0;
2612
2613 XRenderAddGlyphs(xinfo.display(), glyphSet(), &xglyphid, &xglyphinfo, 1, (const char *) glyph->data, glyphBufferSize(*glyph));
2614 cachedGlyphs.insert(xglyphid);
2615 }
2616
2617 return true;
2618}
2619
2620bool QXRenderGlyphCache::draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti)
2621{
2622 Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
2623
2624 if (ti.glyphs.numGlyphs == 0)
2625 return true;
2626
2627 QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
2628 QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(matrix);
2629
2630 QVarLengthArray<glyph_t> glyphs;
2631 QVarLengthArray<QFixedPoint> positions;
2632 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2633
2634 if (glyphs.isEmpty())
2635 return true;
2636
2637 if (!addGlyphs(ti, glyphs, positions))
2638 return false;
2639
2640 QVarLengthArray<unsigned int> chars(glyphs.size());
2641
2642 for (int i = 0; i < glyphs.size(); ++i)
2643 chars[i] = glyphId(glyphs[i], ft->subPixelPositionForX(positions[i].x));
2644
2645 int i = 0;
2646 while (i < glyphs.size() && !isValidCoordinate(positions[i]))
2647 ++i;
2648
2649 if (i >= glyphs.size())
2650 return true;
2651
2652 QFixed xp = positions[i].x;
2653 QFixed yp = positions[i].y;
2654 QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2655
2656 XGlyphElt32 elt;
2657 elt.glyphset = gset;
2658 elt.chars = &chars[i];
2659 elt.nchars = 1;
2660 elt.xOff = qRound(xp + offs);
2661 elt.yOff = qRound(yp + offs);
2662
2663 ++i;
2664
2665 for (; i < glyphs.size(); ++i) {
2666 if (!isValidCoordinate(positions[i]))
2667 break;
2668
2669 const QFixed sppx = ft->subPixelPositionForX(positions[i].x);
2670 const QFixedPoint spp(sppx, 0);
2671 QFontEngineFT::Glyph *g = set->getGlyph(glyphs[i], spp);
2672
2673 if (g
2674 && positions[i].x == xp + g->advance
2675 && positions[i].y == yp
2676 && elt.nchars < 253 // don't draw more than 253 characters as some X servers
2677 // hang with it
2678 ) {
2679 elt.nchars++;
2680 xp += g->advance;
2681 } else {
2682 xp = positions[i].x;
2683 yp = positions[i].y;
2684
2685 XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
2686 renderPictFormat(), 0, 0, 0, 0,
2687 &elt, 1);
2688 elt.chars = &chars[i];
2689 elt.nchars = 1;
2690 elt.xOff = qRound(xp + offs);
2691 elt.yOff = qRound(yp + offs);
2692 }
2693 }
2694
2695 XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
2696 renderPictFormat(), 0, 0, 0, 0, &elt, 1);
2697
2698 return true;
2699}
2700
2701GlyphSet QXRenderGlyphCache::glyphSet()
2702{
2703 if (gset == XNone)
2704 gset = XRenderCreateGlyphSet(xinfo.display(), renderPictFormat());
2705
2706 Q_ASSERT(gset != XNone);
2707 return gset;
2708}
2709
2710int QXRenderGlyphCache::glyphBufferSize(const QFontEngineFT::Glyph &glyph) const
2711{
2712 int pitch = 0;
2713
2714 switch (glyphFormat()) {
2715 case QFontEngine::Format_Mono:
2716 pitch = ((glyph.width + 31) & ~31) >> 3;
2717 break;
2718 case QFontEngine::Format_A8:
2719 pitch = (glyph.width + 3) & ~3;
2720 break;
2721 default:
2722 pitch = glyph.width * 4;
2723 break;
2724 }
2725
2726 return pitch * glyph.height;
2727}
2728
2729QImage::Format QXRenderGlyphCache::imageFormat() const
2730{
2731 switch (glyphFormat()) {
2732 case QFontEngine::Format_None:
2733 Q_UNREACHABLE();
2734 break;
2735 case QFontEngine::Format_Mono:
2736 return QImage::Format_Mono;
2737 break;
2738 case QFontEngine::Format_A8:
2739 return QImage::Format_Alpha8;
2740 break;
2741 case QFontEngine::Format_A32:
2742 case QFontEngine::Format_ARGB:
2743 return QImage::Format_ARGB32_Premultiplied;
2744 break;
2745 }
2746
2747 Q_UNREACHABLE();
2748}
2749
2750const XRenderPictFormat *QXRenderGlyphCache::renderPictFormat() const
2751{
2752 switch (glyphFormat()) {
2753 case QFontEngine::Format_None:
2754 Q_UNREACHABLE();
2755 break;
2756 case QFontEngine::Format_Mono:
2757 return XRenderFindStandardFormat(xinfo.display(), PictStandardA1);
2758 break;
2759 case QFontEngine::Format_A8:
2760 return XRenderFindStandardFormat(xinfo.display(), PictStandardA8);
2761 break;
2762 case QFontEngine::Format_A32:
2763 case QFontEngine::Format_ARGB:
2764 return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32);
2765 break;
2766 }
2767
2768 Q_UNREACHABLE();
2769}
2770
2771QFontEngine::GlyphFormat QXRenderGlyphCache::glyphFormatForDepth(QFontEngine *fontEngine, int depth)
2772{
2773 QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat;
2774
2775 if (glyphFormat == QFontEngine::Format_None) {
2776 switch (depth) {
2777 case 32:
2778 glyphFormat = QFontEngine::Format_ARGB;
2779 break;
2780 case 24:
2781 glyphFormat = QFontEngine::Format_A32;
2782 break;
2783 case 1:
2784 glyphFormat = QFontEngine::Format_Mono;
2785 break;
2786 default:
2787 glyphFormat = QFontEngine::Format_A8;
2788 break;
2789 }
2790 }
2791
2792 return glyphFormat;
2793}
2794
2795Glyph QXRenderGlyphCache::glyphId(glyph_t glyph, QFixed subPixelPosition)
2796{
2797 return qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyph, QFixedPoint(subPixelPosition, 0)));
2798}
2799
2800bool QXRenderGlyphCache::isValidCoordinate(const QFixedPoint &fp)
2801{
2802 enum { t_min = SHRT_MIN, t_max = SHRT_MAX };
2803 return (fp.x < t_min || fp.x > t_max || fp.y < t_min || fp.y > t_max) ? false : true;
2804}
2805#endif // QT_CONFIG(xrender)
2806
2807QT_END_NAMESPACE
void systemStateChanged() override
QPolygonClipper< qt_float_point, qt_float_point, float > polygonClipper
void setupAdaptedOrigin(const QPoint &p)
void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode, QPaintEngine::PolygonDrawMode mode)
void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
QTransform::TransformationType txop
void strokePolygon_dev(const QPointF *points, int pointCount, bool close)
const QXcbX11Info * xinfo
void fillPath(const QPainterPath &path, GCMode gcmode, bool transform)
void strokePolygon_translated(const QPointF *points, int pointCount, bool close)
void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode, QPaintEngine::PolygonDrawMode mode)
void updateState(const QPaintEngineState &state) override
Reimplement this function to update the state of a paint engine.
void drawTextItem(const QPointF &p, const QTextItem &textItem) override
This function draws the text item textItem at position p.
void drawLines(const QLine *lines, int lineCount) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool begin(QPaintDevice *pdev) override
Reimplement this function to initialise your paint engine when painting is to start on the paint devi...
void updateBrush(const QBrush &brush, const QPointF &pt)
void updateClipRegion_dev(const QRegion &region, Qt::ClipOperation op)
void updateFont(const QFont &font)
virtual Drawable handle() const
QX11PaintEngine(QX11PaintEnginePrivate &dptr)
void drawRects(const QRect *rects, int rectCount) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void updateMatrix(const QTransform &matrix)
QPainter::RenderHints supportedRenderHints() const
void updateRenderHints(QPainter::RenderHints hints)
void drawPath(const QPainterPath &path) override
The default implementation ignores the path and does nothing.
void drawPoints(const QPoint *points, int pointCount) override
Draws the first pointCount points in the buffer points.
virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override
Reimplement this virtual function to draw the polygon defined by the pointCount first points in point...
void updatePen(const QPen &pen)
void drawImage(const QRectF &r, const QImage &img, const QRectF &sr, Qt::ImageConversionFlags flags=Qt::AutoColor) override
Reimplement this function to draw the part of the image specified by the sr rectangle in the given re...
bool end() override
Reimplement this function to finish painting on the current paint device.
void drawEllipse(const QRect &r) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
friend const QXcbX11Info & qt_x11Info(const QPixmap &pixmap)
static Display * display()
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
static void setCapStyle(int cap_style, GC gc)
static void x11SetClipRegion(Display *dpy, GC gc, GC gc2, Qt::HANDLE picture, const QRegion &r)
static const qreal aliasedCoordinateDelta
#define X11
static QPaintEngine::PaintEngineFeatures qt_decide_features()
Q_GUI_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image, Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
static QLine clipStraightLine(const QRect &clip, const QLine &l)
#define DITHER_SIZE
QPixmap qt_toX11Pixmap(const QPixmap &pixmap)
static void x11ClearClipRegion(Display *dpy, GC gc, GC gc2, Qt::HANDLE picture)
static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE]
static const QXcbX11Info * qt_x11Info(const QPaintDevice *pd)
void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &, qreal, qreal)
static QPixmap qt_patternForAlpha(uchar alpha, int screen)
static bool clipLine(QLineF *line, const QRect &rect)
XID Drawable
QPixmap qt_pixmapForBrush(int style, bool invert)
Definition qbrush.cpp:80
XID Pixmap
int qt_x11SetDefaultScreen(int screen)
QT_BEGIN_NAMESPACE QPainterPath qt_regionToPath(const QRegion &region)
Definition qregion.cpp:1010
#define EvenOddRule
Definition qregion.cpp:1604
#define WindingRule
Definition qregion.cpp:1605
#define Q_XCB_EXPORT
Definition qxcbexport.h:14
void write(QChar *&dest) const
HexString(const T t)
static int size(const HexString< T > &)
static void appendTo(const HexString< T > &str, QChar *&out)
bool use_xrender
Definition qt_x11_p.h:94