neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
primitives.hpp
Go to the documentation of this file.
1// graphics_context.hpp
2/*
3 neogfx C++ GUI Library
4 Copyright (c) 2015 Leigh Johnston. All Rights Reserved.
5
6 This program is free software: you can redistribute it and / or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#pragma once
21
22#include <neogfx/neogfx.hpp>
23#include <memory>
24#ifdef _WIN32
25#pragma warning( push )
26#pragma warning( disable: 4459 ) // declaration of 'name' hides global declaration
27#endif
28#include <boost/multi_array.hpp>
29#ifdef _WIN32
30#pragma warning( pop )
31#endif
32#include <optional>
35#include <neogfx/gfx/path.hpp>
38#include <neogfx/gfx/pen.hpp>
39
40namespace neogfx
41{
42 enum class blending_mode
43 {
44 None,
45 Default, // todo
46 Blit
47 };
48
49 enum class smoothing_mode
50 {
51 None,
53 };
54
56 {
57 None,
58 Xor
59 };
60
61 enum class shader_effect
62 {
63 None = 0,
64 Colorize = 1,
67 ColorizeSpot = 3,
68 ColorizeAlpha = 4,
69 Monochrome = 5,
70 Filter = 10,
71 Ignore = 99
72 };
73
74 enum class shader_filter
75 {
76 None = 0,
77 GaussianBlur = 1
78 };
79
80 enum class shader_shape
81 {
82 None = 0x00,
83 Line = 0x01,
84 CubicBezier = 0x02,
85 Triangle = 0x03,
86 Rect = 0x04,
87 Circle = 0x05,
88 Ellipse = 0x06,
89 Pie = 0x07,
90 Arc = 0x08,
91 RoundedRect = 0x09
92 };
93
95 {
96 None,
98 };
99
101
102 inline brush to_brush(const color_or_gradient& aEffectColor)
103 {
104 if (std::holds_alternative<color>(aEffectColor))
105 return std::get<color>(aEffectColor);
106 else if (std::holds_alternative<gradient>(aEffectColor))
107 return std::get<gradient>(aEffectColor);
108 else
109 return color{};
110 }
111
113 {
114 public:
115 using color_or_gradient::color_or_gradient;
117 color_or_gradient{ aOther }
118 {
119 }
120 public:
121 using color_or_gradient::operator=;
123 {
125 return *this;
126 }
127 public:
129 {
130 if (std::holds_alternative<color>(*this))
131 return std::get<color>(*this).alpha();
132 else
133 return 255;
134 }
136 {
137 if (std::holds_alternative<color>(*this))
138 return std::get<color>(*this).with_alpha(aAlpha);
139 if (std::holds_alternative<gradient>(*this))
140 return std::get<gradient>(*this).with_combined_alpha(aAlpha);
141 else
142 return text_color{};
143 }
144 };
145
146 inline bool operator==(color_or_gradient const& lhs, color const& rhs) noexcept
147 {
148 return std::holds_alternative<color>(lhs) && std::get<color>(lhs) == rhs;
149 }
150
151 inline bool operator==(color const& lhs, color_or_gradient const& rhs) noexcept
152 {
153 return std::holds_alternative<color>(rhs) && std::get<color>(rhs) == lhs;
154 }
155
156 inline bool operator!=(color_or_gradient const& lhs, color const& rhs) noexcept
157 {
158 return !std::holds_alternative<color>(lhs) || std::get<color>(lhs) != rhs;
159 }
160
161 inline bool operator!=(color const& lhs, color_or_gradient const& rhs) noexcept
162 {
163 return !std::holds_alternative<color>(rhs) && std::get<color>(rhs) != lhs;
164 }
165
166 inline bool operator==(color_or_gradient const& lhs, gradient const& rhs) noexcept
167 {
168 return std::holds_alternative<gradient>(lhs) && std::get<gradient>(lhs) == rhs;
169 }
170
171 inline bool operator==(gradient const& lhs, color_or_gradient const& rhs) noexcept
172 {
173 return std::holds_alternative<gradient>(rhs) && std::get<gradient>(rhs) == lhs;
174 }
175
176 inline bool operator!=(color_or_gradient const& lhs, gradient const& rhs) noexcept
177 {
178 return !std::holds_alternative<gradient>(lhs) || std::get<gradient>(lhs) != rhs;
179 }
180
181 inline bool operator!=(gradient const& lhs, color_or_gradient const& rhs) noexcept
182 {
183 return !std::holds_alternative<gradient>(rhs) && std::get<gradient>(rhs) != lhs;
184 }
185
187
188 enum class text_effect_type : uint32_t
189 {
190 None,
191 Outline,
192 Glow,
193 Shadow
194 };
195}
196
203
204namespace neogfx
205{
206 class text_effect
207 {
208 public:
209 typedef text_effect abstract_type; // todo
210 public:
211 typedef double auxiliary_parameter;
212 typedef optional<auxiliary_parameter> optional_auxiliary_parameter;
213 public:
214 text_effect() :
215 iType{ text_effect_type::None }, iColor{}, iWidth{}, iAux1{}, iIgnoreEmoji{}
216 {
217 }
218 text_effect(text_effect_type aType, const text_color& aColor, const optional_dimension& aWidth = {}, const optional_vec3& aOffset = {}, const optional_auxiliary_parameter& aAux1 = {}, bool aIgnoreEmoji = false) :
219 iType{ aType }, iColor{ aColor }, iWidth{ aWidth }, iAux1{ aAux1 }, iIgnoreEmoji{ aIgnoreEmoji }
220 {
221 }
222 public:
223 text_effect& operator=(const text_effect& aOther)
224 {
225 if (&aOther == this)
226 return *this;
227 iType = aOther.iType;
228 iColor = aOther.iColor;
229 iWidth = aOther.iWidth;
230 iOffset = aOther.iOffset;
231 iAux1 = aOther.iAux1;
232 iIgnoreEmoji = aOther.iIgnoreEmoji;
233 return *this;
234 }
235 public:
236 bool operator==(const text_effect& that) const noexcept
237 {
238 return std::forward_as_tuple(iType, iColor, iWidth, iOffset, iAux1, iIgnoreEmoji) ==
239 std::forward_as_tuple(that.iType, that.iColor, that.iWidth, that.iOffset, that.iAux1, that.iIgnoreEmoji);
240 }
241 auto operator<=>(const text_effect& that) const noexcept
242 {
243 return std::forward_as_tuple(iType, iColor, iWidth, iOffset, iAux1, iIgnoreEmoji) <=>
244 std::forward_as_tuple(that.iType, that.iColor, that.iWidth, that.iOffset, that.iAux1, that.iIgnoreEmoji);
245 }
246 public:
247 text_effect_type type() const
248 {
249 return iType;
250 }
251 void set_type(text_effect_type aType)
252 {
253 iType = aType;
254 }
255 text_color color() const
256 {
257 if (iColor != neolib::none)
258 return iColor;
259 switch (type())
260 {
261 case text_effect_type::Shadow:
262 return color::Black.with_alpha(0.5);
263 default:
264 return color::White;
265 }
266 }
267 void set_color(text_color const& aColor)
268 {
269 iColor = aColor;
270 }
271 dimension width() const
272 {
273 if (iWidth != std::nullopt)
274 return *iWidth;
275 switch (type())
276 {
277 case text_effect_type::None:
278 default:
279 return 0.0;
280 case text_effect_type::Outline:
281 return 1.0;
282 case text_effect_type::Glow:
283 case text_effect_type::Shadow:
284 return 4.0;
285 }
286 }
287 void set_width(optional_dimension const& aWidth)
288 {
289 iWidth = aWidth;
290 }
291 vec3 offset() const
292 {
293 if (iOffset != std::nullopt)
294 return *iOffset;
295 switch (type())
296 {
297 case text_effect_type::None:
298 default:
299 return {};
300 case text_effect_type::Outline:
301 return { -width(), -width() };
302 case text_effect_type::Glow:
303 return {};
304 case text_effect_type::Shadow:
305 return { std::max(1.0, width() / 2.0), std::max(1.0, width() / 2.0) };
306 }
307 }
308 void set_offset(vec3 const& aOffset)
309 {
310 iOffset = aOffset;
311 }
312 double aux1() const
313 {
314 if (iAux1 != std::nullopt)
315 return *iAux1;
316 switch (type())
317 {
318 case text_effect_type::None:
319 default:
320 return 0.0;
321 case text_effect_type::Outline:
322 return 0.0;
323 case text_effect_type::Glow:
324 case text_effect_type::Shadow:
325 return 1.0;
326 }
327 }
328 void set_aux1(double aAux1)
329 {
330 iAux1 = aAux1;
331 }
332 bool ignore_emoji() const
333 {
334 return iIgnoreEmoji;
335 }
336 void set_ignore_emoji(bool aIgnore)
337 {
338 iIgnoreEmoji = aIgnore;
339 }
340 text_effect with_alpha(color::component aAlpha) const
341 {
342 return text_effect{ iType, iColor.with_alpha(aAlpha), iWidth, iOffset, iAux1, iIgnoreEmoji };
343 }
344 text_effect with_alpha(double aAlpha) const
345 {
346 return with_alpha(static_cast<color::component>(aAlpha * 255));
347 }
348 private:
349 text_effect_type iType;
350 text_color iColor;
351 optional_dimension iWidth;
352 optional_vec3 iOffset;
353 optional_auxiliary_parameter iAux1;
354 bool iIgnoreEmoji;
355 };
356
357 typedef neolib::optional<text_effect> optional_text_effect;
358
359 template <typename Elem, typename Traits>
360 inline std::basic_ostream<Elem, Traits>& operator<<(std::basic_ostream<Elem, Traits>& aStream, const text_effect& aEffect)
361 {
362 aStream << '[';
363 aStream << aEffect.type();
364 aStream << ',';
365 aStream << aEffect.color();
366 aStream << ',';
367 aStream << aEffect.width();
368 aStream << ',';
369 aStream << aEffect.offset();
370 aStream << ',';
371 aStream << aEffect.aux1();
372 aStream << ',';
373 aStream << aEffect.ignore_emoji();
374 aStream << ']';
375 return aStream;
376 }
377
378 template <typename Elem, typename Traits>
379 inline std::basic_istream<Elem, Traits>& operator>>(std::basic_istream<Elem, Traits>& aStream, text_effect& aEffect)
380 {
381 auto previousImbued = aStream.getloc();
382 if (typeid(std::use_facet<std::ctype<char>>(previousImbued)) != typeid(neolib::comma_and_brackets_as_whitespace))
383 aStream.imbue(std::locale{ previousImbued, new neolib::comma_and_brackets_as_whitespace{} });
384 text_effect_type type;
385 aStream >> type;
386 aEffect.set_type(type);
387 text_color color;
388 aStream >> color;
389 aEffect.set_color(color);
390 dimension width;
391 aStream >> width;
392 aEffect.set_width(width);
393 vec3 offset;
394 aStream >> offset;
395 aEffect.set_offset(offset);
396 double aux1;
397 aStream >> aux1;
398 aEffect.set_aux1(aux1);
399 bool ignoreEmoji;
400 aStream >> ignoreEmoji;
401 aEffect.set_ignore_emoji(ignoreEmoji);
402 aStream.imbue(previousImbued);
403 return aStream;
404 }
405
406 class text_format
407 {
408 public:
409 struct no_paper : std::logic_error { no_paper() : std::logic_error("neogfx::text_format::no_paper") {} };
410 struct no_effect : std::logic_error { no_effect() : std::logic_error("neogfx::text_format::no_effect") {} };
411 public:
412 typedef text_format abstract_type; // todo
413 public:
414 text_format() :
415 iSmartUnderline{ false },
416 iIgnoreEmoji{ true },
417 iOnlyCalculateEffect{ false },
418 iBeingFiltered{ false }
419 {
420 }
421 text_format(text_format const& aOther) :
422 iInk{ aOther.iInk },
423 iPaper{ aOther.iPaper },
424 iSmartUnderline{ aOther.iSmartUnderline },
425 iIgnoreEmoji{ aOther.iIgnoreEmoji },
426 iEffect{ aOther.iEffect },
427 iOnlyCalculateEffect{ aOther.iOnlyCalculateEffect },
428 iBeingFiltered{ aOther.iBeingFiltered }
429 {
430 }
431 template <typename InkType, typename PaperType>
432 text_format(InkType const& aInk, PaperType const& aPaper, optional_text_effect const& aEffect) :
433 iInk{ aInk },
434 iPaper{ aPaper },
435 iSmartUnderline{ false },
436 iIgnoreEmoji{ true },
437 iEffect{ aEffect },
438 iOnlyCalculateEffect{ false },
439 iBeingFiltered{ false }
440 {
441 }
442 template <typename InkType, typename PaperType>
443 text_format(InkType const& aInk, PaperType const& aPaper, text_effect const& aEffect) :
444 iInk{ aInk },
445 iPaper{ aPaper },
446 iSmartUnderline{ false },
447 iIgnoreEmoji{ true },
448 iEffect{ aEffect },
449 iOnlyCalculateEffect{ false },
450 iBeingFiltered{ false }
451 {
452 }
453 template <typename InkType>
454 text_format(InkType const& aInk, optional_text_effect const& aEffect) :
455 iInk{ aInk },
456 iSmartUnderline{ false },
457 iIgnoreEmoji{ true },
458 iEffect{ aEffect },
459 iOnlyCalculateEffect{ false },
460 iBeingFiltered{ false }
461 {
462 }
463 template <typename InkType>
464 text_format(InkType const& aInk, text_effect const& aEffect) :
465 iInk{ aInk },
466 iSmartUnderline{ false },
467 iIgnoreEmoji{ true },
468 iEffect{ aEffect },
469 iOnlyCalculateEffect{ false },
470 iBeingFiltered{ false }
471 {
472 }
473 template <typename InkType, typename PaperType>
474 text_format(InkType const& aInk, PaperType const& aPaper) :
475 iInk{ aInk },
476 iPaper{ aPaper },
477 iSmartUnderline{ false },
478 iIgnoreEmoji{ true },
479 iOnlyCalculateEffect{ false },
480 iBeingFiltered{ false }
481 {
482 }
483 template <typename InkType>
484 text_format(InkType const& aInk) :
485 iInk{ aInk },
486 iSmartUnderline{ false },
487 iIgnoreEmoji{ true },
488 iOnlyCalculateEffect{ false },
489 iBeingFiltered{ false }
490 {
491 }
492 public:
493 bool operator==(text_format const& aRhs) const noexcept
494 {
495 return std::forward_as_tuple(ink(), paper(), smart_underline(), ignore_emoji(), effect()) ==
496 std::forward_as_tuple(aRhs.ink(), aRhs.paper(), aRhs.smart_underline(), aRhs.ignore_emoji(), aRhs.effect());
497 }
498 auto operator<=>(text_format const& aRhs) const noexcept
499 {
500 return std::forward_as_tuple(ink(), paper(), smart_underline(), ignore_emoji(), effect()) <=>
501 std::forward_as_tuple(aRhs.ink(), aRhs.paper(), aRhs.smart_underline(), aRhs.ignore_emoji(), aRhs.effect());
502 }
503 public:
504 text_color const& ink() const
505 {
506 return iInk;
507 }
508 void set_ink(text_color const& aInk)
509 {
510 iInk = aInk;
511 }
512 optional_text_color const& paper() const
513 {
514 return iPaper;
515 }
516 void set_paper(optional_text_color const& aPaper)
517 {
518 iPaper = aPaper;
519 }
520 bool smart_underline() const
521 {
522 return iSmartUnderline;
523 }
524 void set_smart_underline(bool aSmartUnderline)
525 {
526 iSmartUnderline = aSmartUnderline;
527 }
528 bool ignore_emoji() const
529 {
530 return iIgnoreEmoji;
531 }
532 void set_ignore_emoji(bool aIgnore)
533 {
534 iIgnoreEmoji = aIgnore;
535 }
536 optional_text_effect const& effect() const
537 {
538 return iEffect;
539 }
540 optional_text_effect& effect()
541 {
542 return iEffect;
543 }
544 void set_effect(optional_text_effect const& aEffect)
545 {
546 iEffect = aEffect;
547 }
548 bool only_calculate_effect() const
549 {
550 return iOnlyCalculateEffect;
551 }
552 bool being_filtered() const
553 {
554 return iBeingFiltered;
555 }
556 public:
557 text_format with_ink(text_color const& aInk) const
558 {
559 return text_format{ aInk, iPaper, iEffect }.with_smart_underline(smart_underline()).with_emoji_ignored(ignore_emoji());
560 }
561 text_format with_paper(optional_text_color const& aPaper) const
562 {
563 return text_format{ iInk, aPaper, iEffect }.with_smart_underline(smart_underline()).with_emoji_ignored(ignore_emoji());
564 }
565 text_format with_smart_underline(bool aSmartUnderline) const
566 {
567 auto result = *this;
568 result.set_smart_underline(aSmartUnderline);
569 return result;
570 }
571 text_format with_emoji_ignored(bool aIgnored) const
572 {
573 auto result = *this;
574 result.set_ignore_emoji(aIgnored);
575 return result;
576 }
577 text_format with_effect(optional_text_effect const& aEffect) const
578 {
579 return text_format{ iInk, iPaper, aEffect }.with_smart_underline(smart_underline()).with_emoji_ignored(ignore_emoji());
580 }
581 text_format with_alpha(color::component aAlpha) const
582 {
583 return text_format{
584 iInk.with_alpha(aAlpha),
585 iPaper != std::nullopt ?
586 optional_text_color{ iPaper->with_alpha(aAlpha) } :
588 iEffect != std::nullopt ?
589 iEffect->with_alpha(aAlpha) :
590 optional_text_effect{} }.with_smart_underline(smart_underline()).with_emoji_ignored(ignore_emoji());
591 }
592 text_format with_alpha(double aAlpha) const
593 {
594 return with_alpha(static_cast<color::component>(aAlpha * 255));
595 }
596 text_format with_only_effect_calculation() const
597 {
598 auto copy = *this;
599 copy.iOnlyCalculateEffect = true;
600 return copy;
601 }
602 text_format as_being_filtered() const
603 {
604 auto copy = *this;
605 copy.iBeingFiltered = true;
606 return copy;
607 }
608 private:
609 text_color iInk;
610 optional_text_color iPaper;
611 bool iSmartUnderline;
612 bool iIgnoreEmoji;
613 optional_text_effect iEffect;
614 bool iOnlyCalculateEffect;
615 bool iBeingFiltered;
616 };
617
618 typedef neolib::optional<text_format> optional_text_format;
619
620 template <typename Elem, typename Traits>
621 inline std::basic_ostream<Elem, Traits>& operator<<(std::basic_ostream<Elem, Traits>& aStream, const text_format& aTextFormat)
622 {
623 aStream << "[";
624 aStream << aTextFormat.ink();
625 aStream << ",";
626 aStream << aTextFormat.paper();
627 aStream << ",";
628 aStream << aTextFormat.effect();
629 aStream << ",";
630 aStream << aTextFormat.smart_underline();
631 aStream << ",";
632 aStream << aTextFormat.ignore_emoji();
633 aStream << "]";
634 return aStream;
635 }
636
637 template <typename Elem, typename Traits>
638 inline std::basic_istream<Elem, Traits>& operator>>(std::basic_istream<Elem, Traits>& aStream, text_format& aTextFormat)
639 {
640 auto previousImbued = aStream.getloc();
641 if (typeid(std::use_facet<std::ctype<char>>(previousImbued)) != typeid(neolib::comma_and_brackets_as_whitespace))
642 aStream.imbue(std::locale{ previousImbued, new neolib::comma_and_brackets_as_whitespace{} });
643 text_color ink;
644 aStream >> ink;
645 aTextFormat.set_ink(ink);
647 aStream >> temp;
648 aTextFormat.set_paper(temp);
649 optional_text_effect textEffect;
650 aStream >> textEffect;
651 aTextFormat.set_effect(textEffect);
652 bool smartUnderline;
653 aStream >> smartUnderline;
654 aTextFormat.set_smart_underline(smartUnderline);
655 bool ignoreEmoji;
656 aStream >> ignoreEmoji;
657 aTextFormat.set_ignore_emoji(ignoreEmoji);
658 aStream.imbue(previousImbued);
659 return aStream;
660 }
661}
662
663define_setting_type(neogfx::text_format)
664
665namespace neogfx
666{
667 struct text_format_span
668 {
669 std::ptrdiff_t start;
670 std::ptrdiff_t end;
671 text_format attributes;
672 };
673
674 class text_format_spans
675 {
676 public:
677 typedef neolib::vecarray<text_format_span, 1, -1> spans;
678 public:
679 text_format_spans()
680 {
681 }
682 text_format_spans(text_format_span const& aSpan) :
683 iSpans{ aSpan }
684 {
685 }
686 public:
687 void clear()
688 {
689 iSpans.clear();
690 }
691 void add(std::ptrdiff_t aGlyphTextIndex, text_format const& aTextFormat)
692 {
693 if (iSpans.empty() || iSpans.back().attributes != aTextFormat)
694 iSpans.emplace_back(aGlyphTextIndex, aGlyphTextIndex + 1, aTextFormat);
695 else
696 iSpans.back().end = aGlyphTextIndex + 1;
697 }
698 template <typename... Args>
699 void add(std::ptrdiff_t aGlyphtTextIndex, Args&&... aArgs)
700 {
701 text_format const attributes{ std::forward<Args>(aArgs)... };
702 add(aGlyphtTextIndex, attributes);
703 }
704 public:
705 spans::const_iterator begin() const
706 {
707 return iSpans.begin();
708 }
709 spans::const_iterator end() const
710 {
711 return iSpans.end();
712 }
713 private:
714 spans iSpans;
715 };
716
717 class paragraph_format
718 {
719 public:
720 using self_type = paragraph_format;
721 using abstract_type = self_type;
722 public:
723 paragraph_format() {}
724 paragraph_format(paragraph_format const& aOther) {}
725 public:
726 auto operator<=>(self_type const&) const = default;
727 };
728}
return_type with_alpha(view_component aAlpha) const
Definition color.hpp:154
view_component component
Definition color.hpp:378
text_color with_alpha(color::component aAlpha) const
text_color(color_or_gradient const &aOther)
text_color & operator=(color_or_gradient const &aOther)
color::component alpha() const
self_type & operator=(T const &aOther)
Definition variant.hpp:200
#define end_declare_enum(enumName)
Definition i_enum.hpp:62
#define declare_enum_string(enumName, enumEnumerator)
Definition i_enum.hpp:59
#define begin_declare_enum(enumName)
Definition i_enum.hpp:52
#define define_setting_type(T)
std::basic_ostream< Elem, Traits > & operator<<(std::basic_ostream< Elem, Traits > &aStream, const basic_point< T > &aPoint)
blurring_algorithm
bool operator!=(color_or_gradient const &lhs, color const &rhs) noexcept
default_geometry_value_type dimension
std::basic_istream< Elem, Traits > & operator>>(std::basic_istream< Elem, Traits > &aStream, basic_point< T > &aPoint)
brush to_brush(const color_or_gradient &aEffectColor)
logical_operation
bool operator==(const basic_rect< CoordinateType, CoordinateSystem > &left, const basic_rect< CoordinateType, CoordinateSystem > &right)
optional< dimension > optional_dimension
neolib::variant< color, gradient, texture, std::pair< texture, rect >, sub_texture, std::pair< sub_texture, rect > > brush
sRGB_color color
Definition color.hpp:1067
std::partial_ordering operator<=>(const gradient &aLhs, const gradient &aRhs)
Definition gradient.hpp:189
neolib::optional< text_color > optional_text_color
optional< vec3 > optional_vec3
const none_t none
Definition variant.hpp:111
Definition plf_hive.h:79