/* * Copyright (c) 2020, Andreas Kling * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ImageEditor.h" #include "Image.h" #include "Layer.h" #include "MoveTool.h" #include "Tool.h" #include #include #include #include namespace PixelPaint { ImageEditor::ImageEditor() : m_undo_stack(make()) { set_focus_policy(GUI::FocusPolicy::StrongFocus); } ImageEditor::~ImageEditor() { if (m_image) m_image->remove_client(*this); } void ImageEditor::set_image(RefPtr image) { if (m_image) m_image->remove_client(*this); m_image = move(image); m_active_layer = nullptr; m_undo_stack = make(); m_undo_stack->push(make(*m_image)); update(); relayout(); if (m_image) m_image->add_client(*this); } void ImageEditor::did_complete_action() { if (!m_image) return; m_undo_stack->finalize_current_combo(); m_undo_stack->push(make(*m_image)); } bool ImageEditor::undo() { if (!m_image) return false; if (m_undo_stack->can_undo()) { m_undo_stack->undo(); layers_did_change(); return true; } return false; } bool ImageEditor::redo() { if (!m_image) return false; if (m_undo_stack->can_redo()) { m_undo_stack->redo(); layers_did_change(); return true; } return false; } void ImageEditor::paint_event(GUI::PaintEvent& event) { GUI::Frame::paint_event(event); GUI::Painter painter(*this); painter.add_clip_rect(event.rect()); painter.add_clip_rect(frame_inner_rect()); Gfx::StylePainter::paint_transparency_grid(painter, rect(), palette()); if (m_image) { painter.draw_rect(m_editor_image_rect.inflated(2, 2), Color::Black); m_image->paint_into(painter, m_editor_image_rect); } if (m_active_layer) { painter.draw_rect(enclosing_int_rect(image_rect_to_editor_rect(m_active_layer->relative_rect())).inflated(2, 2), Color::Black); } } Gfx::FloatRect ImageEditor::layer_rect_to_editor_rect(const Layer& layer, const Gfx::IntRect& layer_rect) const { return image_rect_to_editor_rect(layer_rect.translated(layer.location())); } Gfx::FloatRect ImageEditor::image_rect_to_editor_rect(const Gfx::IntRect& image_rect) const { Gfx::FloatRect editor_rect; editor_rect.set_location(image_position_to_editor_position(image_rect.location())); editor_rect.set_width((float)image_rect.width() * m_scale); editor_rect.set_height((float)image_rect.height() * m_scale); return editor_rect; } Gfx::FloatRect ImageEditor::editor_rect_to_image_rect(const Gfx::IntRect& editor_rect) const { Gfx::FloatRect image_rect; image_rect.set_location(editor_position_to_image_position(editor_rect.location())); image_rect.set_width((float)editor_rect.width() / m_scale); image_rect.set_height((float)editor_rect.height() / m_scale); return image_rect; } Gfx::FloatPoint ImageEditor::layer_position_to_editor_position(const Layer& layer, const Gfx::IntPoint& layer_position) const { return image_position_to_editor_position(layer_position.translated(layer.location())); } Gfx::FloatPoint ImageEditor::image_position_to_editor_position(const Gfx::IntPoint& image_position) const { Gfx::FloatPoint editor_position; editor_position.set_x(m_editor_image_rect.x() + ((float)image_position.x() * m_scale)); editor_position.set_y(m_editor_image_rect.y() + ((float)image_position.y() * m_scale)); return editor_position; } Gfx::FloatPoint ImageEditor::editor_position_to_image_position(const Gfx::IntPoint& editor_position) const { Gfx::FloatPoint image_position; image_position.set_x(((float)editor_position.x() - m_editor_image_rect.x()) / m_scale); image_position.set_y(((float)editor_position.y() - m_editor_image_rect.y()) / m_scale); return image_position; } void ImageEditor::second_paint_event(GUI::PaintEvent& event) { if (m_active_tool && m_active_layer) m_active_tool->on_second_paint(*m_active_layer, event); } GUI::MouseEvent ImageEditor::event_with_pan_and_scale_applied(const GUI::MouseEvent& event) const { auto image_position = editor_position_to_image_position(event.position()); return { static_cast(event.type()), Gfx::IntPoint(image_position.x(), image_position.y()), event.buttons(), event.button(), event.modifiers(), event.wheel_delta() }; } GUI::MouseEvent ImageEditor::event_adjusted_for_layer(const GUI::MouseEvent& event, const Layer& layer) const { auto image_position = editor_position_to_image_position(event.position()); image_position.move_by(-layer.location().x(), -layer.location().y()); return { static_cast(event.type()), Gfx::IntPoint(image_position.x(), image_position.y()), event.buttons(), event.button(), event.modifiers(), event.wheel_delta() }; } void ImageEditor::mousedown_event(GUI::MouseEvent& event) { if (event.button() == GUI::MouseButton::Middle) { m_click_position = event.position(); m_saved_pan_origin = m_pan_origin; return; } if (!m_active_tool) return; if (is(*m_active_tool)) { if (auto* other_layer = layer_at_editor_position(event.position())) { set_active_layer(other_layer); } } if (!m_active_layer) return; auto layer_event = event_adjusted_for_layer(event, *m_active_layer); auto image_event = event_with_pan_and_scale_applied(event); m_active_tool->on_mousedown(*m_active_layer, layer_event, image_event); } void ImageEditor::mousemove_event(GUI::MouseEvent& event) { if (event.buttons() & GUI::MouseButton::Middle) { auto delta = event.position() - m_click_position; m_pan_origin = m_saved_pan_origin.translated( -delta.x() / m_scale, -delta.y() / m_scale); relayout(); return; } if (!m_active_layer || !m_active_tool) return; auto layer_event = event_adjusted_for_layer(event, *m_active_layer); auto image_event = event_with_pan_and_scale_applied(event); m_active_tool->on_mousemove(*m_active_layer, layer_event, image_event); } void ImageEditor::mouseup_event(GUI::MouseEvent& event) { if (!m_active_layer || !m_active_tool) return; auto layer_event = event_adjusted_for_layer(event, *m_active_layer); auto image_event = event_with_pan_and_scale_applied(event); m_active_tool->on_mouseup(*m_active_layer, layer_event, image_event); } void ImageEditor::mousewheel_event(GUI::MouseEvent& event) { auto scale_delta = -event.wheel_delta() * 0.1f; scale_centered_on_position(event.position(), scale_delta); } void ImageEditor::context_menu_event(GUI::ContextMenuEvent& event) { if (!m_active_layer || !m_active_tool) return; m_active_tool->on_context_menu(*m_active_layer, event); } void ImageEditor::resize_event(GUI::ResizeEvent& event) { relayout(); GUI::Frame::resize_event(event); } void ImageEditor::keydown_event(GUI::KeyEvent& event) { if (m_active_tool) m_active_tool->on_keydown(event); } void ImageEditor::keyup_event(GUI::KeyEvent& event) { if (m_active_tool) m_active_tool->on_keyup(event); } void ImageEditor::set_active_layer(Layer* layer) { if (m_active_layer == layer) return; m_active_layer = layer; if (m_active_layer) { size_t index = 0; for (; index < m_image->layer_count(); ++index) { if (&m_image->layer(index) == layer) break; } if (on_active_layer_change) on_active_layer_change(layer); } else { if (on_active_layer_change) on_active_layer_change({}); } layers_did_change(); } void ImageEditor::set_active_tool(Tool* tool) { if (m_active_tool == tool) return; if (m_active_tool) m_active_tool->clear(); m_active_tool = tool; if (m_active_tool) m_active_tool->setup(*this); } void ImageEditor::layers_did_change() { update(); } Color ImageEditor::color_for(GUI::MouseButton button) const { if (button == GUI::MouseButton::Left) return m_primary_color; if (button == GUI::MouseButton::Right) return m_secondary_color; VERIFY_NOT_REACHED(); } Color ImageEditor::color_for(const GUI::MouseEvent& event) const { if (event.buttons() & GUI::MouseButton::Left) return m_primary_color; if (event.buttons() & GUI::MouseButton::Right) return m_secondary_color; VERIFY_NOT_REACHED(); } void ImageEditor::set_primary_color(Color color) { if (m_primary_color == color) return; m_primary_color = color; if (on_primary_color_change) on_primary_color_change(color); } void ImageEditor::set_secondary_color(Color color) { if (m_secondary_color == color) return; m_secondary_color = color; if (on_secondary_color_change) on_secondary_color_change(color); } Layer* ImageEditor::layer_at_editor_position(const Gfx::IntPoint& editor_position) { if (!m_image) return nullptr; auto image_position = editor_position_to_image_position(editor_position); for (ssize_t i = m_image->layer_count() - 1; i >= 0; --i) { auto& layer = m_image->layer(i); if (!layer.is_visible()) continue; if (layer.relative_rect().contains(Gfx::IntPoint(image_position.x(), image_position.y()))) return const_cast(&layer); } return nullptr; } void ImageEditor::scale_centered_on_position(const Gfx::IntPoint& position, float scale_delta) { auto old_scale = m_scale; m_scale += scale_delta; if (m_scale < 0.1f) m_scale = 0.1f; if (m_scale > 100.0f) m_scale = 100.0f; auto focus_point = Gfx::FloatPoint( m_pan_origin.x() - ((float)position.x() - (float)width() / 2.0) / old_scale, m_pan_origin.y() - ((float)position.y() - (float)height() / 2.0) / old_scale); m_pan_origin = Gfx::FloatPoint( focus_point.x() - m_scale / old_scale * (focus_point.x() - m_pan_origin.x()), focus_point.y() - m_scale / old_scale * (focus_point.y() - m_pan_origin.y())); if (old_scale != m_scale) relayout(); } void ImageEditor::relayout() { if (!image()) return; auto& image = *this->image(); Gfx::IntSize new_size; new_size.set_width(image.size().width() * m_scale); new_size.set_height(image.size().height() * m_scale); m_editor_image_rect.set_size(new_size); Gfx::IntPoint new_location; new_location.set_x((width() / 2) - (new_size.width() / 2) - (m_pan_origin.x() * m_scale)); new_location.set_y((height() / 2) - (new_size.height() / 2) - (m_pan_origin.y() * m_scale)); m_editor_image_rect.set_location(new_location); update(); } void ImageEditor::image_did_change() { update(); } void ImageEditor::image_select_layer(Layer* layer) { set_active_layer(layer); } }