gradient.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #pragma once
  8. #include "base/flat_map.h"
  9. #include "ui/effects/animation_value.h"
  10. #include <QtGui/QLinearGradient>
  11. #include <QtGui/QRadialGradient>
  12. namespace anim {
  13. [[nodiscard]] QColor gradient_color_at(
  14. const QGradientStops &stops,
  15. float64 ratio);
  16. [[nodiscard]] QColor gradient_color_at(
  17. const QGradient &gradient,
  18. float64 ratio);
  19. struct gradient_colors {
  20. explicit gradient_colors(QColor color) {
  21. stops.push_back({ 0., color });
  22. stops.push_back({ 1., color });
  23. }
  24. explicit gradient_colors(std::vector<QColor> colors) {
  25. if (colors.size() == 1) {
  26. gradient_colors(colors.front());
  27. return;
  28. }
  29. const auto last = float(colors.size() - 1);
  30. for (auto i = 0; i < colors.size(); i++) {
  31. stops.push_back({ i / last, std::move(colors[i]) });
  32. }
  33. }
  34. explicit gradient_colors(QGradientStops colors)
  35. : stops(std::move(colors)) {
  36. }
  37. QGradientStops stops;
  38. };
  39. namespace details {
  40. template <typename T, typename Derived>
  41. class gradients {
  42. public:
  43. gradients() = default;
  44. gradients(base::flat_map<T, std::vector<QColor>> colors) {
  45. Expects(!colors.empty());
  46. for (const auto &[key, value] : colors) {
  47. auto c = gradient_colors(std::move(value));
  48. _gradients.emplace(key, gradient_with_stops(std::move(c.stops)));
  49. }
  50. }
  51. gradients(base::flat_map<T, gradient_colors> colors) {
  52. Expects(!colors.empty());
  53. for (const auto &[key, c] : colors) {
  54. _gradients.emplace(key, gradient_with_stops(std::move(c.stops)));
  55. }
  56. }
  57. QGradient gradient(T state1, T state2, float64 b_ratio) const {
  58. Expects(!_gradients.empty());
  59. if (b_ratio == 0.) {
  60. return _gradients.find(state1)->second;
  61. } else if (b_ratio == 1.) {
  62. return _gradients.find(state2)->second;
  63. }
  64. auto gradient = empty_gradient();
  65. const auto gradient1 = _gradients.find(state1);
  66. const auto gradient2 = _gradients.find(state2);
  67. Assert(gradient1 != end(_gradients));
  68. Assert(gradient2 != end(_gradients));
  69. const auto stopsFrom = gradient1->second.stops();
  70. const auto stopsTo = gradient2->second.stops();
  71. if ((stopsFrom.size() == stopsTo.size())
  72. && ranges::equal(
  73. stopsFrom,
  74. stopsTo,
  75. ranges::equal_to(),
  76. &QGradientStop::first,
  77. &QGradientStop::first)) {
  78. const auto size = stopsFrom.size();
  79. const auto &p = b_ratio;
  80. for (auto i = 0; i < size; i++) {
  81. auto c = color(stopsFrom[i].second, stopsTo[i].second, p);
  82. gradient.setColorAt(stopsTo[i].first, std::move(c));
  83. }
  84. return gradient;
  85. }
  86. const auto invert = (stopsFrom.size() > stopsTo.size());
  87. if (invert) {
  88. b_ratio = 1. - b_ratio;
  89. }
  90. const auto &stops1 = invert ? stopsTo : stopsFrom;
  91. const auto &stops2 = invert ? stopsFrom : stopsTo;
  92. const auto size1 = stops1.size();
  93. const auto size2 = stops2.size();
  94. for (auto i = 0; i < size1; i++) {
  95. const auto point1 = stops1[i].first;
  96. const auto previousPoint1 = i ? stops1[i - 1].first : -1.;
  97. for (auto n = 0; n < size2; n++) {
  98. const auto point2 = stops2[n].first;
  99. if ((point2 <= previousPoint1) || (point2 > point1)) {
  100. continue;
  101. }
  102. const auto color2 = stops2[n].second;
  103. QColor result;
  104. if (point2 < point1) {
  105. const auto pointRatio2 = (point2 - previousPoint1)
  106. / (point1 - previousPoint1);
  107. const auto color1 = color(
  108. stops1[i - 1].second,
  109. stops1[i].second,
  110. pointRatio2);
  111. result = color(color1, color2, b_ratio);
  112. } else {
  113. // point2 == point1
  114. result = color(stops1[i].second, color2, b_ratio);
  115. }
  116. gradient.setColorAt(point2, std::move(result));
  117. }
  118. }
  119. return gradient;
  120. }
  121. protected:
  122. void cache_gradients() {
  123. auto copy = std::move(_gradients);
  124. for (const auto &[key, value] : copy) {
  125. _gradients.emplace(key, gradient_with_stops(value.stops()));
  126. }
  127. }
  128. private:
  129. QGradient empty_gradient() const {
  130. return static_cast<const Derived*>(this)->empty_gradient();
  131. }
  132. QGradient gradient_with_stops(QGradientStops stops) const {
  133. auto gradient = empty_gradient();
  134. gradient.setStops(std::move(stops));
  135. return gradient;
  136. }
  137. base::flat_map<T, QGradient> _gradients;
  138. };
  139. } // namespace details
  140. template <typename T>
  141. class linear_gradients final
  142. : public details::gradients<T, linear_gradients<T>> {
  143. using parent = details::gradients<T, linear_gradients<T>>;
  144. public:
  145. linear_gradients() = default;
  146. linear_gradients(
  147. base::flat_map<T, std::vector<QColor>> colors,
  148. QPointF point1,
  149. QPointF point2)
  150. : parent(std::move(colors)) {
  151. set_points(point1, point2);
  152. }
  153. linear_gradients(
  154. base::flat_map<T, gradient_colors> colors,
  155. QPointF point1,
  156. QPointF point2)
  157. : parent(std::move(colors)) {
  158. set_points(point1, point2);
  159. }
  160. void set_points(QPointF point1, QPointF point2) {
  161. if (_point1 == point1 && _point2 == point2) {
  162. return;
  163. }
  164. _point1 = point1;
  165. _point2 = point2;
  166. parent::cache_gradients();
  167. }
  168. private:
  169. friend class details::gradients<T, linear_gradients<T>>;
  170. QGradient empty_gradient() const {
  171. return QLinearGradient(_point1, _point2);
  172. }
  173. QPointF _point1;
  174. QPointF _point2;
  175. };
  176. template <typename T>
  177. class radial_gradients final
  178. : public details::gradients<T, radial_gradients<T>> {
  179. using parent = details::gradients<T, radial_gradients<T>>;
  180. public:
  181. radial_gradients() = default;
  182. radial_gradients(
  183. base::flat_map<T, std::vector<QColor>> colors,
  184. QPointF center,
  185. float radius)
  186. : parent(std::move(colors)) {
  187. set_points(center, radius);
  188. }
  189. radial_gradients(
  190. base::flat_map<T, gradient_colors> colors,
  191. QPointF center,
  192. float radius)
  193. : parent(std::move(colors)) {
  194. set_points(center, radius);
  195. }
  196. void set_points(QPointF center, float radius) {
  197. if (_center == center && _radius == radius) {
  198. return;
  199. }
  200. _center = center;
  201. _radius = radius;
  202. parent::cache_gradients();
  203. }
  204. private:
  205. friend class details::gradients<T, radial_gradients<T>>;
  206. QGradient empty_gradient() const {
  207. return QRadialGradient(_center, _radius);
  208. }
  209. QPointF _center;
  210. float _radius = 0.;
  211. };
  212. } // namespace anim