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