mirror of
https://github.com/aseprite/aseprite.git
synced 2024-09-02 13:09:27 +00:00
Fix shift after flip throws error (fix #1873)
This commit is contained in:
parent
5cb0adc46a
commit
1c05ea10bb
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user