GradientTool.cpp 15 KB

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