SprayTool.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "SprayTool.h"
  8. #include "../ImageEditor.h"
  9. #include "../Layer.h"
  10. #include <AK/Math.h>
  11. #include <AK/Queue.h>
  12. #include <LibGUI/Action.h>
  13. #include <LibGUI/BoxLayout.h>
  14. #include <LibGUI/Label.h>
  15. #include <LibGUI/Menu.h>
  16. #include <LibGUI/Painter.h>
  17. #include <LibGUI/ValueSlider.h>
  18. #include <LibGfx/Bitmap.h>
  19. namespace PixelPaint {
  20. SprayTool::SprayTool()
  21. {
  22. m_timer = Core::Timer::create_repeating(200, [&]() {
  23. paint_it();
  24. }).release_value_but_fixme_should_propagate_errors();
  25. }
  26. static double nrand()
  27. {
  28. return double(rand()) / double(RAND_MAX);
  29. }
  30. void SprayTool::paint_it()
  31. {
  32. auto* layer = m_editor->active_layer();
  33. if (!layer)
  34. return;
  35. auto& bitmap = layer->get_scratch_edited_bitmap();
  36. GUI::Painter painter(bitmap);
  37. VERIFY(bitmap.bpp() == 32);
  38. double const minimal_radius = 2;
  39. double const base_radius = minimal_radius * m_thickness;
  40. for (int i = 0; i < M_PI * base_radius * base_radius * (m_density / 100.0); i++) {
  41. double radius = base_radius * nrand();
  42. double angle = 2 * M_PI * nrand();
  43. int const xpos = m_last_pos.x() + radius * AK::cos(angle);
  44. int const ypos = m_last_pos.y() - radius * AK::sin(angle);
  45. if (xpos < 0 || xpos >= bitmap.width())
  46. continue;
  47. if (ypos < 0 || ypos >= bitmap.height())
  48. continue;
  49. bitmap.set_pixel<Gfx::StorageFormat::BGRA8888>(xpos, ypos, m_color);
  50. }
  51. layer->did_modify_bitmap(Gfx::IntRect::centered_on(m_last_pos, Gfx::IntSize(base_radius * 2, base_radius * 2)));
  52. }
  53. void SprayTool::on_mousedown(Layer* layer, MouseEvent& event)
  54. {
  55. if (!layer)
  56. return;
  57. auto& layer_event = event.layer_event();
  58. m_color = m_editor->color_for(layer_event);
  59. m_last_pos = layer_event.position();
  60. m_timer->start();
  61. paint_it();
  62. }
  63. void SprayTool::on_mousemove(Layer* layer, MouseEvent& event)
  64. {
  65. if (!layer)
  66. return;
  67. m_last_pos = event.layer_event().position();
  68. if (m_timer->is_active()) {
  69. paint_it();
  70. m_timer->restart(m_timer->interval());
  71. }
  72. }
  73. void SprayTool::on_mouseup(Layer*, MouseEvent&)
  74. {
  75. if (m_timer->is_active()) {
  76. m_timer->stop();
  77. m_editor->did_complete_action(tool_name());
  78. }
  79. }
  80. ErrorOr<GUI::Widget*> SprayTool::get_properties_widget()
  81. {
  82. if (!m_properties_widget) {
  83. auto properties_widget = TRY(GUI::Widget::try_create());
  84. (void)TRY(properties_widget->try_set_layout<GUI::VerticalBoxLayout>());
  85. auto size_container = TRY(properties_widget->try_add<GUI::Widget>());
  86. size_container->set_fixed_height(20);
  87. (void)TRY(size_container->try_set_layout<GUI::HorizontalBoxLayout>());
  88. auto size_label = TRY(size_container->try_add<GUI::Label>("Size:"));
  89. size_label->set_text_alignment(Gfx::TextAlignment::CenterLeft);
  90. size_label->set_fixed_size(80, 20);
  91. auto size_slider = TRY(size_container->try_add<GUI::ValueSlider>(Orientation::Horizontal, String::from_utf8_short_string("px"sv)));
  92. size_slider->set_range(1, 20);
  93. size_slider->set_value(m_thickness);
  94. size_slider->on_change = [this](int value) {
  95. m_thickness = value;
  96. };
  97. set_primary_slider(size_slider);
  98. auto density_container = TRY(properties_widget->try_add<GUI::Widget>());
  99. density_container->set_fixed_height(20);
  100. (void)TRY(density_container->try_set_layout<GUI::HorizontalBoxLayout>());
  101. auto density_label = TRY(density_container->try_add<GUI::Label>("Density:"));
  102. density_label->set_text_alignment(Gfx::TextAlignment::CenterLeft);
  103. density_label->set_fixed_size(80, 20);
  104. auto density_slider = TRY(density_container->try_add<GUI::ValueSlider>(Orientation::Horizontal, String::from_utf8_short_string("%"sv)));
  105. density_slider->set_range(1, 100);
  106. density_slider->set_value(m_density);
  107. density_slider->on_change = [this](int value) {
  108. m_density = value;
  109. };
  110. set_secondary_slider(density_slider);
  111. m_properties_widget = properties_widget;
  112. }
  113. return m_properties_widget.ptr();
  114. }
  115. }