GradientTool.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. * Copyright (c) 2023, Torsten Engelmann <engelTorsten@gmx.de>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "GradientTool.h"
  7. #include "../ImageEditor.h"
  8. #include "../Layer.h"
  9. #include <LibGUI/Action.h>
  10. #include <LibGUI/Application.h>
  11. #include <LibGUI/BoxLayout.h>
  12. #include <LibGUI/Button.h>
  13. #include <LibGUI/Label.h>
  14. #include <LibGUI/OpacitySlider.h>
  15. #include <LibGUI/Painter.h>
  16. #include <LibGfx/AntiAliasingPainter.h>
  17. #include <LibGfx/Color.h>
  18. #include <LibGfx/Gradients.h>
  19. #include <LibGfx/Path.h>
  20. #include <LibGfx/Rect.h>
  21. namespace PixelPaint {
  22. Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> GradientTool::cursor()
  23. {
  24. if (m_hover_over_drag_handle || m_hover_over_start_handle || m_hover_over_end_handle)
  25. return Gfx::StandardCursor::Hand;
  26. if (m_button_pressed)
  27. return Gfx::StandardCursor::Move;
  28. return Gfx::StandardCursor::Crosshair;
  29. }
  30. void GradientTool::on_mousedown(Layer* layer, MouseEvent& event)
  31. {
  32. if (!layer)
  33. return;
  34. auto& layer_event = event.layer_event();
  35. if (layer_event.button() != GUI::MouseButton::Primary && layer_event.button() != GUI::MouseButton::Secondary)
  36. return;
  37. m_button_pressed = true;
  38. if (!m_hover_over_start_handle && !m_hover_over_end_handle) {
  39. if (has_gradient_start_end()) {
  40. Gfx::IntPoint movement_delta = layer_event.position() - m_gradient_center.value();
  41. m_gradient_center = layer_event.position();
  42. translate_gradient_start_end(movement_delta, false);
  43. calculate_gradient_lines();
  44. } else {
  45. m_gradient_center = layer_event.position();
  46. }
  47. }
  48. m_physical_diagonal_layer_length = Gfx::IntPoint(0, 0).distance_from({ layer->rect().width(), layer->rect().height() });
  49. m_editor->update_tool_cursor();
  50. }
  51. void GradientTool::on_mousemove(Layer* layer, MouseEvent& event)
  52. {
  53. // Check if user is hovering over a handle
  54. if (layer && m_editor && !m_button_pressed && has_gradient_start_end()) {
  55. auto set_hover_flag = [&](bool& flag, const Gfx::IntPoint p) {
  56. auto frame_postion = m_editor->content_to_frame_position(p).to_type<int>();
  57. auto handle = Gfx::IntRect::centered_at(frame_postion, { 16, 16 });
  58. if (flag != handle.contains(event.raw_event().position())) {
  59. flag = !flag;
  60. m_editor->update_tool_cursor();
  61. m_editor->update();
  62. }
  63. };
  64. set_hover_flag(m_hover_over_start_handle, m_gradient_start.value());
  65. set_hover_flag(m_hover_over_drag_handle, m_gradient_center.value());
  66. set_hover_flag(m_hover_over_end_handle, m_gradient_end.value());
  67. }
  68. if (!layer || !m_button_pressed)
  69. return;
  70. auto& layer_event = event.layer_event();
  71. if (!m_hover_over_drag_handle && (m_hover_over_start_handle || m_hover_over_end_handle)) {
  72. auto movement_delta = m_hover_over_start_handle ? layer_event.position() - m_gradient_start.value() : layer_event.position() - m_gradient_end.value();
  73. translate_gradient_start_end(m_hover_over_start_handle ? movement_delta.scaled({ -1, -1 }) : movement_delta);
  74. }
  75. if (m_hover_over_drag_handle) {
  76. auto movement_delta = layer_event.position() - m_gradient_center.value();
  77. m_gradient_center.value().translate_by(movement_delta);
  78. translate_gradient_start_end(movement_delta, false);
  79. }
  80. if (!(m_hover_over_drag_handle || m_hover_over_start_handle || m_hover_over_end_handle))
  81. update_gradient_end_and_derive_start(layer_event.position());
  82. // If Shift is pressed, align the gradient horizontally or vertically
  83. if (m_shift_pressed && has_gradient_start_end()) {
  84. auto delta = m_gradient_center.value() - m_gradient_end.value();
  85. if (AK::abs(delta.x()) < AK::abs(delta.y())) {
  86. m_gradient_start.value().set_x(m_gradient_center.value().x());
  87. m_gradient_end.value().set_x(m_gradient_center.value().x());
  88. } else {
  89. m_gradient_start.value().set_y(m_gradient_center.value().y());
  90. m_gradient_end.value().set_y(m_gradient_center.value().y());
  91. }
  92. }
  93. calculate_gradient_lines();
  94. }
  95. void GradientTool::on_mouseup(Layer*, MouseEvent& event)
  96. {
  97. auto& layer_event = event.layer_event();
  98. if (layer_event.button() != GUI::MouseButton::Primary && layer_event.button() != GUI::MouseButton::Secondary)
  99. return;
  100. m_button_pressed = false;
  101. m_editor->update_tool_cursor();
  102. }
  103. bool GradientTool::on_keydown(GUI::KeyEvent& event)
  104. {
  105. if (event.key() == Key_LeftShift || event.key() == Key_RightShift) {
  106. m_shift_pressed = true;
  107. if (m_button_pressed)
  108. m_editor->update();
  109. return true;
  110. }
  111. if (event.key() == Key_Return) {
  112. rasterize_gradient();
  113. return true;
  114. }
  115. if (event.key() == Key_Escape) {
  116. reset();
  117. return true;
  118. }
  119. return Tool::on_keydown(event);
  120. }
  121. void GradientTool::on_keyup(GUI::KeyEvent& event)
  122. {
  123. Tool::on_keydown(event);
  124. if (event.key() == Key_Shift) {
  125. m_shift_pressed = false;
  126. event.accept();
  127. }
  128. }
  129. void GradientTool::on_second_paint(Layer const* layer, GUI::PaintEvent& event)
  130. {
  131. if (!layer || !has_gradient_start_end())
  132. return;
  133. GUI::Painter painter(*m_editor);
  134. painter.add_clip_rect(event.rect());
  135. draw_gradient(painter, true, m_editor->content_to_frame_position(Gfx::IntPoint(0, 0)), m_editor->scale(), m_editor->content_rect());
  136. }
  137. void GradientTool::on_tool_activation()
  138. {
  139. m_editor->on_primary_color_change = [this](Color) {
  140. if (m_gradient_end.has_value())
  141. m_editor->update();
  142. };
  143. reset();
  144. }
  145. GUI::Widget* GradientTool::get_properties_widget()
  146. {
  147. if (!m_properties_widget) {
  148. m_properties_widget = GUI::Widget::construct();
  149. m_properties_widget->set_layout<GUI::VerticalBoxLayout>();
  150. auto& size_container = m_properties_widget->add<GUI::Widget>();
  151. size_container.set_fixed_height(20);
  152. size_container.set_layout<GUI::HorizontalBoxLayout>();
  153. auto& size_label = size_container.add<GUI::Label>("Opacity:");
  154. size_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
  155. size_label.set_fixed_size(80, 20);
  156. auto& opacity_slider = size_container.add<GUI::HorizontalOpacitySlider>();
  157. opacity_slider.set_range(1, 100);
  158. opacity_slider.set_value(100);
  159. opacity_slider.on_change = [&](int value) {
  160. m_opacity = value;
  161. m_editor->update();
  162. };
  163. set_primary_slider(&opacity_slider);
  164. auto& button_container = m_properties_widget->add<GUI::Widget>();
  165. button_container.set_fixed_height(22);
  166. auto& button_container_layout = button_container.set_layout<GUI::HorizontalBoxLayout>();
  167. button_container_layout.add_spacer();
  168. auto& apply_button = button_container.add<GUI::DialogButton>("Apply");
  169. apply_button.on_click = [this](auto) {
  170. rasterize_gradient();
  171. };
  172. }
  173. return m_properties_widget.ptr();
  174. }
  175. void GradientTool::rasterize_gradient()
  176. {
  177. if (!has_gradient_start_end())
  178. return;
  179. auto layer = m_editor->active_layer();
  180. if (!layer)
  181. return;
  182. GUI::Painter painter(layer->get_scratch_edited_bitmap());
  183. draw_gradient(painter);
  184. layer->did_modify_bitmap(layer->get_scratch_edited_bitmap().rect());
  185. m_editor->did_complete_action(tool_name());
  186. reset();
  187. }
  188. void GradientTool::calculate_gradient_lines()
  189. {
  190. m_gradient_half_length = m_gradient_end.value().distance_from(m_gradient_center.value());
  191. // Create a perpendicular point between the center and end point.
  192. m_perpendicular_point = m_gradient_end.value();
  193. m_perpendicular_point -= m_gradient_center.value();
  194. m_perpendicular_point = { -m_perpendicular_point.y(), m_perpendicular_point.x() };
  195. m_perpendicular_point += m_gradient_center.value();
  196. auto to_edge_scale_direction = (m_physical_diagonal_layer_length * 2) / m_gradient_center.value().distance_from(m_perpendicular_point);
  197. m_gradient_center_line.set_a({ m_gradient_center.value().x() + (to_edge_scale_direction * (m_gradient_center.value().x() - m_perpendicular_point.x())), m_gradient_center.value().y() + (to_edge_scale_direction * (m_gradient_center.value().y() - m_perpendicular_point.y())) });
  198. m_gradient_center_line.set_b({ m_gradient_center.value().x() + (-to_edge_scale_direction * (m_gradient_center.value().x() - m_perpendicular_point.x())), m_gradient_center.value().y() + (-to_edge_scale_direction * (m_gradient_center.value().y() - m_perpendicular_point.y())) });
  199. m_gradient_begin_line.set_a(m_gradient_center_line.a().translated(static_cast<Gfx::FloatPoint>(m_gradient_end.value() - m_gradient_center.value())));
  200. m_gradient_begin_line.set_b(m_gradient_center_line.b().translated(static_cast<Gfx::FloatPoint>(m_gradient_end.value() - m_gradient_center.value())));
  201. m_gradient_end_line.set_a(m_gradient_center_line.a().translated(static_cast<Gfx::FloatPoint>(m_gradient_center.value() - m_gradient_end.value())));
  202. m_gradient_end_line.set_b(m_gradient_center_line.b().translated(static_cast<Gfx::FloatPoint>(m_gradient_center.value() - m_gradient_end.value())));
  203. m_editor->update();
  204. }
  205. void GradientTool::draw_gradient(GUI::Painter& painter, bool with_guidelines, const Gfx::FloatPoint drawing_offset, float scale, Optional<Gfx::IntRect const&> gradient_clip)
  206. {
  207. auto t_gradient_begin_line = m_gradient_begin_line.scaled(scale, scale).translated(drawing_offset);
  208. auto t_gradient_center_line = m_gradient_center_line.scaled(scale, scale).translated(drawing_offset);
  209. auto t_gradient_end_line = m_gradient_end_line.scaled(scale, scale).translated(drawing_offset);
  210. auto t_gradient_center = m_gradient_center.value().to_type<float>().scaled(scale, scale).translated(drawing_offset).to_type<int>();
  211. int width = m_editor->active_layer()->rect().width() * scale;
  212. int height = m_editor->active_layer()->rect().height() * scale;
  213. float rotation_radians = atan2f(t_gradient_begin_line.a().y() - t_gradient_end_line.a().y(), t_gradient_begin_line.a().x() - t_gradient_end_line.a().x());
  214. float rotation_degrees = ((rotation_radians * 180) / static_cast<float>(M_PI)) - 90;
  215. auto determine_required_side_length = [&](int center, int side_length) {
  216. if (center < 0)
  217. return 2 * (AK::abs(center) + side_length);
  218. if (center > side_length)
  219. return 2 * center;
  220. return 2 * (AK::max(center, side_length - center));
  221. };
  222. auto scaled_gradient_center = m_gradient_center.value().to_type<float>().scaled(scale, scale).to_type<int>();
  223. auto gradient_rect_height = determine_required_side_length(scaled_gradient_center.y(), height);
  224. auto gradient_rect_width = determine_required_side_length(scaled_gradient_center.x(), width);
  225. auto gradient_max_side_length = AK::max(gradient_rect_height, gradient_rect_width);
  226. auto gradient_rect = Gfx::IntRect::centered_at(t_gradient_center, { gradient_max_side_length, gradient_max_side_length });
  227. float overall_gradient_length_in_rect = Gfx::calculate_gradient_length(gradient_rect.size(), rotation_degrees);
  228. if (m_gradient_half_length == 0 || overall_gradient_length_in_rect == 0 || isnan(overall_gradient_length_in_rect))
  229. return;
  230. auto gradient_half_width_percentage_offset = (m_gradient_half_length * scale) / overall_gradient_length_in_rect;
  231. auto color_to_use = m_editor->color_for(GUI::MouseButton::Primary);
  232. int base_opacity = color_to_use.alpha() * m_opacity / 100;
  233. color_to_use.set_alpha(base_opacity);
  234. auto gradient_start_color = color_to_use;
  235. gradient_start_color.set_alpha(0);
  236. {
  237. Gfx::PainterStateSaver saver(painter);
  238. if (gradient_clip.has_value())
  239. painter.add_clip_rect(*gradient_clip);
  240. painter.fill_rect_with_linear_gradient(gradient_rect, Array { Gfx::ColorStop { gradient_start_color, 0.5f - gradient_half_width_percentage_offset }, Gfx::ColorStop { color_to_use, 0.5f + gradient_half_width_percentage_offset } }, rotation_degrees);
  241. }
  242. if (with_guidelines) {
  243. Gfx::AntiAliasingPainter aa_painter = Gfx::AntiAliasingPainter(painter);
  244. aa_painter.draw_line(t_gradient_begin_line, Color::LightGray);
  245. aa_painter.draw_line(t_gradient_center_line, Color::MidGray);
  246. aa_painter.draw_line(t_gradient_end_line, Color::Black);
  247. Gfx::FloatLine icon_line1_rotated_offset = Gfx::FloatLine({ -2, -4 }, { -2, 4 }).rotated(rotation_radians);
  248. Gfx::FloatLine icon_line2_rotated_offset = Gfx::FloatLine({ 2, -4 }, { 2, 4 }).rotated(rotation_radians);
  249. auto draw_handle = [&](Gfx::IntPoint p, bool is_hovered, bool with_icon) {
  250. auto alpha = is_hovered ? 255 : 100;
  251. auto translated_p = p.to_type<float>().scaled(scale, scale).translated(drawing_offset);
  252. aa_painter.fill_circle(translated_p.to_type<int>(), 10, Color(Color::MidGray).with_alpha(alpha));
  253. aa_painter.fill_circle(translated_p.to_type<int>(), 8, Color(Color::LightGray).with_alpha(alpha));
  254. if (with_icon) {
  255. aa_painter.draw_line(icon_line1_rotated_offset.translated(translated_p), Color(Color::MidGray).with_alpha(alpha), 2);
  256. aa_painter.draw_line(icon_line2_rotated_offset.translated(translated_p), Color(Color::MidGray).with_alpha(alpha), 2);
  257. }
  258. };
  259. draw_handle(m_gradient_start.value(), m_hover_over_start_handle, true);
  260. draw_handle(m_gradient_center.value(), m_hover_over_drag_handle, false);
  261. draw_handle(m_gradient_end.value(), m_hover_over_end_handle, true);
  262. }
  263. }
  264. void GradientTool::reset()
  265. {
  266. m_gradient_start = {};
  267. m_gradient_center = {};
  268. m_gradient_end = {};
  269. m_gradient_half_length = 0;
  270. m_physical_diagonal_layer_length = 0;
  271. m_hover_over_drag_handle = false;
  272. m_hover_over_start_handle = false;
  273. m_hover_over_end_handle = false;
  274. if (m_editor) {
  275. m_editor->update();
  276. m_editor->update_tool_cursor();
  277. }
  278. }
  279. void GradientTool::update_gradient_end_and_derive_start(Gfx::IntPoint const new_end_point)
  280. {
  281. VERIFY(m_gradient_center.has_value());
  282. m_gradient_end = new_end_point;
  283. m_gradient_start = m_gradient_center.value() - (m_gradient_end.value() - m_gradient_center.value());
  284. }
  285. void GradientTool::translate_gradient_start_end(Gfx::IntPoint const delta, bool update_start_counterwise)
  286. {
  287. m_gradient_end.value().translate_by(delta);
  288. if (update_start_counterwise)
  289. m_gradient_start.value().translate_by(delta.scaled(-1, -1));
  290. else
  291. m_gradient_start.value().translate_by(delta);
  292. }
  293. }