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