1 /*
2 * Copyright 2012, 2013 Thomas Schöps
3 * Copyright 2012-2017 Kai Pastor
4 *
5 * This file is part of OpenOrienteering.
6 *
7 * OpenOrienteering is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * OpenOrienteering is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifndef OPENORIENTEERING_RENDERABLE_H
22 #define OPENORIENTEERING_RENDERABLE_H
23
24 #include <map>
25 #include <vector>
26
27 #include <QtGlobal>
28 #include <QFlags>
29 #include <QRectF>
30 #include <QSharedData>
31 #include <QExplicitlySharedDataPointer>
32
33 #include "core/map_color.h"
34
35 class QColor;
36 class QPainter;
37 class QPainterPath;
38 // IWYU pragma: no_forward_declare QRectF
39
40 namespace OpenOrienteering {
41
42 class Map;
43 class Object;
44 class PainterConfig;
45
46
47 /**
48 * This class contains rendering configuration values.
49 *
50 * A reference to an object of this class replaces what used to be part of the
51 * parameter list of various draw()/render() methods. With that old approach,
52 * each new rendering configuration option required changes in many signatures
53 * and function calls. In addition, the boolean options were not verbose at all.
54 *
55 * Objects are meant to be initialized by initializer lists (C++11).
56 */
57 class RenderConfig
58 {
59 public:
60 /**
61 * Flags indicating particular rendering configuration options.
62 */
63 enum Option
64 {
65 Screen = 1<<0, ///< Indicates that the drawing is for the screen.
66 /// Can turn on optimizations which result in slightly
67 /// lower display quality (e.g. disable antialiasing
68 /// for texts) for the benefit of speed.
69 DisableAntialiasing = 1<<1, ///< Forces disabling of Antialiasing.
70 ForceMinSize = 1<<2, ///< Forces a minimum size of app. 1 pixel for objects.
71 /// Makes maps look better at small zoom levels without antialiasing.
72 HelperSymbols = 1<<3, ///< Activates display of symbols with the "helper symbol" flag.
73 Highlighted = 1<<4, ///< Makes the color appear highlighted.
74 RequireSpotColor = 1<<5, ///< Skips colors which do not have a spot color definition.
75 Tool = Screen | ForceMinSize | HelperSymbols, ///< The recommended flags for tools.
76 NoOptions = 0 ///< No option activated.
77 };
78
79 /**
80 * \class RenderConfig::Options
81 *
82 * A combination of flags for rendering configuration options.
83 *
84 * \see QFlags::testFlag(), RenderConfig::Option
85 */
86 Q_DECLARE_FLAGS(Options, Option)
87
88 const Map& map; ///< The map.
89
90 QRectF bounding_box; ///< The bounding box of the area to be drawn.
91 /// Given in map coordinates.
92
93 qreal scaling; ///< The scaling expressed as pixels per mm.
94 /// Used to calculate the final object sizes when
95 /// ForceMinSize is set.
96
97 Options options; ///< The rendering options.
98
99 qreal opacity; ///< The opacity.
100
101 /**
102 * A convenience method for testing flags in the options value.
103 *
104 * \see QFlags::testFlag()
105 */
106 bool testFlag(const Option flag) const;
107 };
108
109
110
111 /**
112 * A Renderable is a graphical item with a simple shape and a single color.
113 *
114 * This is the abstract base class. Inheriting classes must implement the
115 * abstract methods, and they must set the extent during construction.
116 */
117 class Renderable // clazy:exclude=copyable-polymorphic
118 {
119 protected:
120 /** The constructor for new renderables. */
121 explicit Renderable(const MapColor* color);
122
123 public:
124 Renderable(const Renderable&) = delete;
125 Renderable(Renderable&&) = delete;
126
127 /**
128 * The destructor.
129 */
130 virtual ~Renderable();
131
132 Renderable& operator=(const Renderable&) = delete;
133 Renderable& operator=(Renderable&&) = delete;
134
135 /**
136 * Returns the extent (bounding box).
137 */
138 const QRectF& getExtent() const;
139
140 /**
141 * Tests whether the renderable's extent intersects the given rect.
142 */
143 bool intersects(const QRectF& rect) const;
144
145 /**
146 * Returns the painter configuration information.
147 *
148 * This configuration must be set when rendering this renderable.
149 */
150 virtual PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const = 0;
151
152 /**
153 * Renders the renderable with the given painter and rendering configuration.
154 */
155 virtual void render(QPainter& painter, const RenderConfig& config) const = 0;
156
157 protected:
158 /** The color priority is a major attribute and cannot be modified. */
159 const int color_priority;
160
161 /** The extent must be set by inheriting classes. */
162 QRectF extent;
163 };
164
165
166
167 /**
168 * PainterConfig contains painter configuration information.
169 *
170 * When painting a renderable item, the QPainter shall be configured according
171 * to this information.
172 *
173 * A PainterConfig is an immutable values, constructed with initializer lists.
174 */
175 class PainterConfig
176 {
177 public:
178 enum PainterMode
179 {
180 BrushOnly = 0, ///< Render using the brush only.
181 PenOnly = 1, ///< Render using the pen only.
182 Reserved = -1 ///< Not used.
183 };
184
185 const int color_priority; ///< The color priority which determines rendering order
186 const PainterMode mode; ///< The mode of painting
187 const qreal pen_width; ///< The width of the pen
188 const QPainterPath* clip_path; ///< A clip_path which may be shared by several Renderables
189
190 /**
191 * Activates the configuration on the given painter.
192 *
193 * If this method returns false, the corresponding renderables shall not be drawn.
194 *
195 * @param painter The painter to be configured.
196 * @param current_clip A pointer which will be set to the address of the current clip,
197 * in order to avoid switching the clip area unnecessarily.
198 * @param config The rendering configurations.
199 * @param color The QColor to be used for the pen or brush.
200 * @param initial_clip The clip which was set initially for this painter.
201 * @return True if the configuration was activated, false if the corresponding renderables shall not be drawn.
202 */
203 bool activate(QPainter* painter, const QPainterPath*& current_clip, const RenderConfig& config, const QColor& color, const QPainterPath& initial_clip) const;
204
205 friend bool operator==(const PainterConfig& lhs, const PainterConfig& rhs);
206 friend bool operator<(const PainterConfig& lhs, const PainterConfig& rhs);
207 };
208
209 /**
210 * Returns true if the configurations are equal.
211 */
212 bool operator==(const PainterConfig& lhs, const PainterConfig& rhs);
213
214 /**
215 * Returns true if the configurations are not equal.
216 */
217 bool operator!=(const PainterConfig& lhs, const PainterConfig& rhs);
218
219 /**
220 * Defines an order over values which are not equal.
221 */
222 bool operator<(const PainterConfig& lhs, const PainterConfig& rhs);
223
224
225
226 /**
227 * A low-level container for renderables.
228 */
229 typedef std::vector<Renderable*> RenderableVector;
230
231
232
233 /**
234 * A shared high-level container for renderables
235 * grouped by common render attributes.
236 *
237 * This shared container can be used in different collections. When the last
238 * reference to this container is dropped, it will delete the renderables.
239 */
240 class SharedRenderables : public QSharedData, public std::map< PainterConfig, RenderableVector >
241 {
242 public:
243 typedef QExplicitlySharedDataPointer<SharedRenderables> Pointer;
244 SharedRenderables() = default;
245 SharedRenderables(const SharedRenderables&) = delete;
246 SharedRenderables& operator=(const SharedRenderables&) = delete;
247 ~SharedRenderables();
248 void deleteRenderables();
249 void compact(); // release memory which is occupied by unused PainterConfig, FIXME: maybe call this regularly...
250 };
251
252
253
254 /**
255 * A high-level container for all renderables of a single object,
256 * grouped by color priority and common render attributes.
257 */
258 class ObjectRenderables : protected std::map<int, SharedRenderables::Pointer>
259 {
260 friend class MapRenderables;
261 public:
262 ObjectRenderables(Object& object);
263 ObjectRenderables(const ObjectRenderables&) = delete;
264 ObjectRenderables& operator=(const ObjectRenderables&) = delete;
265 ~ObjectRenderables();
266
267 inline void insertRenderable(Renderable* r);
268 void insertRenderable(Renderable* r, const PainterConfig& state);
269
270 void clear();
271 void deleteRenderables();
272 void takeRenderables();
273
274 /**
275 * Draws all renderables matching the given map color with the given color.
276 *
277 * If map_color is -1, this functions draws all renderables of color which
278 * are not in the list of map colors (i.e. objects with undefined symbol).
279 * Used by FillTool to encode object IDs as colors.
280 */
281 void draw(int map_color, const QColor& color, QPainter* painter, const RenderConfig& config) const;
282
283 void setClipPath(const QPainterPath* path);
284 const QPainterPath* getClipPath() const;
285
286 const QRectF& getExtent() const;
287
288 private:
289 QRectF& extent;
290 const QPainterPath* clip_path = nullptr; // no memory management here!
291 };
292
293
294
295 /**
296 * A low-level container for renderables of multiple objects
297 * grouped by object and common render attributes.
298 *
299 * This container uses a smart pointer to the renderable collection
300 * of each single object.
301 */
302 typedef std::map<const Object*, SharedRenderables::Pointer> ObjectRenderablesMap;
303
304
305
306 /**
307 * A high-level container for renderables of multiple objects
308 * grouped by color priority, object and common render attributes.
309 *
310 * This container is able to draw the renderables.
311 */
312 class MapRenderables : protected std::map<int, ObjectRenderablesMap>
313 {
314 public:
315 /**
316 * An Object deleter which takes care of removing the renderables of the object.
317 *
318 * Synopsis:
319 * std::unique_ptr<Object, MapRenderables::ObjectDeleter> object { nullptr, { renderables } };
320 */
321 class ObjectDeleter
322 {
323 public:
324 MapRenderables& renderables;
325 void operator()(Object* object) const;
326 };
327
328
329 MapRenderables(Map* map);
330
331 /**
332 * Draws the renderables normally (one opaque over the other).
333 *
334 * @param painter The QPainter used for drawing.
335 * @param config The rendering configuration
336 */
337 void draw(QPainter* painter, const RenderConfig& config) const;
338
339 /**
340 * Draws the renderables in a spot color overprinting simulation.
341 *
342 * @param painter Must be a QPainter on a QImage of Format_ARGB32_Premultiplied.
343 * @param config The rendering configuration
344 */
345 void drawOverprintingSimulation(QPainter* painter, const RenderConfig& config) const;
346
347 /**
348 * Draws only the renderables which belong to a particular spot color.
349 *
350 * Separations are normally drawn in levels of gray where black means
351 * full tone of the spot color. The parameter use_color can be used to
352 * draw in the actual spot color instead.
353 *
354 * @param painter The QPainter used for drawing.
355 * @param config The rendering configuration
356 * @param separation The spot color to draw the separation for.
357 * @param use_color If true, forces the separation to be drawn in its actual color.
358 */
359 void drawColorSeparation(QPainter* painter, const RenderConfig& config,
360 const MapColor* separation, bool use_color = false) const;
361
362 void insertRenderablesOfObject(const Object* object);
363
364 /* NOTE: does not delete the renderables, just removes them from display */
365 void removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty);
366
367 void clear(bool mark_area_as_dirty = false);
368
369 inline bool empty() const;
370
371 private:
372 Map* const map;
373 };
374
375
376
377 // ### RenderConfig ###
378
379 inline
testFlag(const RenderConfig::Option flag)380 bool RenderConfig::testFlag(const RenderConfig::Option flag) const
381 {
382 return options.testFlag(flag);
383 }
384
385
386
387 // ### Renderable ###
388
389 inline
Renderable(const MapColor * color)390 Renderable::Renderable(const MapColor* color)
391 : color_priority(color ? color->getPriority() : MapColor::Reserved)
392 {
393 ; // nothing
394 }
395
396 inline
getExtent()397 const QRectF&Renderable::getExtent() const
398 {
399 return extent;
400 }
401
402 inline
intersects(const QRectF & rect)403 bool Renderable::intersects(const QRectF& rect) const
404 {
405 return extent.intersects(rect);
406 }
407
408
409
410 // ### PainterConfig ###
411
412 inline
413 bool operator==(const PainterConfig& lhs, const PainterConfig& rhs)
414 {
415 return (lhs.color_priority == rhs.color_priority) &&
416 (lhs.mode == rhs.mode) &&
417 (lhs.pen_width == rhs.pen_width || lhs.mode == PainterConfig::BrushOnly) &&
418 (lhs.clip_path != rhs.clip_path);
419 }
420
421 inline
422 bool operator!=(const PainterConfig& lhs, const PainterConfig& rhs)
423 {
424 return !(lhs == rhs);
425 }
426
427 inline
428 bool operator<(const PainterConfig& lhs, const PainterConfig& rhs)
429 {
430 // First, decide by priority
431 if (lhs.color_priority != rhs.color_priority)
432 return lhs.color_priority > rhs.color_priority;
433
434 // Same priority, decide by clip path
435 else if (lhs.clip_path != rhs.clip_path)
436 return lhs.clip_path > rhs.clip_path;
437
438 // Same clip path, decide by mode
439 else if ((int)lhs.mode != (int)rhs.mode)
440 return (int)lhs.mode > (int)rhs.mode;
441
442 // Same mode, decide by pen width
443 else
444 return lhs.pen_width < rhs.pen_width;
445 }
446
447
448
449 // ### ObjectRenderables ###
450
451 inline
insertRenderable(Renderable * r)452 void ObjectRenderables::insertRenderable(Renderable* r)
453 {
454 insertRenderable(r, r->getPainterConfig(clip_path));
455 }
456
457 inline
getClipPath()458 const QPainterPath* ObjectRenderables::getClipPath() const
459 {
460 return clip_path;
461 }
462
463 inline
getExtent()464 const QRectF &ObjectRenderables::getExtent() const
465 {
466 return extent;
467 }
468
469
470
471 // ### MapRenderables ###
472
473 inline
empty()474 bool MapRenderables::empty() const
475 {
476 return std::map<int, ObjectRenderablesMap>::empty();
477 }
478
479
480 } // namespace OpenOrienteering
481
482
483 Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::RenderConfig::Options)
484
485
486 #endif
487