From 1c05ea10bb7c6cde8bfc04711ebf104f5b7da658 Mon Sep 17 00:00:00 2001 From: Gaspar Capello Date: Wed, 30 Jan 2019 16:36:53 -0300 Subject: [PATCH] Fix shift after flip throws error (fix #1873) --- src/app/cmd/shift_masked_cel.cpp | 12 +-- src/app/commands/cmd_move_mask.h | 4 +- src/app/ui/editor/moving_pixels_state.cpp | 14 +++- src/app/ui/editor/moving_pixels_state.h | 2 + src/app/ui/editor/pixels_movement.cpp | 16 ++++ src/app/ui/editor/pixels_movement.h | 3 + src/doc/algorithm/shift_image.cpp | 99 +++++++++++++++++++---- src/doc/algorithm/shift_image.h | 6 +- 8 files changed, 127 insertions(+), 29 deletions(-) diff --git a/src/app/cmd/shift_masked_cel.cpp b/src/app/cmd/shift_masked_cel.cpp index 9dc8bbea3..8f860ea38 100644 --- a/src/app/cmd/shift_masked_cel.cpp +++ b/src/app/cmd/shift_masked_cel.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -40,20 +41,11 @@ void ShiftMaskedCel::onUndo() void ShiftMaskedCel::shift(int dx, int dy) { Cel* cel = this->cel(); - Image* image = cel->image(); Mask* mask = static_cast(cel->document())->mask(); ASSERT(mask->bitmap()); if (!mask->bitmap()) return; - - int x = cel->x(); - int y = cel->y(); - - mask->offsetOrigin(-x, -y); - doc::algorithm::shift_image_with_mask(image, mask, dx, dy); - mask->offsetOrigin(x, y); - - image->incrementVersion(); + doc::algorithm::shift_image_with_mask(cel, mask, dx, dy); } } // namespace cmd diff --git a/src/app/commands/cmd_move_mask.h b/src/app/commands/cmd_move_mask.h index 8b0d5c96d..559e19fc8 100644 --- a/src/app/commands/cmd_move_mask.h +++ b/src/app/commands/cmd_move_mask.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -20,7 +21,8 @@ namespace app { MoveMaskCommand(); Target getTarget() const { return m_target; } - gfx::Point getDelta(Context* context) const; + MoveThing getMoveThing() const { return m_moveThing; } + bool isWrap() const { return m_wrap; } protected: bool onNeedsParams() const override { return true; } diff --git a/src/app/ui/editor/moving_pixels_state.cpp b/src/app/ui/editor/moving_pixels_state.cpp index f8fa3cc0d..74df06bad 100644 --- a/src/app/ui/editor/moving_pixels_state.cpp +++ b/src/app/ui/editor/moving_pixels_state.cpp @@ -18,6 +18,7 @@ #include "app/commands/cmd_rotate.h" #include "app/commands/command.h" #include "app/commands/commands.h" +#include "app/commands/move_thing.h" #include "app/console.h" #include "app/modules/gui.h" #include "app/pref/preferences.h" @@ -140,6 +141,11 @@ void MovingPixelsState::flip(doc::algorithm::FlipType flipType) m_pixelsMovement->flipImage(flipType); } +void MovingPixelsState::shift(int dx, int dy) +{ + m_pixelsMovement->shift(dx, dy); +} + void MovingPixelsState::onEnterState(Editor* editor) { StandbyState::onEnterState(editor); @@ -501,9 +507,15 @@ void MovingPixelsState::onBeforeCommandExecution(CommandExecutionEvent& ev) return; // We don't need to drop the pixels if a MoveMaskCommand of Content is executed. - if (MoveMaskCommand* moveMaskCmd = dynamic_cast(ev.command())) { + if (MoveMaskCommand* moveMaskCmd = dynamic_cast(command)) { if (moveMaskCmd->getTarget() == MoveMaskCommand::Content) { // Do not drop pixels + // Verify Shift condition of the MoveMaskCommand (i.e. wrap = true) + if (moveMaskCmd->isWrap()) { + gfx::Point delta = moveMaskCmd->getMoveThing().getDelta(UIContext::instance()); + m_pixelsMovement->shift(delta.x, delta.y); + } + ev.cancel(); return; } } diff --git a/src/app/ui/editor/moving_pixels_state.h b/src/app/ui/editor/moving_pixels_state.h index 71b5f9f19..6c9f97305 100644 --- a/src/app/ui/editor/moving_pixels_state.h +++ b/src/app/ui/editor/moving_pixels_state.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -35,6 +36,7 @@ namespace app { void translate(const gfx::Point& delta); void rotate(double angle); void flip(doc::algorithm::FlipType flipType); + void shift(int dx, int dy); // EditorState virtual void onEnterState(Editor* editor) override; diff --git a/src/app/ui/editor/pixels_movement.cpp b/src/app/ui/editor/pixels_movement.cpp index 3bfadf431..9b7b27a4a 100644 --- a/src/app/ui/editor/pixels_movement.cpp +++ b/src/app/ui/editor/pixels_movement.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -32,6 +33,7 @@ #include "doc/algorithm/flip_image.h" #include "doc/algorithm/rotate.h" #include "doc/algorithm/rotsprite.h" +#include "doc/algorithm/shift_image.h" #include "doc/blend_internals.h" #include "doc/cel.h" #include "doc/image.h" @@ -173,6 +175,20 @@ void PixelsMovement::rotate(double angle) update_screen_for_document(m_document); } +void PixelsMovement::shift(int dx, int dy) +{ + doc::algorithm::shift_image(m_originalImage, dx, dy, m_currentData.angle()); + { + ContextWriter writer(m_reader, 1000); + + redrawExtraImage(); + redrawCurrentMask(); + updateDocumentMask(); + + update_screen_for_document(m_document); + } +} + void PixelsMovement::trim() { ContextWriter writer(m_reader, 1000); diff --git a/src/app/ui/editor/pixels_movement.h b/src/app/ui/editor/pixels_movement.h index 49495ca29..64efc12e9 100644 --- a/src/app/ui/editor/pixels_movement.h +++ b/src/app/ui/editor/pixels_movement.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -104,6 +105,8 @@ namespace app { // Rotates the image and the mask the given angle. It's used to // simulate RotateCommand when we're inside MovingPixelsState. void rotate(double angle); + + void shift(int dx, int dy); const Transformation& getTransformation() const { return m_currentData; } diff --git a/src/doc/algorithm/shift_image.cpp b/src/doc/algorithm/shift_image.cpp index 78766481d..06c326424 100644 --- a/src/doc/algorithm/shift_image.cpp +++ b/src/doc/algorithm/shift_image.cpp @@ -1,4 +1,5 @@ // Aseprite Document Library +// Copyright (c) 2019 Igara Studio S.A. // Copyright (c) 2001-2018 David Capello // // This file is released under the terms of the MIT license. @@ -10,7 +11,10 @@ #include "doc/algorithm/shift_image.h" +#include "base/pi.h" #include "gfx/rect.h" +#include "doc/algorithm/shrink_bounds.h" +#include "doc/cel.h" #include "doc/image.h" #include "doc/mask.h" #include "doc/primitives.h" @@ -20,31 +24,96 @@ namespace doc { namespace algorithm { -void shift_image_with_mask(Image* image, const Mask* mask, int dx, int dy) +void shift_image(Image* image, int dx, int dy, double angle) { - gfx::Rect bounds = mask->bounds(); - if (bounds.isEmpty()) - return; - + gfx::Rect bounds(image->bounds()); + if (cos(angle) < -sqrt(2)/2) { + dx = -dx; + dy = -dy; + } + else if (sin(angle) >= sqrt(2)/2) { + double aux; + aux = dx; + dx = -dy; + dy = aux; + } + else if (sin(angle) < -sqrt(2)/2) { + double aux; + aux = dx; + dx = dy; + dy = -aux; + } // To simplify the algorithm we use a copy of the original image, we // could avoid this copy swapping rows and columns. ImageRef crop(crop_image(image, bounds.x, bounds.y, bounds.w, bounds.h, - image->maskColor())); - - int u = dx; - int v = dy; - while (u < 0) u += bounds.w; - while (v < 0) v += bounds.h; + image->maskColor())); for (int y=0; ybounds().isEmpty()); + ASSERT(!mask->bounds().isEmpty()); + + // Making a image which bounds are equal to the UNION of cel->bounds() and mask->bounds() + gfx::Rect compCelBounds = cel->bounds() | mask->bounds(); + ImageRef compImage(Image::create(cel->image()->pixelFormat(), compCelBounds.w, compCelBounds.h)); + + // Making a Rect which represents the mask bounds of the original MASK relative to + // the new COMPOUND IMAGE (compImage) + gfx::Point maskCelGap(0, 0); + if (cel->bounds().x < mask->bounds().x) + maskCelGap.x = mask->bounds().x - cel->bounds().x; + if (cel->bounds().y < mask->bounds().y) + maskCelGap.y = mask->bounds().y - cel->bounds().y; + gfx::Rect maskedBounds(maskCelGap.x, maskCelGap.y, mask->bounds().w, mask->bounds().h); + + // Making one combined image: Image with the Mask Bounds (unfilled spaces were filled with mask color) + compImage->copy(cel->image(), gfx::Clip(cel->position().x-compCelBounds.x, + cel->position().y-compCelBounds.y, + 0, 0, + cel->bounds().w, cel->bounds().h)); + + // Making a copy of only the image which will be shiftted + ImageRef imageToShift(Image::create(compImage->pixelFormat(), maskedBounds.w, maskedBounds.h)); + imageToShift->copy(compImage.get(), gfx::Clip(0, 0, maskedBounds)); + + // Shiftting the masked area of the COMPOUND IMAGE (compImage). + int initialX = maskedBounds.x; + int initialY = maskedBounds.y; + int finalX = maskedBounds.x2(); + int finalY = maskedBounds.y2(); + for (int y=initialY; ybounds(); + if (algorithm::shrink_bounds(compImage.get(), newBounds, compImage->maskColor())) { + compCelBounds.offset(newBounds.x, newBounds.y); + compCelBounds.setSize(newBounds.size()); + } + ImageRef finalImage(Image::create(compImage->pixelFormat(), compCelBounds.w, compCelBounds.h)); + finalImage->copy(compImage.get(), gfx::Clip(0, 0, newBounds.x, newBounds.y, + compCelBounds.w, compCelBounds.h)); + + // Final cel content assign + finalImage->incrementVersion(); // TODO this should be in app::cmd module + cel->data()->setBounds(compCelBounds); + cel->data()->setImage(finalImage); +} } // namespace algorithm } // namespace doc diff --git a/src/doc/algorithm/shift_image.h b/src/doc/algorithm/shift_image.h index 7520d351c..beae5243e 100644 --- a/src/doc/algorithm/shift_image.h +++ b/src/doc/algorithm/shift_image.h @@ -1,4 +1,5 @@ // Aseprite Document Library +// Copyright (c) 2019 Igara Studio S.A. // Copyright (c) 2001-2015 David Capello // // This file is released under the terms of the MIT license. @@ -9,13 +10,14 @@ #pragma once namespace doc { + class Cel; class Image; class Mask; namespace algorithm { - void shift_image_with_mask(Image* image, const Mask* mask, int dx, int dy); - + void shift_image(Image* image, int dx, int dy, double angle); + void shift_image_with_mask(Cel* cel, const Mask* mask, int dx, int dy); } }