mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-30 15:32:38 +00:00
Fix shift after flip throws error (fix #1873)
This commit is contained in:
parent
5cb0adc46a
commit
1c05ea10bb
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -40,20 +41,11 @@ void ShiftMaskedCel::onUndo()
|
|||||||
void ShiftMaskedCel::shift(int dx, int dy)
|
void ShiftMaskedCel::shift(int dx, int dy)
|
||||||
{
|
{
|
||||||
Cel* cel = this->cel();
|
Cel* cel = this->cel();
|
||||||
Image* image = cel->image();
|
|
||||||
Mask* mask = static_cast<Doc*>(cel->document())->mask();
|
Mask* mask = static_cast<Doc*>(cel->document())->mask();
|
||||||
ASSERT(mask->bitmap());
|
ASSERT(mask->bitmap());
|
||||||
if (!mask->bitmap())
|
if (!mask->bitmap())
|
||||||
return;
|
return;
|
||||||
|
doc::algorithm::shift_image_with_mask(cel, mask, dx, dy);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cmd
|
} // namespace cmd
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -20,7 +21,8 @@ namespace app {
|
|||||||
MoveMaskCommand();
|
MoveMaskCommand();
|
||||||
|
|
||||||
Target getTarget() const { return m_target; }
|
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:
|
protected:
|
||||||
bool onNeedsParams() const override { return true; }
|
bool onNeedsParams() const override { return true; }
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "app/commands/cmd_rotate.h"
|
#include "app/commands/cmd_rotate.h"
|
||||||
#include "app/commands/command.h"
|
#include "app/commands/command.h"
|
||||||
#include "app/commands/commands.h"
|
#include "app/commands/commands.h"
|
||||||
|
#include "app/commands/move_thing.h"
|
||||||
#include "app/console.h"
|
#include "app/console.h"
|
||||||
#include "app/modules/gui.h"
|
#include "app/modules/gui.h"
|
||||||
#include "app/pref/preferences.h"
|
#include "app/pref/preferences.h"
|
||||||
@ -140,6 +141,11 @@ void MovingPixelsState::flip(doc::algorithm::FlipType flipType)
|
|||||||
m_pixelsMovement->flipImage(flipType);
|
m_pixelsMovement->flipImage(flipType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MovingPixelsState::shift(int dx, int dy)
|
||||||
|
{
|
||||||
|
m_pixelsMovement->shift(dx, dy);
|
||||||
|
}
|
||||||
|
|
||||||
void MovingPixelsState::onEnterState(Editor* editor)
|
void MovingPixelsState::onEnterState(Editor* editor)
|
||||||
{
|
{
|
||||||
StandbyState::onEnterState(editor);
|
StandbyState::onEnterState(editor);
|
||||||
@ -501,9 +507,15 @@ void MovingPixelsState::onBeforeCommandExecution(CommandExecutionEvent& ev)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// We don't need to drop the pixels if a MoveMaskCommand of Content is executed.
|
// 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) {
|
if (moveMaskCmd->getTarget() == MoveMaskCommand::Content) {
|
||||||
// Do not drop pixels
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -35,6 +36,7 @@ namespace app {
|
|||||||
void translate(const gfx::Point& delta);
|
void translate(const gfx::Point& delta);
|
||||||
void rotate(double angle);
|
void rotate(double angle);
|
||||||
void flip(doc::algorithm::FlipType flipType);
|
void flip(doc::algorithm::FlipType flipType);
|
||||||
|
void shift(int dx, int dy);
|
||||||
|
|
||||||
// EditorState
|
// EditorState
|
||||||
virtual void onEnterState(Editor* editor) override;
|
virtual void onEnterState(Editor* editor) override;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -32,6 +33,7 @@
|
|||||||
#include "doc/algorithm/flip_image.h"
|
#include "doc/algorithm/flip_image.h"
|
||||||
#include "doc/algorithm/rotate.h"
|
#include "doc/algorithm/rotate.h"
|
||||||
#include "doc/algorithm/rotsprite.h"
|
#include "doc/algorithm/rotsprite.h"
|
||||||
|
#include "doc/algorithm/shift_image.h"
|
||||||
#include "doc/blend_internals.h"
|
#include "doc/blend_internals.h"
|
||||||
#include "doc/cel.h"
|
#include "doc/cel.h"
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
@ -173,6 +175,20 @@ void PixelsMovement::rotate(double angle)
|
|||||||
update_screen_for_document(m_document);
|
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()
|
void PixelsMovement::trim()
|
||||||
{
|
{
|
||||||
ContextWriter writer(m_reader, 1000);
|
ContextWriter writer(m_reader, 1000);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -105,6 +106,8 @@ namespace app {
|
|||||||
// simulate RotateCommand when we're inside MovingPixelsState.
|
// simulate RotateCommand when we're inside MovingPixelsState.
|
||||||
void rotate(double angle);
|
void rotate(double angle);
|
||||||
|
|
||||||
|
void shift(int dx, int dy);
|
||||||
|
|
||||||
const Transformation& getTransformation() const { return m_currentData; }
|
const Transformation& getTransformation() const { return m_currentData; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2019 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2018 David Capello
|
// Copyright (c) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -10,7 +11,10 @@
|
|||||||
|
|
||||||
#include "doc/algorithm/shift_image.h"
|
#include "doc/algorithm/shift_image.h"
|
||||||
|
|
||||||
|
#include "base/pi.h"
|
||||||
#include "gfx/rect.h"
|
#include "gfx/rect.h"
|
||||||
|
#include "doc/algorithm/shrink_bounds.h"
|
||||||
|
#include "doc/cel.h"
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
#include "doc/mask.h"
|
#include "doc/mask.h"
|
||||||
#include "doc/primitives.h"
|
#include "doc/primitives.h"
|
||||||
@ -20,31 +24,96 @@
|
|||||||
namespace doc {
|
namespace doc {
|
||||||
namespace algorithm {
|
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();
|
gfx::Rect bounds(image->bounds());
|
||||||
if (bounds.isEmpty())
|
if (cos(angle) < -sqrt(2)/2) {
|
||||||
return;
|
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
|
// To simplify the algorithm we use a copy of the original image, we
|
||||||
// could avoid this copy swapping rows and columns.
|
// could avoid this copy swapping rows and columns.
|
||||||
ImageRef crop(crop_image(image, bounds.x, bounds.y, bounds.w, bounds.h,
|
ImageRef crop(crop_image(image, bounds.x, bounds.y, bounds.w, bounds.h,
|
||||||
image->maskColor()));
|
image->maskColor()));
|
||||||
|
|
||||||
int u = dx;
|
|
||||||
int v = dy;
|
|
||||||
while (u < 0) u += bounds.w;
|
|
||||||
while (v < 0) v += bounds.h;
|
|
||||||
|
|
||||||
for (int y=0; y<bounds.h; ++y) {
|
for (int y=0; y<bounds.h; ++y) {
|
||||||
for (int x=0; x<bounds.w; ++x) {
|
for (int x=0; x<bounds.w; ++x) {
|
||||||
put_pixel(image,
|
put_pixel(image,
|
||||||
bounds.x + ((u + x) % bounds.w),
|
(bounds.w + dx + x) % bounds.w,
|
||||||
bounds.y + ((v + y) % bounds.h),
|
(bounds.h + dy + y) % bounds.h,
|
||||||
get_pixel(crop.get(), x, y));
|
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 algorithm
|
||||||
} // namespace doc
|
} // namespace doc
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2019 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2015 David Capello
|
// Copyright (c) 2001-2015 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -9,13 +10,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
|
class Cel;
|
||||||
class Image;
|
class Image;
|
||||||
class Mask;
|
class Mask;
|
||||||
|
|
||||||
namespace algorithm {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user