Serenity Operating System
at master 917 lines 32 kB view raw
1/* 2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org> 4 * Copyright (c) 2021-2022, Mustafa Quraish <mustafa@serenityos.org> 5 * Copyright (c) 2021, David Isaksson <davidisaksson93@gmail.com> 6 * Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com> 7 * 8 * SPDX-License-Identifier: BSD-2-Clause 9 */ 10 11#include "ImageEditor.h" 12#include "Image.h" 13#include "Layer.h" 14#include "Tools/MoveTool.h" 15#include "Tools/Tool.h" 16#include <AK/IntegralMath.h> 17#include <AK/LexicalPath.h> 18#include <LibConfig/Client.h> 19#include <LibFileSystemAccessClient/Client.h> 20#include <LibGUI/Command.h> 21#include <LibGUI/MessageBox.h> 22#include <LibGUI/Painter.h> 23#include <LibGfx/DisjointRectSet.h> 24#include <LibGfx/Palette.h> 25#include <LibGfx/Rect.h> 26 27namespace PixelPaint { 28 29constexpr int marching_ant_length = 4; 30 31ImageEditor::ImageEditor(NonnullRefPtr<Image> image) 32 : m_image(move(image)) 33 , m_title("Untitled") 34 , m_gui_event_loop(Core::EventLoop::current()) 35{ 36 set_focus_policy(GUI::FocusPolicy::StrongFocus); 37 m_undo_stack.push(make<ImageUndoCommand>(*m_image, DeprecatedString())); 38 m_image->add_client(*this); 39 m_image->selection().add_client(*this); 40 set_original_rect(m_image->rect()); 41 set_scale_bounds(0.1f, 100.0f); 42 43 m_pixel_grid_threshold = (float)Config::read_i32("PixelPaint"sv, "PixelGrid"sv, "Threshold"sv, 15); 44 m_show_pixel_grid = Config::read_bool("PixelPaint"sv, "PixelGrid"sv, "Show"sv, true); 45 46 m_show_rulers = Config::read_bool("PixelPaint"sv, "Rulers"sv, "Show"sv, true); 47 m_show_guides = Config::read_bool("PixelPaint"sv, "Guides"sv, "Show"sv, true); 48 49 m_marching_ants_timer = Core::Timer::create_repeating(80, [this] { 50 ++m_marching_ants_offset; 51 m_marching_ants_offset %= (marching_ant_length * 2); 52 if (!m_image->selection().is_empty() || m_image->selection().in_interactive_selection()) 53 update(); 54 }).release_value_but_fixme_should_propagate_errors(); 55 m_marching_ants_timer->start(); 56} 57 58ImageEditor::~ImageEditor() 59{ 60 m_image->selection().remove_client(*this); 61 m_image->remove_client(*this); 62} 63 64void ImageEditor::did_complete_action(DeprecatedString action_text) 65{ 66 set_modified(move(action_text)); 67} 68 69bool ImageEditor::is_modified() 70{ 71 return undo_stack().is_current_modified(); 72} 73 74bool ImageEditor::undo() 75{ 76 if (!m_undo_stack.can_undo()) 77 return false; 78 79 /* Without this you need to undo twice to actually start undoing stuff. 80 * This is due to the fact that the top of the UndoStack contains the snapshot of the currently 81 * shown image but what we actually want to restore is the snapshot right below it. 82 * Doing "undo->undo->redo" restores the 2nd topmost snapshot on the stack while lowering the 83 * stack pointer only by 1. This is important because we want the UndoStack's pointer to always point 84 * at the currently shown snapshot, otherwise doing 'undo->undo->draw something else' would delete 85 * one of the snapshots. 86 * This works because UndoStack::undo first decrements the stack pointer and then restores the snapshot, 87 * while UndoStack::redo first restores the snapshot and then increments the stack pointer. 88 */ 89 m_undo_stack.undo(); 90 m_undo_stack.undo(); 91 m_undo_stack.redo(); 92 layers_did_change(); 93 return true; 94} 95 96bool ImageEditor::redo() 97{ 98 if (!m_undo_stack.can_redo()) 99 return false; 100 101 m_undo_stack.redo(); 102 layers_did_change(); 103 return true; 104} 105 106void ImageEditor::set_title(DeprecatedString title) 107{ 108 m_title = move(title); 109 if (on_title_change) 110 on_title_change(m_title); 111} 112 113void ImageEditor::set_path(DeprecatedString path) 114{ 115 m_path = move(path); 116 set_title(LexicalPath::title(m_path)); 117} 118 119void ImageEditor::set_modified(DeprecatedString action_text) 120{ 121 m_undo_stack.push(make<ImageUndoCommand>(*m_image, move(action_text))); 122 update_modified(); 123} 124 125void ImageEditor::set_unmodified() 126{ 127 m_undo_stack.set_current_unmodified(); 128 update_modified(); 129} 130 131void ImageEditor::update_modified() 132{ 133 if (on_modified_change) 134 on_modified_change(is_modified()); 135} 136 137Gfx::IntRect ImageEditor::subtract_rulers_from_rect(Gfx::IntRect const& rect) const 138{ 139 Gfx::IntRect clipped_rect {}; 140 clipped_rect.set_top(max(rect.y(), m_ruler_thickness + 1)); 141 clipped_rect.set_left(max(rect.x(), m_ruler_thickness + 1)); 142 clipped_rect.set_bottom(rect.bottom()); 143 clipped_rect.set_right(rect.right()); 144 return clipped_rect; 145} 146 147void ImageEditor::paint_event(GUI::PaintEvent& event) 148{ 149 GUI::Frame::paint_event(event); 150 151 GUI::Painter painter(*this); 152 painter.add_clip_rect(event.rect()); 153 painter.add_clip_rect(frame_inner_rect()); 154 155 { 156 Gfx::DisjointIntRectSet background_rects; 157 background_rects.add(frame_inner_rect()); 158 background_rects.shatter(content_rect()); 159 for (auto& rect : background_rects.rects()) 160 painter.fill_rect(rect, palette().color(Gfx::ColorRole::Tray)); 161 } 162 163 Gfx::StylePainter::paint_transparency_grid(painter, content_rect(), palette()); 164 165 painter.draw_rect(content_rect().inflated(2, 2), Color::Black); 166 m_image->paint_into(painter, content_rect(), scale()); 167 168 if (m_active_layer && m_show_active_layer_boundary) 169 painter.draw_rect(content_to_frame_rect(m_active_layer->relative_rect()).to_type<int>().inflated(2, 2), Color::Black); 170 171 if (m_show_pixel_grid && scale() > m_pixel_grid_threshold) { 172 auto event_image_rect = enclosing_int_rect(frame_to_content_rect(event.rect())).inflated(1, 1); 173 auto image_rect = m_image->rect().inflated(1, 1).intersected(event_image_rect); 174 175 for (auto i = image_rect.left(); i < image_rect.right(); i++) { 176 auto start_point = content_to_frame_position({ i, image_rect.top() }).to_type<int>(); 177 auto end_point = content_to_frame_position({ i, image_rect.bottom() }).to_type<int>(); 178 painter.draw_line(start_point, end_point, Color::LightGray); 179 } 180 181 for (auto i = image_rect.top(); i < image_rect.bottom(); i++) { 182 auto start_point = content_to_frame_position({ image_rect.left(), i }).to_type<int>(); 183 auto end_point = content_to_frame_position({ image_rect.right(), i }).to_type<int>(); 184 painter.draw_line(start_point, end_point, Color::LightGray); 185 } 186 } 187 188 if (m_show_guides) { 189 for (auto& guide : m_guides) { 190 if (guide->orientation() == Guide::Orientation::Horizontal) { 191 int y_coordinate = (int)content_to_frame_position({ 0.0f, guide->offset() }).y(); 192 painter.draw_line({ 0, y_coordinate }, { rect().width(), y_coordinate }, Color::Cyan, 1, Gfx::Painter::LineStyle::Dashed, Color::LightGray); 193 } else if (guide->orientation() == Guide::Orientation::Vertical) { 194 int x_coordinate = (int)content_to_frame_position({ guide->offset(), 0.0f }).x(); 195 painter.draw_line({ x_coordinate, 0 }, { x_coordinate, rect().height() }, Color::Cyan, 1, Gfx::Painter::LineStyle::Dashed, Color::LightGray); 196 } 197 } 198 } 199 200 paint_selection(painter); 201 202 if (m_show_rulers) { 203 auto const ruler_bg_color = palette().color(Gfx::ColorRole::InactiveSelection); 204 auto const ruler_fg_color = palette().color(Gfx::ColorRole::Ruler); 205 auto const ruler_text_color = palette().color(Gfx::ColorRole::InactiveSelectionText); 206 auto const mouse_indicator_color = Color::White; 207 208 // Ruler background 209 painter.fill_rect({ { 0, 0 }, { m_ruler_thickness, rect().height() } }, ruler_bg_color); 210 painter.fill_rect({ { 0, 0 }, { rect().width(), m_ruler_thickness } }, ruler_bg_color); 211 212 auto const ruler_step = calculate_ruler_step_size(); 213 auto const editor_origin_to_image = frame_to_content_position({ 0, 0 }); 214 auto const editor_max_to_image = frame_to_content_position({ width(), height() }); 215 216 // Horizontal ruler 217 painter.draw_line({ 0, m_ruler_thickness }, { rect().width(), m_ruler_thickness }, ruler_fg_color); 218 auto const x_start = floor(editor_origin_to_image.x()) - ((int)floor(editor_origin_to_image.x()) % ruler_step) - ruler_step; 219 for (int x = x_start; x < editor_max_to_image.x(); x += ruler_step) { 220 int const num_sub_divisions = min(ruler_step, 10); 221 for (int x_sub = 0; x_sub < num_sub_divisions; ++x_sub) { 222 int const x_pos = x + (int)(ruler_step * x_sub / num_sub_divisions); 223 int const editor_x_sub = content_to_frame_position({ x_pos, 0 }).x(); 224 int const line_length = (x_sub % 2 == 0) ? m_ruler_thickness / 3 : m_ruler_thickness / 6; 225 painter.draw_line({ editor_x_sub, m_ruler_thickness - line_length }, { editor_x_sub, m_ruler_thickness }, ruler_fg_color); 226 } 227 228 int const editor_x = content_to_frame_position({ x, 0 }).x(); 229 painter.draw_line({ editor_x, 0 }, { editor_x, m_ruler_thickness }, ruler_fg_color); 230 painter.draw_text(Gfx::IntRect { { editor_x + 2, 0 }, { m_ruler_thickness, m_ruler_thickness - 2 } }, DeprecatedString::formatted("{}", x), painter.font(), Gfx::TextAlignment::CenterLeft, ruler_text_color); 231 } 232 233 // Vertical ruler 234 painter.draw_line({ m_ruler_thickness, 0 }, { m_ruler_thickness, rect().height() }, ruler_fg_color); 235 auto const y_start = floor(editor_origin_to_image.y()) - ((int)floor(editor_origin_to_image.y()) % ruler_step) - ruler_step; 236 for (int y = y_start; y < editor_max_to_image.y(); y += ruler_step) { 237 int const num_sub_divisions = min(ruler_step, 10); 238 for (int y_sub = 0; y_sub < num_sub_divisions; ++y_sub) { 239 int const y_pos = y + (int)(ruler_step * y_sub / num_sub_divisions); 240 int const editor_y_sub = content_to_frame_position({ 0, y_pos }).y(); 241 int const line_length = (y_sub % 2 == 0) ? m_ruler_thickness / 3 : m_ruler_thickness / 6; 242 painter.draw_line({ m_ruler_thickness - line_length, editor_y_sub }, { m_ruler_thickness, editor_y_sub }, ruler_fg_color); 243 } 244 245 int const editor_y = content_to_frame_position({ 0, y }).y(); 246 painter.draw_line({ 0, editor_y }, { m_ruler_thickness, editor_y }, ruler_fg_color); 247 painter.draw_text(Gfx::IntRect { { 0, editor_y - m_ruler_thickness }, { m_ruler_thickness, m_ruler_thickness } }, DeprecatedString::formatted("{}", y), painter.font(), Gfx::TextAlignment::BottomRight, ruler_text_color); 248 } 249 250 // Mouse position indicator 251 const Gfx::IntPoint indicator_x({ m_mouse_position.x(), m_ruler_thickness }); 252 const Gfx::IntPoint indicator_y({ m_ruler_thickness, m_mouse_position.y() }); 253 painter.draw_triangle(indicator_x, indicator_x + Gfx::IntPoint(-m_mouse_indicator_triangle_size, -m_mouse_indicator_triangle_size), indicator_x + Gfx::IntPoint(m_mouse_indicator_triangle_size, -m_mouse_indicator_triangle_size), mouse_indicator_color); 254 painter.draw_triangle(indicator_y, indicator_y + Gfx::IntPoint(-m_mouse_indicator_triangle_size, -m_mouse_indicator_triangle_size), indicator_y + Gfx::IntPoint(-m_mouse_indicator_triangle_size, m_mouse_indicator_triangle_size), mouse_indicator_color); 255 256 // Top left square 257 painter.fill_rect({ { 0, 0 }, { m_ruler_thickness, m_ruler_thickness } }, ruler_bg_color); 258 } 259} 260 261int ImageEditor::calculate_ruler_step_size() const 262{ 263 auto const step_target = 80 / scale(); 264 auto const max_factor = 5; 265 for (int factor = 0; factor < max_factor; ++factor) { 266 int ten_to_factor = AK::pow<int>(10, factor); 267 if (step_target <= 1 * ten_to_factor) 268 return 1 * ten_to_factor; 269 if (step_target <= 2 * ten_to_factor) 270 return 2 * ten_to_factor; 271 if (step_target <= 5 * ten_to_factor) 272 return 5 * ten_to_factor; 273 } 274 return AK::pow<int>(10, max_factor); 275} 276 277Gfx::IntRect ImageEditor::mouse_indicator_rect_x() const 278{ 279 const Gfx::IntPoint top_left({ m_ruler_thickness, m_ruler_thickness - m_mouse_indicator_triangle_size }); 280 const Gfx::IntSize size({ width() + 1, m_mouse_indicator_triangle_size + 1 }); 281 return Gfx::IntRect(top_left, size); 282} 283 284Gfx::IntRect ImageEditor::mouse_indicator_rect_y() const 285{ 286 const Gfx::IntPoint top_left({ m_ruler_thickness - m_mouse_indicator_triangle_size, m_ruler_thickness }); 287 const Gfx::IntSize size({ m_mouse_indicator_triangle_size + 1, height() + 1 }); 288 return Gfx::IntRect(top_left, size); 289} 290 291void ImageEditor::second_paint_event(GUI::PaintEvent& event) 292{ 293 if (m_active_tool) { 294 if (m_show_rulers) { 295 auto clipped_event = GUI::PaintEvent(subtract_rulers_from_rect(event.rect()), event.window_size()); 296 m_active_tool->on_second_paint(m_active_layer, clipped_event); 297 } else { 298 m_active_tool->on_second_paint(m_active_layer, event); 299 } 300 } 301} 302 303GUI::MouseEvent ImageEditor::event_with_pan_and_scale_applied(GUI::MouseEvent const& event) const 304{ 305 auto image_position = frame_to_content_position(event.position()); 306 auto tool_adjusted_image_position = m_active_tool->point_position_to_preferred_cell(image_position); 307 308 return { 309 static_cast<GUI::Event::Type>(event.type()), 310 tool_adjusted_image_position, 311 event.buttons(), 312 event.button(), 313 event.modifiers(), 314 event.wheel_delta_x(), 315 event.wheel_delta_y(), 316 event.wheel_raw_delta_x(), 317 event.wheel_raw_delta_y(), 318 }; 319} 320 321GUI::MouseEvent ImageEditor::event_adjusted_for_layer(GUI::MouseEvent const& event, Layer const& layer) const 322{ 323 auto image_position = frame_to_content_position(event.position()); 324 image_position.translate_by(-layer.location().x(), -layer.location().y()); 325 auto tool_adjusted_image_position = m_active_tool->point_position_to_preferred_cell(image_position); 326 327 return { 328 static_cast<GUI::Event::Type>(event.type()), 329 tool_adjusted_image_position, 330 event.buttons(), 331 event.button(), 332 event.modifiers(), 333 event.wheel_delta_x(), 334 event.wheel_delta_y(), 335 event.wheel_raw_delta_x(), 336 event.wheel_raw_delta_y(), 337 }; 338} 339 340Optional<Color> ImageEditor::color_from_position(Gfx::IntPoint position, bool sample_all_layers) 341{ 342 Color color; 343 auto* layer = active_layer(); 344 if (sample_all_layers) { 345 color = image().color_at(position); 346 } else { 347 if (!layer || !layer->rect().contains(position)) 348 return {}; 349 color = layer->currently_edited_bitmap().get_pixel(position); 350 } 351 return color; 352} 353 354void ImageEditor::set_status_info_to_color_at_mouse_position(Gfx::IntPoint position, bool sample_all_layers) 355{ 356 auto const color = color_from_position(position, sample_all_layers); 357 if (!color.has_value()) 358 return; 359 360 set_appended_status_info(DeprecatedString::formatted("R:{}, G:{}, B:{}, A:{} [{}]", color->red(), color->green(), color->blue(), color->alpha(), color->to_deprecated_string())); 361} 362 363void ImageEditor::set_editor_color_to_color_at_mouse_position(GUI::MouseEvent const& event, bool sample_all_layers = false) 364{ 365 auto const color = color_from_position(event.position(), sample_all_layers); 366 367 if (!color.has_value()) 368 return; 369 370 // We picked a transparent pixel, do nothing. 371 if (!color->alpha()) 372 return; 373 374 if (event.buttons() & GUI::MouseButton::Primary) 375 set_primary_color(*color); 376 if (event.buttons() & GUI::MouseButton::Secondary) 377 set_secondary_color(*color); 378} 379 380void ImageEditor::mousedown_event(GUI::MouseEvent& event) 381{ 382 if (event.button() == GUI::MouseButton::Middle) { 383 start_panning(event.position()); 384 set_override_cursor(Gfx::StandardCursor::Drag); 385 return; 386 } 387 388 if (!m_active_tool) 389 return; 390 391 if (auto* tool = dynamic_cast<MoveTool*>(m_active_tool); tool && tool->layer_selection_mode() == MoveTool::LayerSelectionMode::ForegroundLayer) { 392 if (auto* foreground_layer = layer_at_editor_position(event.position()); foreground_layer && !tool->cursor_is_within_resize_anchor()) 393 set_active_layer(foreground_layer); 394 } 395 396 auto layer_event = m_active_layer ? event_adjusted_for_layer(event, *m_active_layer) : event; 397 if (event.alt() && !m_active_tool->is_overriding_alt()) { 398 set_editor_color_to_color_at_mouse_position(layer_event); 399 return; // Pick Color instead of acivating active tool when holding alt. 400 } 401 402 auto image_event = event_with_pan_and_scale_applied(event); 403 Tool::MouseEvent tool_event(Tool::MouseEvent::Action::MouseDown, layer_event, image_event, event); 404 m_active_tool->on_mousedown(m_active_layer.ptr(), tool_event); 405} 406 407void ImageEditor::doubleclick_event(GUI::MouseEvent& event) 408{ 409 if (!m_active_tool || (event.alt() && !m_active_tool->is_overriding_alt())) 410 return; 411 auto layer_event = m_active_layer ? event_adjusted_for_layer(event, *m_active_layer) : event; 412 auto image_event = event_with_pan_and_scale_applied(event); 413 Tool::MouseEvent tool_event(Tool::MouseEvent::Action::DoubleClick, layer_event, image_event, event); 414 m_active_tool->on_doubleclick(m_active_layer.ptr(), tool_event); 415} 416 417void ImageEditor::mousemove_event(GUI::MouseEvent& event) 418{ 419 m_mouse_position = event.position(); 420 if (m_show_rulers) { 421 update(mouse_indicator_rect_x()); 422 update(mouse_indicator_rect_y()); 423 } 424 425 if (is_panning()) { 426 GUI::AbstractZoomPanWidget::mousemove_event(event); 427 return; 428 } 429 430 auto image_event = event_with_pan_and_scale_applied(event); 431 if (on_image_mouse_position_change) { 432 on_image_mouse_position_change(image_event.position()); 433 } 434 435 auto layer_event = m_active_layer ? event_adjusted_for_layer(event, *m_active_layer) : event; 436 if (m_active_tool && event.alt() && !m_active_tool->is_overriding_alt()) { 437 set_override_cursor(Gfx::StandardCursor::Eyedropper); 438 set_editor_color_to_color_at_mouse_position(layer_event); 439 return; 440 } 441 442 Tool::MouseEvent tool_event(Tool::MouseEvent::Action::MouseDown, layer_event, image_event, event); 443 m_active_tool->on_mousemove(m_active_layer.ptr(), tool_event); 444} 445 446void ImageEditor::mouseup_event(GUI::MouseEvent& event) 447{ 448 if (!(m_active_tool && event.alt() && !m_active_tool->is_overriding_alt())) 449 set_override_cursor(m_active_cursor); 450 451 if (event.button() == GUI::MouseButton::Middle) { 452 stop_panning(); 453 return; 454 } 455 456 if (!m_active_tool || (event.alt() && !m_active_tool->is_overriding_alt())) 457 return; 458 auto layer_event = m_active_layer ? event_adjusted_for_layer(event, *m_active_layer) : event; 459 auto image_event = event_with_pan_and_scale_applied(event); 460 Tool::MouseEvent tool_event(Tool::MouseEvent::Action::MouseDown, layer_event, image_event, event); 461 m_active_tool->on_mouseup(m_active_layer.ptr(), tool_event); 462} 463 464void ImageEditor::context_menu_event(GUI::ContextMenuEvent& event) 465{ 466 if (!m_active_tool) 467 return; 468 m_active_tool->on_context_menu(m_active_layer, event); 469} 470 471void ImageEditor::keydown_event(GUI::KeyEvent& event) 472{ 473 if (event.key() == Key_Delete && !m_image->selection().is_empty() && active_layer()) { 474 active_layer()->erase_selection(m_image->selection()); 475 did_complete_action("Erase Selection"sv); 476 return; 477 } 478 479 if (!m_active_tool) 480 return; 481 482 if (!m_active_tool->is_overriding_alt() && event.key() == Key_Alt) 483 set_override_cursor(Gfx::StandardCursor::Eyedropper); 484 485 if (m_active_tool->on_keydown(event)) 486 return; 487 488 if (event.key() == Key_Escape && !m_image->selection().is_empty()) { 489 m_image->selection().clear(); 490 did_complete_action("Clear Selection"sv); 491 return; 492 } 493 494 event.ignore(); 495} 496 497void ImageEditor::keyup_event(GUI::KeyEvent& event) 498{ 499 if (!m_active_tool) 500 return; 501 502 if (!m_active_tool->is_overriding_alt() && event.key() == Key_Alt) 503 update_tool_cursor(); 504 505 m_active_tool->on_keyup(event); 506} 507 508void ImageEditor::enter_event(Core::Event&) 509{ 510 set_override_cursor(m_active_cursor); 511} 512 513void ImageEditor::leave_event(Core::Event&) 514{ 515 set_override_cursor(Gfx::StandardCursor::None); 516 517 if (on_leave) 518 on_leave(); 519} 520 521void ImageEditor::set_active_layer(Layer* layer) 522{ 523 if (m_active_layer == layer) 524 return; 525 m_active_layer = layer; 526 527 if (m_active_layer) { 528 VERIFY(&m_active_layer->image() == m_image.ptr()); 529 size_t index = 0; 530 for (; index < m_image->layer_count(); ++index) { 531 if (&m_image->layer(index) == layer) 532 break; 533 } 534 if (on_active_layer_change) 535 on_active_layer_change(layer); 536 } else { 537 if (on_active_layer_change) 538 on_active_layer_change({}); 539 } 540 if (m_show_active_layer_boundary) 541 update(); 542} 543 544ErrorOr<void> ImageEditor::add_new_layer_from_selection() 545{ 546 auto current_layer_selection = image().selection(); 547 if (current_layer_selection.is_empty()) 548 return Error::from_string_literal("There is no active selection to create layer from."); 549 550 // save offsets of selection so we know where to place the new layer 551 auto selection_offset = current_layer_selection.bounding_rect().location(); 552 553 auto selection_bitmap = active_layer()->copy_bitmap(current_layer_selection); 554 if (selection_bitmap.is_null()) 555 return Error::from_string_literal("Unable to create bitmap from selection."); 556 557 auto layer_or_error = PixelPaint::Layer::create_with_bitmap(image(), selection_bitmap.release_nonnull(), "New Layer"sv); 558 if (layer_or_error.is_error()) 559 return Error::from_string_literal("Unable to create layer from selection."); 560 561 auto new_layer = layer_or_error.release_value(); 562 new_layer->set_location(selection_offset); 563 image().add_layer(new_layer); 564 layers_did_change(); 565 return {}; 566} 567 568void ImageEditor::set_active_tool(Tool* tool) 569{ 570 if (m_active_tool == tool) { 571 if (m_active_tool) 572 m_active_tool->setup(*this); 573 return; 574 } 575 576 if (m_active_tool) { 577 m_active_tool->on_tool_deactivation(); 578 m_active_tool->clear(); 579 } 580 581 m_active_tool = tool; 582 583 if (m_active_tool) { 584 m_active_tool->setup(*this); 585 m_active_tool->on_tool_activation(); 586 m_active_cursor = m_active_tool->cursor(); 587 set_override_cursor(m_active_cursor); 588 } 589} 590 591void ImageEditor::update_tool_cursor() 592{ 593 if (m_active_tool) { 594 m_active_cursor = m_active_tool->cursor(); 595 set_override_cursor(m_active_cursor); 596 } 597} 598 599void ImageEditor::set_guide_visibility(bool show_guides) 600{ 601 if (m_show_guides == show_guides) 602 return; 603 604 m_show_guides = show_guides; 605 606 if (on_set_guide_visibility) 607 on_set_guide_visibility(m_show_guides); 608 609 update(); 610} 611 612void ImageEditor::set_ruler_visibility(bool show_rulers) 613{ 614 if (m_show_rulers == show_rulers) 615 return; 616 617 m_show_rulers = show_rulers; 618 619 if (on_set_ruler_visibility) 620 on_set_ruler_visibility(m_show_rulers); 621 622 update(); 623} 624 625void ImageEditor::set_pixel_grid_visibility(bool show_pixel_grid) 626{ 627 if (m_show_pixel_grid == show_pixel_grid) 628 return; 629 m_show_pixel_grid = show_pixel_grid; 630 update(); 631} 632 633void ImageEditor::clear_guides() 634{ 635 m_guides.clear(); 636 update(); 637} 638 639void ImageEditor::layers_did_change() 640{ 641 update_modified(); 642 update(); 643} 644 645Color ImageEditor::color_for(GUI::MouseButton button) const 646{ 647 if (button == GUI::MouseButton::Primary) 648 return m_primary_color; 649 if (button == GUI::MouseButton::Secondary) 650 return m_secondary_color; 651 VERIFY_NOT_REACHED(); 652} 653 654Color ImageEditor::color_for(GUI::MouseEvent const& event) const 655{ 656 if (event.buttons() & GUI::MouseButton::Primary) 657 return m_primary_color; 658 if (event.buttons() & GUI::MouseButton::Secondary) 659 return m_secondary_color; 660 VERIFY_NOT_REACHED(); 661} 662 663void ImageEditor::set_primary_color(Color color) 664{ 665 if (m_primary_color == color) 666 return; 667 m_primary_color = color; 668 if (on_primary_color_change) 669 on_primary_color_change(color); 670} 671 672void ImageEditor::set_secondary_color(Color color) 673{ 674 if (m_secondary_color == color) 675 return; 676 m_secondary_color = color; 677 if (on_secondary_color_change) 678 on_secondary_color_change(color); 679} 680 681Layer* ImageEditor::layer_at_editor_position(Gfx::IntPoint editor_position) 682{ 683 auto image_position = frame_to_content_position(editor_position); 684 for (ssize_t i = m_image->layer_count() - 1; i >= 0; --i) { 685 auto& layer = m_image->layer(i); 686 if (!layer.is_visible()) 687 continue; 688 if (layer.relative_rect().contains(Gfx::IntPoint(image_position.x(), image_position.y()))) 689 return const_cast<Layer*>(&layer); 690 } 691 return nullptr; 692} 693 694void ImageEditor::fit_image_to_view(FitType type) 695{ 696 auto viewport_rect = rect(); 697 698 if (m_show_rulers) { 699 viewport_rect = { 700 viewport_rect.x() + m_ruler_thickness, 701 viewport_rect.y() + m_ruler_thickness, 702 viewport_rect.width() - m_ruler_thickness, 703 viewport_rect.height() - m_ruler_thickness 704 }; 705 } 706 707 fit_content_to_rect(viewport_rect, type); 708} 709 710void ImageEditor::image_did_change(Gfx::IntRect const& modified_image_rect) 711{ 712 update(content_rect().intersected(enclosing_int_rect(content_to_frame_rect(modified_image_rect)))); 713} 714 715void ImageEditor::image_did_change_rect(Gfx::IntRect const& new_image_rect) 716{ 717 set_original_rect(new_image_rect); 718 set_content_rect(new_image_rect); 719 relayout(); 720} 721 722void ImageEditor::image_select_layer(Layer* layer) 723{ 724 set_active_layer(layer); 725} 726 727bool ImageEditor::request_close() 728{ 729 if (!undo_stack().is_current_modified()) 730 return true; 731 732 auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), path(), undo_stack().last_unmodified_timestamp()); 733 734 if (result == GUI::MessageBox::ExecResult::Yes) { 735 save_project(); 736 return true; 737 } 738 739 if (result == GUI::MessageBox::ExecResult::No) 740 return true; 741 742 return false; 743} 744 745void ImageEditor::save_project() 746{ 747 if (path().is_empty() || m_loaded_from_image) { 748 save_project_as(); 749 return; 750 } 751 auto response = FileSystemAccessClient::Client::the().request_file(window(), path(), Core::File::OpenMode::Truncate | Core::File::OpenMode::Write); 752 if (response.is_error()) 753 return; 754 auto result = save_project_to_file(response.value().release_stream()); 755 if (result.is_error()) { 756 GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Could not save {}: {}", path(), result.error())); 757 return; 758 } 759 set_unmodified(); 760} 761 762void ImageEditor::save_project_as() 763{ 764 auto response = FileSystemAccessClient::Client::the().save_file(window(), m_title, "pp"); 765 if (response.is_error()) 766 return; 767 auto file = response.release_value(); 768 auto result = save_project_to_file(file.release_stream()); 769 if (result.is_error()) { 770 GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Could not save {}: {}", file.filename(), result.error())); 771 return; 772 } 773 set_path(file.filename().to_deprecated_string()); 774 set_loaded_from_image(false); 775 set_unmodified(); 776} 777 778ErrorOr<void> ImageEditor::save_project_to_file(NonnullOwnPtr<Core::File> file) const 779{ 780 StringBuilder builder; 781 auto json = TRY(JsonObjectSerializer<>::try_create(builder)); 782 TRY(m_image->serialize_as_json(json)); 783 auto json_guides = TRY(json.add_array("guides"sv)); 784 for (auto const& guide : m_guides) { 785 auto json_guide = TRY(json_guides.add_object()); 786 TRY(json_guide.add("offset"sv, (double)guide->offset())); 787 if (guide->orientation() == Guide::Orientation::Vertical) 788 TRY(json_guide.add("orientation"sv, "vertical")); 789 else if (guide->orientation() == Guide::Orientation::Horizontal) 790 TRY(json_guide.add("orientation"sv, "horizontal")); 791 TRY(json_guide.finish()); 792 } 793 TRY(json_guides.finish()); 794 TRY(json.finish()); 795 796 TRY(file->write_until_depleted(builder.string_view().bytes())); 797 return {}; 798} 799 800void ImageEditor::set_show_active_layer_boundary(bool show) 801{ 802 if (m_show_active_layer_boundary == show) 803 return; 804 805 m_show_active_layer_boundary = show; 806 update(); 807} 808 809void ImageEditor::set_loaded_from_image(bool loaded_from_image) 810{ 811 m_loaded_from_image = loaded_from_image; 812} 813 814void ImageEditor::paint_selection(Gfx::Painter& painter) 815{ 816 if (m_image->selection().is_empty()) 817 return; 818 819 draw_marching_ants(painter, m_image->selection().mask()); 820} 821 822void ImageEditor::draw_marching_ants(Gfx::Painter& painter, Gfx::IntRect const& rect) const 823{ 824 // Top line 825 for (int x = rect.left(); x <= rect.right(); ++x) 826 draw_marching_ants_pixel(painter, x, rect.top()); 827 828 // Right line 829 for (int y = rect.top() + 1; y <= rect.bottom(); ++y) 830 draw_marching_ants_pixel(painter, rect.right(), y); 831 832 // Bottom line 833 for (int x = rect.right() - 1; x >= rect.left(); --x) 834 draw_marching_ants_pixel(painter, x, rect.bottom()); 835 836 // Left line 837 for (int y = rect.bottom() - 1; y > rect.top(); --y) 838 draw_marching_ants_pixel(painter, rect.left(), y); 839} 840 841void ImageEditor::draw_marching_ants(Gfx::Painter& painter, Mask const& mask) const 842{ 843 // If the zoom is < 100%, we can skip pixels to save a lot of time drawing the ants 844 int step = max(1, (int)floorf(1.0f / scale())); 845 846 // Only check the visible selection area when drawing for performance 847 auto rect = this->rect(); 848 rect = Gfx::enclosing_int_rect(frame_to_content_rect(rect)); 849 rect.inflate(step * 2, step * 2); // prevent borders from having visible ants if the selection extends beyond it 850 851 // Scan the image horizontally to find vertical borders 852 for (int y = rect.top(); y <= rect.bottom(); y += step) { 853 854 bool previous_selected = false; 855 for (int x = rect.left(); x <= rect.right(); x += step) { 856 bool this_selected = mask.get(x, y) > 0; 857 858 if (this_selected != previous_selected) { 859 Gfx::IntRect image_pixel { x, y, 1, 1 }; 860 auto pixel = content_to_frame_rect(image_pixel).to_type<int>(); 861 auto end = max(pixel.top(), pixel.bottom()); // for when the zoom is < 100% 862 863 for (int pixel_y = pixel.top(); pixel_y <= end; pixel_y++) { 864 draw_marching_ants_pixel(painter, pixel.left(), pixel_y); 865 } 866 } 867 868 previous_selected = this_selected; 869 } 870 } 871 872 // Scan the image vertically to find horizontal borders 873 for (int x = rect.left(); x <= rect.right(); x += step) { 874 875 bool previous_selected = false; 876 for (int y = rect.top(); y <= rect.bottom(); y += step) { 877 bool this_selected = mask.get(x, y) > 0; 878 879 if (this_selected != previous_selected) { 880 Gfx::IntRect image_pixel { x, y, 1, 1 }; 881 auto pixel = content_to_frame_rect(image_pixel).to_type<int>(); 882 auto end = max(pixel.left(), pixel.right()); // for when the zoom is < 100% 883 884 for (int pixel_x = pixel.left(); pixel_x <= end; pixel_x++) { 885 draw_marching_ants_pixel(painter, pixel_x, pixel.top()); 886 } 887 } 888 889 previous_selected = this_selected; 890 } 891 } 892} 893 894void ImageEditor::draw_marching_ants_pixel(Gfx::Painter& painter, int x, int y) const 895{ 896 int pattern_index = x + y + m_marching_ants_offset; 897 898 if (pattern_index % (marching_ant_length * 2) < marching_ant_length) { 899 painter.set_pixel(x, y, Color::Black); 900 } else { 901 painter.set_pixel(x, y, Color::White); 902 } 903} 904 905void ImageEditor::selection_did_change() 906{ 907 update(); 908} 909 910void ImageEditor::set_appended_status_info(DeprecatedString new_status_info) 911{ 912 m_appended_status_info = new_status_info; 913 if (on_appended_status_info_change) 914 on_appended_status_info_change(m_appended_status_info); 915} 916 917}