1 /*
2  * wangoverlay.cpp
3  * Copyright 2020, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
4  *
5  * This file is part of Tiled.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "wangoverlay.h"
22 
23 #include "utils.h"
24 
25 #include <QGuiApplication>
26 #include <QPainter>
27 #include <QPainterPath>
28 #include <QPalette>
29 
30 namespace Tiled {
31 
32 static constexpr WangId oneCornerMask = WangId::MaskTopRight;
33 static constexpr WangId oneEdgeMask = WangId::MaskTop;
34 static constexpr WangId twoAdjacentCornersMask = WangId::MaskTopRight | WangId::MaskBottomRight;
35 static constexpr WangId twoOppositeCornersMask = WangId::MaskTopRight | WangId::MaskBottomLeft;
36 static constexpr WangId twoAdjacentEdgesMask = WangId::MaskTop | WangId::MaskRight;
37 static constexpr WangId twoOppositeEdgesMask = WangId::MaskTop | WangId::MaskBottom;
38 static constexpr WangId threeCornersMask = WangId::MaskTopRight | WangId::MaskBottomRight | WangId::MaskBottomLeft;
39 static constexpr WangId threeEdgesMask = WangId::MaskTop | WangId::MaskRight | WangId::MaskBottom;
40 
41 struct PathWithMask {
42     const QPainterPath path;
43     const WangId mask;
44 };
45 
rotated(const QPainterPath & path,int rotations)46 static QPainterPath rotated(const QPainterPath &path, int rotations)
47 {
48     QTransform transform;
49     transform.translate(0.5, 0.5);
50     transform.rotate(rotations * 90.0);
51     transform.translate(-0.5, -0.5);
52     return transform.map(path);
53 }
54 
55 namespace EdgesAndCorners {
56 
__anon930826070102null57 static const QPainterPath oneEdge = [] {
58     constexpr qreal d = 1.0 / 6.0;
59     QPainterPath path(QPointF(2 * d, 0));
60     path.lineTo(4 * d, 0);
61     path.lineTo(4 * d, 1 * d);
62     path.arcTo(QRectF(QPointF(2 * d, 0), QSizeF(2 * d, 2 * d)), 0, -180);
63     path.closeSubpath();
64     return path;
65 }();
66 
67 #if 1   // Draw edges as "roads"
68 
__anon930826070202null69 static const QPainterPath twoAdjacentEdges = [] {
70     constexpr qreal d = 1.0 / 6.0;
71     QPainterPath path(QPointF(2 * d, 0));
72     path.lineTo(4 * d, 0);
73     path.lineTo(4 * d, 1 * d);
74     path.arcTo(QRectF(QPointF(4 * d, 0), QSizeF(2 * d, 2 * d)), -180, 90);
75     path.lineTo(6 * d, 2 * d);
76     path.lineTo(6 * d, 4 * d);
77     path.lineTo(5 * d, 4 * d);
78     path.arcTo(QRectF(QPointF(2 * d, -2 * d), QSizeF(6 * d, 6 * d)), -90, -90);
79     path.closeSubpath();
80     return path;
81 }();
82 
__anon930826070302null83 static const QPainterPath twoOppositeEdges = [] {
84     constexpr qreal d = 1.0 / 3.0;
85     QPainterPath path;
86     path.addRect(d, 0, d, d * 3);
87     return path;
88 }();
89 
__anon930826070402null90 static const QPainterPath threeEdges = [] {
91     constexpr qreal d = 1.0 / 6.0;
92     QPainterPath path(QPointF(2 * d, 0));
93     path.lineTo(4 * d, 0);
94     path.lineTo(4 * d, 1 * d);
95     path.arcTo(QRectF(QPointF(4 * d, 0), QSizeF(2 * d, 2 * d)), -180, 90);
96     path.lineTo(6 * d, 2 * d);
97     path.lineTo(6 * d, 4 * d);
98     path.lineTo(5 * d, 4 * d);
99     path.arcTo(QRectF(QPointF(4 * d, 4 * d), QSizeF(2 * d, 2 * d)), 90, 90);
100     path.lineTo(4 * d, 6 * d);
101     path.lineTo(2 * d, 6 * d);
102     path.closeSubpath();
103     return path;
104 }();
105 
__anon930826070502null106 static const QPainterPath fourEdges = [] {
107     constexpr qreal d = 1.0 / 6.0;
108     QPainterPath path(QPointF(2 * d, 0));
109     path.lineTo(4 * d, 0);
110     path.lineTo(4 * d, 1 * d);
111     path.arcTo(QRectF(QPointF(4 * d, 0), QSizeF(2 * d, 2 * d)), -180, 90);
112     path.lineTo(6 * d, 2 * d);
113     path.lineTo(6 * d, 4 * d);
114     path.lineTo(5 * d, 4 * d);
115     path.arcTo(QRectF(QPointF(4 * d, 4 * d), QSizeF(2 * d, 2 * d)), 90, 90);
116     path.lineTo(4 * d, 6 * d);
117     path.lineTo(2 * d, 6 * d);
118     path.arcTo(QRectF(QPointF(0, 4 * d), QSizeF(2 * d, 2 * d)), 0, 90);
119     path.lineTo(0, 4 * d);
120     path.lineTo(0, 2 * d);
121     path.lineTo(d, 2 * d);
122     path.arcTo(QRectF(QPointF(0, 0), QSizeF(2 * d, 2 * d)), -90, 90);
123     path.closeSubpath();
124     return path;
125 }();
126 
127 #else   // Don't connect edges
128 
__anon930826070602null129 static const QPainterPath twoAdjacentEdges = [] {
130     QPainterPath path = oneEdge;
131     path |= rotated(oneEdge, 1);
132     return path;
133 }();
134 
__anon930826070702null135 static const QPainterPath twoOppositeEdges = [] {
136     QPainterPath path = oneEdge;
137     path |= rotated(oneEdge, 2);
138     return path;
139 }();
140 
__anon930826070802null141 static const QPainterPath threeEdges = [] {
142     QPainterPath path = twoAdjacentEdges;
143     path |= rotated(oneEdge, 2);
144     return path;
145 }();
146 
__anon930826070902null147 static const QPainterPath fourEdges = [] {
148     QPainterPath path = threeEdges;
149     path |= rotated(oneEdge, 3);
150     return path;
151 }();
152 
153 #endif
154 
edgePathForMask(WangId mask)155 static const QPainterPath *edgePathForMask(WangId mask)
156 {
157     static const PathWithMask edgesWithMasks[] = {
158         { fourEdges, WangId::MaskEdges },
159         { threeEdges, threeEdgesMask },
160         { rotated(threeEdges, 1), threeEdgesMask.rotated(1) },
161         { rotated(threeEdges, 2), threeEdgesMask.rotated(2) },
162         { rotated(threeEdges, 3), threeEdgesMask.rotated(3) },
163         { twoAdjacentEdges, twoAdjacentEdgesMask },
164         { rotated(twoAdjacentEdges, 1), twoAdjacentEdgesMask.rotated(1) },
165         { rotated(twoAdjacentEdges, 2), twoAdjacentEdgesMask.rotated(2) },
166         { rotated(twoAdjacentEdges, 3), twoAdjacentEdgesMask.rotated(3) },
167         { twoOppositeEdges, twoOppositeEdgesMask },
168         { rotated(twoOppositeEdges, 1), twoOppositeEdgesMask.rotated(1) },
169         { oneEdge, oneEdgeMask },
170         { rotated(oneEdge, 1), oneEdgeMask.rotated(1) },
171         { rotated(oneEdge, 2), oneEdgeMask.rotated(2) },
172         { rotated(oneEdge, 3), oneEdgeMask.rotated(3) },
173     };
174 
175     for (auto &pathWithMask : edgesWithMasks)
176         if (mask == pathWithMask.mask)
177             return &pathWithMask.path;
178     return nullptr;
179 }
180 
__anon930826070a02null181 static const QPainterPath oneCorner = [] {
182     constexpr qreal d = 1.0 / 6.0;
183     QPainterPath path(QPointF(4 * d, 0));
184     path.lineTo(6 * d, 0);
185     path.lineTo(6 * d, 2 * d);
186     path.lineTo(5 * d, 2 * d);
187     path.arcTo(QRectF(QPointF(4 * d, 0), QSizeF(2 * d, 2 * d)), -90, -90);
188     path.closeSubpath();
189     return path;
190 }();
191 
__anon930826070b02null192 static const QPainterPath twoAdjacentCorners = [] {
193     QPainterPath path = oneCorner;
194     path |= rotated(oneCorner, 1);
195     return path;
196 }();
197 
__anon930826070c02null198 static const QPainterPath twoOppositeCorners = [] {
199     QPainterPath path = oneCorner;
200     path |= rotated(oneCorner, 2);
201     return path;
202 }();
203 
__anon930826070d02null204 static const QPainterPath threeCorners = [] {
205     QPainterPath path = twoAdjacentCorners;
206     path |= rotated(oneCorner, 2);
207     return path;
208 }();
209 
__anon930826070e02null210 static const QPainterPath fourCorners = [] {
211     QPainterPath path = twoAdjacentCorners;
212     path |= rotated(twoAdjacentCorners, 2);
213     return path;
214 }();
215 
cornerPathForMask(WangId mask)216 static const QPainterPath *cornerPathForMask(WangId mask)
217 {
218     static const PathWithMask cornersWithMasks[] = {
219         { fourCorners, WangId::MaskCorners },
220         { threeCorners, threeCornersMask },
221         { rotated(threeCorners, 1), threeCornersMask.rotated(1) },
222         { rotated(threeCorners, 2), threeCornersMask.rotated(2) },
223         { rotated(threeCorners, 3), threeCornersMask.rotated(3) },
224         { twoAdjacentCorners, twoAdjacentCornersMask },
225         { rotated(twoAdjacentCorners, 1), twoAdjacentCornersMask.rotated(1) },
226         { rotated(twoAdjacentCorners, 2), twoAdjacentCornersMask.rotated(2) },
227         { rotated(twoAdjacentCorners, 3), twoAdjacentCornersMask.rotated(3) },
228         { twoOppositeCorners, twoOppositeCornersMask },
229         { rotated(twoOppositeCorners, 1), twoOppositeCornersMask.rotated(1) },
230         { oneCorner, oneCornerMask },
231         { rotated(oneCorner, 1), oneCornerMask.rotated(1) },
232         { rotated(oneCorner, 2), oneCornerMask.rotated(2) },
233         { rotated(oneCorner, 3), oneCornerMask.rotated(3) },
234     };
235 
236     for (auto &pathWithMask : cornersWithMasks)
237         if (mask == pathWithMask.mask)
238             return &pathWithMask.path;
239     return nullptr;
240 }
241 
242 } // namespace EdgesAndCorners
243 
244 #if 0   // Special handling of edge-only Wang sets
245 
246 namespace EdgesOnly {
247 
248 #if 1   // Draw edge Wang sets as "wide roads"
249 
250 static const QPainterPath oneEdge = [] {
251     constexpr qreal d = 1.0 / 6.0;
252     QPainterPath path(QPointF(5 * d, 0));
253     path.arcTo(QRectF(QPointF(d, -2 * d), QSizeF(4 * d, 4 * d)), 0, -180);
254     path.closeSubpath();
255     return path;
256 }();
257 
258 static const QPainterPath twoAdjacentEdges = [] {
259     constexpr qreal d = 1.0 / 6.0;
260     QPainterPath path(QPointF(5 * d, 0));
261     path.arcTo(QRectF(QPointF(5 * d, -d), QSizeF(2 * d, 2 * d)), 180, 90);
262     path.lineTo(6 * d, 5 * d);
263     path.arcTo(QRectF(QPointF(d, -5 * d), QSizeF(10 * d, 10 * d)), -90, -90);
264     path.closeSubpath();
265     return path;
266 }();
267 
268 static const QPainterPath twoOppositeEdges = [] {
269     constexpr qreal d = 1.0 / 6.0;
270     QPainterPath path;
271     path.addRect(d, 0, 4 * d, d * 6);
272     return path;
273 }();
274 
275 static const QPainterPath threeEdges = [] {
276     constexpr qreal d = 1.0 / 6.0;
277     QPainterPath path(QPointF(5 * d, 0));
278     path.arcTo(QRectF(QPointF(5 * d, -d), QSizeF(2 * d, 2 * d)), 180, 90);
279     path.lineTo(6 * d, 5 * d);
280     path.arcTo(QRectF(QPointF(5 * d, 5 * d), QSizeF(2 * d, 2 * d)), 90, 90);
281     path.lineTo(d, 6 * d);
282     path.lineTo(d, 0);
283     path.closeSubpath();
284     return path;
285 }();
286 
287 static const QPainterPath fourEdges = [] {
288     constexpr qreal d = 1.0 / 6.0;
289     QPainterPath path(QPointF(5 * d, 0));
290     path.arcTo(QRectF(QPointF(5 * d, -d), QSizeF(2 * d, 2 * d)), 180, 90);
291     path.lineTo(6 * d, 5 * d);
292     path.arcTo(QRectF(QPointF(5 * d, 5 * d), QSizeF(2 * d, 2 * d)), 90, 90);
293     path.lineTo(d, 6 * d);
294     path.arcTo(QRectF(QPointF(-d, 5 * d), QSizeF(2 * d, 2 * d)), 0, 90);
295     path.lineTo(0, d);
296     path.arcTo(QRectF(QPointF(-d, -d), QSizeF(2 * d, 2 * d)), -90, 90);
297     path.closeSubpath();
298     return path;
299 }();
300 
301 #else   // Draw edge Wang sets as abstract triangles
302 
303 static const QPainterPath oneEdge = [] {
304     QPainterPath path(QPointF(0.0, 0.0));
305     path.lineTo(QPointF(1.0, 0.0));
306     path.lineTo(QPointF(0.5, 0.5));
307     path.closeSubpath();
308     return path;
309 }();
310 
311 static const QPainterPath twoAdjacentEdges = [] {
312     QPainterPath path(QPointF(0.0, 0.0));
313     path.lineTo(QPointF(1.0, 0.0));
314     path.lineTo(QPointF(1.0, 1.0));
315     path.closeSubpath();
316     return path;
317 }();
318 
319 static const QPainterPath twoOppositeEdges = [] {
320     QPainterPath path(QPointF(0.0, 0.0));
321     path.lineTo(QPointF(1.0, 0.0));
322     path.lineTo(QPointF(0.5, 0.5));
323     path.lineTo(QPointF(1.0, 1.0));
324     path.lineTo(QPointF(0.0, 1.0));
325     path.lineTo(QPointF(0.5, 0.5));
326     path.closeSubpath();
327     return path;
328 }();
329 
330 static const QPainterPath threeEdges = [] {
331     QPainterPath path(QPointF(0.0, 0.0));
332     path.lineTo(QPointF(1.0, 0.0));
333     path.lineTo(QPointF(1.0, 1.0));
334     path.lineTo(QPointF(0.0, 1.0));
335     path.lineTo(QPointF(0.5, 0.5));
336     path.closeSubpath();
337     return path;
338 }();
339 
340 static const QPainterPath fourEdges = [] {
341     QPainterPath path;
342     path.addRect(0, 0, 1, 1);
343     return path;
344 }();
345 
346 #endif
347 
348 static const QPainterPath *pathForMask(WangId mask)
349 {
350     static const PathWithMask edgesWithMasks[] = {
351         { fourEdges, WangId::MaskEdges },
352         { threeEdges, threeEdgesMask },
353         { rotated(threeEdges, 1), threeEdgesMask.rotated(1) },
354         { rotated(threeEdges, 2), threeEdgesMask.rotated(2) },
355         { rotated(threeEdges, 3), threeEdgesMask.rotated(3) },
356         { twoAdjacentEdges, twoAdjacentEdgesMask },
357         { rotated(twoAdjacentEdges, 1), twoAdjacentEdgesMask.rotated(1) },
358         { rotated(twoAdjacentEdges, 2), twoAdjacentEdgesMask.rotated(2) },
359         { rotated(twoAdjacentEdges, 3), twoAdjacentEdgesMask.rotated(3) },
360         { twoOppositeEdges, twoOppositeEdgesMask },
361         { rotated(twoOppositeEdges, 1), twoOppositeEdgesMask.rotated(1) },
362         { oneEdge, oneEdgeMask },
363         { rotated(oneEdge, 1), oneEdgeMask.rotated(1) },
364         { rotated(oneEdge, 2), oneEdgeMask.rotated(2) },
365         { rotated(oneEdge, 3), oneEdgeMask.rotated(3) },
366     };
367 
368     for (auto &pathWithMask : edgesWithMasks)
369         if (mask == pathWithMask.mask)
370             return &pathWithMask.path;
371     return nullptr;
372 }
373 
374 } // namespace EdgesOnly
375 
376 #endif
377 
378 namespace CornersOnly {
379 
380 #if 0   // Use larger corners for corner-only sets
381 
382 static const QPainterPath oneCorner = [] {
383     QPainterPath path(QPointF(0.5, 0));
384     path.arcTo(QRectF(QPointF(0.5, -0.5), QSizeF(1, 1)), 180, 90);
385     path.lineTo(1, 0);
386     path.closeSubpath();
387     return path;
388 }();
389 
390 static const QPainterPath twoAdjacentCorners = [] {
391     QPainterPath path;
392     path.addRect(0.5, 0, 0.5, 1);
393     return path;
394 }();
395 
396 static const QPainterPath twoOppositeCorners = [] {
397     QPainterPath path = oneCorner;
398     path |= rotated(oneCorner, 2);
399     return path;
400 }();
401 
402 static const QPainterPath threeCorners = [] {
403     QPainterPath path(QPointF(1, 0));
404     path.lineTo(1, 1);
405     path.lineTo(0, 1);
406     path.lineTo(0, 0.5);
407     path.arcTo(QRectF(QPointF(-0.5, -0.5), QSizeF(1, 1)), -90, 90);
408     path.closeSubpath();
409     return path;
410 }();
411 
412 #else
413 
414 using EdgesAndCorners::oneCorner;
415 
__anon930826070f02null416 static const QPainterPath twoAdjacentCorners = [] {
417     constexpr qreal d = 1.0 / 6.0;
418     QPainterPath path;
419     path.addRect(4 * d, 0, 2 * d, 1);
420     return path;
421 }();
422 
423 using EdgesAndCorners::twoOppositeCorners;
424 
__anon930826071002null425 static const QPainterPath threeCorners = [] {
426     constexpr qreal d = 1.0 / 6.0;
427     QPainterPath path(QPointF(1, 0));
428     path.lineTo(1, 1);
429     path.lineTo(0, 1);
430     path.lineTo(0, 4 * d);
431     path.lineTo(2 * d, 4 * d);
432     path.arcTo(QRectF(QPointF(2 * d, 2 * d), QSizeF(2 * d, 2 * d)), -90, 90);
433     path.lineTo(4 * d, 0);
434     path.closeSubpath();
435     return path;
436 }();
437 
438 #endif
439 
__anon930826071102null440 static const QPainterPath fourCorners = [] {
441     QPainterPath path;
442     path.addRect(0, 0, 1, 1);
443     return path;
444 }();
445 
pathForMask(WangId mask)446 static const QPainterPath *pathForMask(WangId mask)
447 {
448     static const PathWithMask cornersWithMasks[] = {
449         { fourCorners, WangId::MaskCorners },
450         { threeCorners, threeCornersMask },
451         { rotated(threeCorners, 1), threeCornersMask.rotated(1) },
452         { rotated(threeCorners, 2), threeCornersMask.rotated(2) },
453         { rotated(threeCorners, 3), threeCornersMask.rotated(3) },
454         { twoAdjacentCorners, twoAdjacentCornersMask },
455         { rotated(twoAdjacentCorners, 1), twoAdjacentCornersMask.rotated(1) },
456         { rotated(twoAdjacentCorners, 2), twoAdjacentCornersMask.rotated(2) },
457         { rotated(twoAdjacentCorners, 3), twoAdjacentCornersMask.rotated(3) },
458         { twoOppositeCorners, twoOppositeCornersMask },
459         { rotated(twoOppositeCorners, 1), twoOppositeCornersMask.rotated(1) },
460         { oneCorner, oneCornerMask },
461         { rotated(oneCorner, 1), oneCornerMask.rotated(1) },
462         { rotated(oneCorner, 2), oneCornerMask.rotated(2) },
463         { rotated(oneCorner, 3), oneCornerMask.rotated(3) },
464     };
465 
466     for (auto &pathWithMask : cornersWithMasks)
467         if (mask == pathWithMask.mask)
468             return &pathWithMask.path;
469     return nullptr;
470 }
471 
472 } // namespace CornersOnly
473 
setCosmeticPen(QPainter * painter,const QBrush & brush,qreal width)474 static void setCosmeticPen(QPainter *painter, const QBrush &brush, qreal width)
475 {
476     const qreal devicePixelRatio = painter->device()->devicePixelRatioF();
477     QPen pen(brush, width * devicePixelRatio);
478     pen.setCosmetic(true);
479     painter->setPen(pen);
480 }
481 
paintWangOverlay(QPainter * painter,WangId wangId,const WangSet & wangSet,const QRect & rect,WangOverlayOptions options)482 void paintWangOverlay(QPainter *painter,
483                       WangId wangId,
484                       const WangSet &wangSet,
485                       const QRect &rect,
486                       WangOverlayOptions options)
487 {
488     if (!wangId)
489         return;
490 
491     const QRect adjustedRect = rect.adjusted(2, 2, -2, -2);
492     if (adjustedRect.isEmpty())
493         return;
494 
495     const qreal fillOpacity = options.testFlag(WO_TransparentFill) ? 0.3 : 1.0;
496     const qreal penWidth = qMin(2.0, adjustedRect.width() / 16.0);
497 
498     painter->save();
499     painter->setClipRect(rect);
500     painter->setRenderHint(QPainter::Antialiasing);
501 
502     QTransform foregroundTransform = painter->transform();
503     foregroundTransform.translate(adjustedRect.left(), adjustedRect.top());
504 
505     QTransform shadowTransform = foregroundTransform;
506     shadowTransform.translate(1, 1);
507 
508     shadowTransform.scale(adjustedRect.width(), adjustedRect.height());
509     foregroundTransform.scale(adjustedRect.width(), adjustedRect.height());
510 
511     if (!options.testFlag(WO_Outline))
512         painter->setPen(Qt::NoPen);
513 
514     auto paintColor = [&] (const WangId mask, const QColor &color) {
515         const QPainterPath *cornerPath = nullptr;
516         const QPainterPath *edgePath = nullptr;
517 
518         switch (wangSet.type()) {
519         case WangSet::Corner:
520         case WangSet::Edge:
521             // One of these should be nullptr, but if it isn't we may want to
522             // see that the Wang set is a little messed up.
523             cornerPath = CornersOnly::pathForMask(mask & WangId::MaskCorners);
524             edgePath = EdgesAndCorners::edgePathForMask(mask & WangId::MaskEdges);
525             break;
526         case WangSet::Mixed:
527             cornerPath = EdgesAndCorners::cornerPathForMask(mask & WangId::MaskCorners);
528             edgePath = EdgesAndCorners::edgePathForMask(mask & WangId::MaskEdges);
529             break;
530         }
531 
532         // Draw the shadow
533         if (options.testFlag(WO_Shadow)) {
534             painter->setBrush(Qt::NoBrush);
535 
536             if (options.testFlag(WO_Outline))
537                 setCosmeticPen(painter, Qt::black, penWidth);
538 
539             painter->setTransform(shadowTransform);
540 
541             if (cornerPath)
542                 painter->drawPath(*cornerPath);
543             if (edgePath)
544                 painter->drawPath(*edgePath);
545         }
546 
547         // Draw the foreground
548         painter->setBrush(QColor(color.red(), color.green(), color.blue(),
549                                  color.alpha() * fillOpacity));
550 
551         if (options.testFlag(WO_Outline)) {
552             if (options.testFlag(WO_TransparentFill))
553                 setCosmeticPen(painter, color, penWidth);
554             else
555                 setCosmeticPen(painter, Qt::black, penWidth);
556         }
557 
558         painter->setTransform(foregroundTransform);
559 
560         if (cornerPath)
561             painter->drawPath(*cornerPath);
562         if (edgePath)
563             painter->drawPath(*edgePath);
564     };
565 
566     for (int color = 1; color <= wangSet.colorCount(); ++color) {
567         const WangId mask = wangId.mask(color);
568         if (!mask)
569             continue;
570         paintColor(mask, wangSet.colorAt(color)->color());
571     }
572 
573     const WangId mask = wangId.mask(WangId::INDEX_MASK);
574     if (mask) {
575         const QColor maskColor = QGuiApplication::palette().color(QPalette::Highlight);
576         paintColor(mask, maskColor);
577     }
578 
579     painter->restore();
580 }
581 
paintWangSetIcon(WangSet::Type type)582 static QIcon paintWangSetIcon(WangSet::Type type)
583 {
584     static const auto iconSize = Utils::dpiScaled(QSize(32, 32));
585 
586     QPixmap pixmap(iconSize);
587     pixmap.fill(Qt::transparent);
588 
589     QPainter painter(&pixmap);
590 
591     WangSet wangSet(nullptr, QString(), type);
592     wangSet.setColorCount(2);
593 
594     WangId wangId;
595 
596     switch (type) {
597     case WangSet::Corner:
598         wangId.setIndexColor(WangId::TopRight, 2);
599         wangId.setIndexColor(WangId::BottomRight, 1);
600         wangId.setIndexColor(WangId::BottomLeft, 2);
601         wangId.setIndexColor(WangId::TopLeft, 1);
602         break;
603     case WangSet::Edge:
604         wangId.setIndexColor(WangId::Top, 1);
605         wangId.setIndexColor(WangId::Right, 2);
606         wangId.setIndexColor(WangId::Bottom, 1);
607         wangId.setIndexColor(WangId::Left, 2);
608         break;
609     case WangSet::Mixed:
610         wangId.setIndexColor(WangId::Top, 1);
611         wangId.setIndexColor(WangId::TopRight, 2);
612         wangId.setIndexColor(WangId::Right, 1);
613         wangId.setIndexColor(WangId::BottomRight, 2);
614         wangId.setIndexColor(WangId::Bottom, 1);
615         wangId.setIndexColor(WangId::BottomLeft, 2);
616         wangId.setIndexColor(WangId::Left, 1);
617         wangId.setIndexColor(WangId::TopLeft, 2);
618         break;
619     }
620 
621     paintWangOverlay(&painter, wangId, wangSet, pixmap.rect(),
622                      WO_Shadow | WO_Outline);
623 
624     return QIcon(pixmap);
625 }
626 
wangSetIcon(WangSet::Type type)627 QIcon wangSetIcon(WangSet::Type type)
628 {
629     switch (type) {
630     case WangSet::Corner: {
631         static QIcon icon = paintWangSetIcon(type);
632         return icon;
633     }
634     case WangSet::Edge: {
635         static QIcon icon = paintWangSetIcon(type);
636         return icon;
637     }
638     case WangSet::Mixed: {
639         static QIcon icon = paintWangSetIcon(type);
640         return icon;
641     }
642     }
643 
644     return QIcon();
645 }
646 
647 } // namespace Tiled
648