Fix shift after flip throws error (fix #1873)

This commit is contained in:
Gaspar Capello 2019-01-30 16:36:53 -03:00 committed by David Capello
parent 5cb0adc46a
commit 1c05ea10bb
8 changed files with 127 additions and 29 deletions

View File

@ -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<Doc*>(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

View File

@ -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; }

View File

@ -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<MoveMaskCommand*>(ev.command())) {
if (MoveMaskCommand* moveMaskCmd = dynamic_cast<MoveMaskCommand*>(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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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; }

View File

@ -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; y<bounds.h; ++y) {
for (int x=0; x<bounds.w; ++x) {
put_pixel(image,
bounds.x + ((u + x) % bounds.w),
bounds.y + ((v + y) % bounds.h),
get_pixel(crop.get(), x, y));
put_pixel(image,
(bounds.w + dx + x) % bounds.w,
(bounds.h + dy + y) % bounds.h,
get_pixel(crop.get(), x, y));
}
}
}
void shift_image_with_mask(Cel* cel, const Mask* mask, int dx, int dy)
{
ASSERT(!cel->bounds().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; y<finalY; ++y) {
for (int x=initialX; x<finalX; ++x) {
put_pixel(compImage.get(),
initialX + (maskedBounds.w + dx + x-initialX) % maskedBounds.w,
initialY + (maskedBounds.h + dy + y-initialY) % maskedBounds.h,
get_pixel(imageToShift.get(), x - initialX, y - initialY));
}
}
// Bounds and Image shrinking (we have to fit compound image (compImage) and bounds (compCelBounds))
gfx::Rect newBounds = compImage->bounds();
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

View File

@ -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);
}
}