mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-30 15:32:38 +00:00
Flip command changes to moving pixels state (fix #1738)
This commit is contained in:
parent
3ed516bc11
commit
a69a120e14
@ -21,10 +21,15 @@
|
||||
#include "app/document_api.h"
|
||||
#include "app/document_range.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/editor/moving_pixels_state.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui/timeline/timeline.h"
|
||||
#include "app/ui/toolbar.h"
|
||||
#include "app/util/expand_cel_canvas.h"
|
||||
#include "app/util/range_utils.h"
|
||||
#include "doc/algorithm/flip_image.h"
|
||||
@ -37,6 +42,7 @@
|
||||
#include "fmt/format.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
|
||||
namespace app {
|
||||
|
||||
FlipCommand::FlipCommand()
|
||||
@ -63,156 +69,164 @@ bool FlipCommand::onEnabled(Context* context)
|
||||
|
||||
void FlipCommand::onExecute(Context* context)
|
||||
{
|
||||
Site site = context->activeSite();
|
||||
Timeline* timeline = App::instance()->timeline();
|
||||
LockTimelineRange lockRange(timeline);
|
||||
|
||||
CelList cels;
|
||||
if (m_flipMask) {
|
||||
auto range = timeline->range();
|
||||
if (range.enabled()) {
|
||||
cels = get_unlocked_unique_cels(site.sprite(), range);
|
||||
}
|
||||
else if (site.cel() &&
|
||||
site.layer() &&
|
||||
site.layer()->isEditable()) {
|
||||
// If we want to flip the visible mask for the current cel,
|
||||
// we can go to MovingPixelsState.
|
||||
if (static_cast<app::Document*>(site.document())->isMaskVisible()) {
|
||||
// Select marquee tool
|
||||
if (tools::Tool* tool = App::instance()->toolBox()
|
||||
->getToolById(tools::WellKnownTools::RectangularMarquee)) {
|
||||
ToolBar::instance()->selectTool(tool);
|
||||
current_editor->startFlipTransformation(m_flipType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
cels.push_back(site.cel());
|
||||
}
|
||||
|
||||
if (cels.empty()) {
|
||||
StatusBar::instance()->showTip(
|
||||
1000, Strings::statusbar_tips_all_layers_are_locked().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Flip the whole sprite (even locked layers)
|
||||
else {
|
||||
for (Cel* cel : site.sprite()->uniqueCels())
|
||||
cels.push_back(cel);
|
||||
}
|
||||
|
||||
ContextWriter writer(context);
|
||||
Document* document = writer.document();
|
||||
Sprite* sprite = writer.sprite();
|
||||
Transaction transaction(writer.context(), friendlyName());
|
||||
DocumentApi api = document->getApi(transaction);
|
||||
|
||||
Mask* mask = document->mask();
|
||||
if (m_flipMask && document->isMaskVisible()) {
|
||||
Site site = *writer.site();
|
||||
|
||||
{
|
||||
Transaction transaction(writer.context(), friendlyName());
|
||||
DocumentApi api = document->getApi(transaction);
|
||||
for (Cel* cel : cels) {
|
||||
// TODO add support to flip masked part of a reference layer
|
||||
if (cel->layer()->isReference())
|
||||
continue;
|
||||
|
||||
Timeline* timeline = App::instance()->timeline();
|
||||
LockTimelineRange lockRange(timeline);
|
||||
site.frame(cel->frame());
|
||||
site.layer(cel->layer());
|
||||
|
||||
CelList cels;
|
||||
if (m_flipMask) {
|
||||
auto range = timeline->range();
|
||||
if (range.enabled()) {
|
||||
cels = get_unlocked_unique_cels(sprite, range);
|
||||
}
|
||||
else if (writer.cel() &&
|
||||
writer.layer() &&
|
||||
writer.layer()->isEditable()) {
|
||||
cels.push_back(writer.cel());
|
||||
}
|
||||
int x, y;
|
||||
Image* image = site.image(&x, &y);
|
||||
if (!image)
|
||||
continue;
|
||||
|
||||
if (cels.empty()) {
|
||||
StatusBar::instance()->showTip(
|
||||
1000, Strings::statusbar_tips_all_layers_are_locked().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Flip the whole sprite (even locked layers)
|
||||
else {
|
||||
for (Cel* cel : sprite->uniqueCels())
|
||||
cels.push_back(cel);
|
||||
}
|
||||
|
||||
Mask* mask = document->mask();
|
||||
if (m_flipMask && document->isMaskVisible()) {
|
||||
Site site = *writer.site();
|
||||
|
||||
for (Cel* cel : cels) {
|
||||
// TODO add support to flip masked part of a reference layer
|
||||
if (cel->layer()->isReference())
|
||||
// When the mask is inside the cel, we can try to flip the
|
||||
// pixels inside the image.
|
||||
if (cel->bounds().contains(mask->bounds())) {
|
||||
gfx::Rect flipBounds = mask->bounds();
|
||||
flipBounds.offset(-x, -y);
|
||||
flipBounds &= image->bounds();
|
||||
if (flipBounds.isEmpty())
|
||||
continue;
|
||||
|
||||
site.frame(cel->frame());
|
||||
site.layer(cel->layer());
|
||||
if (mask->bitmap() && !mask->isRectangular())
|
||||
transaction.execute(new cmd::FlipMaskedCel(cel, m_flipType));
|
||||
else
|
||||
api.flipImage(image, flipBounds, m_flipType);
|
||||
|
||||
int x, y;
|
||||
Image* image = site.image(&x, &y);
|
||||
if (!image)
|
||||
if (cel->layer()->isTransparent())
|
||||
transaction.execute(new cmd::TrimCel(cel));
|
||||
}
|
||||
// When the mask is bigger than the cel bounds, we have to
|
||||
// expand the cel, make the flip, and shrink it again.
|
||||
else {
|
||||
gfx::Rect flipBounds = (sprite->bounds() & mask->bounds());
|
||||
if (flipBounds.isEmpty())
|
||||
continue;
|
||||
|
||||
// When the mask is inside the cel, we can try to flip the
|
||||
// pixels inside the image.
|
||||
if (cel->bounds().contains(mask->bounds())) {
|
||||
gfx::Rect flipBounds = mask->bounds();
|
||||
flipBounds.offset(-x, -y);
|
||||
flipBounds &= image->bounds();
|
||||
if (flipBounds.isEmpty())
|
||||
continue;
|
||||
ExpandCelCanvas expand(
|
||||
site, cel->layer(),
|
||||
TiledMode::NONE, transaction,
|
||||
ExpandCelCanvas::None);
|
||||
|
||||
if (mask->bitmap() && !mask->isRectangular())
|
||||
transaction.execute(new cmd::FlipMaskedCel(cel, m_flipType));
|
||||
else
|
||||
api.flipImage(image, flipBounds, m_flipType);
|
||||
expand.validateDestCanvas(gfx::Region(flipBounds));
|
||||
|
||||
if (cel->layer()->isTransparent())
|
||||
transaction.execute(new cmd::TrimCel(cel));
|
||||
}
|
||||
// When the mask is bigger than the cel bounds, we have to
|
||||
// expand the cel, make the flip, and shrink it again.
|
||||
else {
|
||||
gfx::Rect flipBounds = (sprite->bounds() & mask->bounds());
|
||||
if (flipBounds.isEmpty())
|
||||
continue;
|
||||
if (mask->bitmap() && !mask->isRectangular())
|
||||
doc::algorithm::flip_image_with_mask(
|
||||
expand.getDestCanvas(), mask, m_flipType,
|
||||
document->bgColor(cel->layer()));
|
||||
else
|
||||
doc::algorithm::flip_image(
|
||||
expand.getDestCanvas(),
|
||||
flipBounds, m_flipType);
|
||||
|
||||
ExpandCelCanvas expand(
|
||||
site, cel->layer(),
|
||||
TiledMode::NONE, transaction,
|
||||
ExpandCelCanvas::None);
|
||||
|
||||
expand.validateDestCanvas(gfx::Region(flipBounds));
|
||||
|
||||
if (mask->bitmap() && !mask->isRectangular())
|
||||
doc::algorithm::flip_image_with_mask(
|
||||
expand.getDestCanvas(), mask, m_flipType,
|
||||
document->bgColor(cel->layer()));
|
||||
else
|
||||
doc::algorithm::flip_image(
|
||||
expand.getDestCanvas(),
|
||||
flipBounds, m_flipType);
|
||||
|
||||
expand.commit();
|
||||
}
|
||||
expand.commit();
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (Cel* cel : cels) {
|
||||
Image* image = cel->image();
|
||||
}
|
||||
else {
|
||||
for (Cel* cel : cels) {
|
||||
Image* image = cel->image();
|
||||
|
||||
// Flip reference layer cel
|
||||
if (cel->layer()->isReference()) {
|
||||
gfx::RectF bounds = cel->boundsF();
|
||||
// Flip reference layer cel
|
||||
if (cel->layer()->isReference()) {
|
||||
gfx::RectF bounds = cel->boundsF();
|
||||
|
||||
if (m_flipType == doc::algorithm::FlipHorizontal)
|
||||
bounds.x = sprite->width() - bounds.w - bounds.x;
|
||||
if (m_flipType == doc::algorithm::FlipVertical)
|
||||
bounds.y = sprite->height() - bounds.h - bounds.y;
|
||||
if (m_flipType == doc::algorithm::FlipHorizontal)
|
||||
bounds.x = sprite->width() - bounds.w - bounds.x;
|
||||
if (m_flipType == doc::algorithm::FlipVertical)
|
||||
bounds.y = sprite->height() - bounds.h - bounds.y;
|
||||
|
||||
transaction.execute(new cmd::SetCelBoundsF(cel, bounds));
|
||||
}
|
||||
else {
|
||||
api.setCelPosition
|
||||
(sprite, cel,
|
||||
(m_flipType == doc::algorithm::FlipHorizontal ?
|
||||
sprite->width() - image->width() - cel->x():
|
||||
cel->x()),
|
||||
(m_flipType == doc::algorithm::FlipVertical ?
|
||||
sprite->height() - image->height() - cel->y():
|
||||
cel->y()));
|
||||
}
|
||||
|
||||
api.flipImage(image, image->bounds(), m_flipType);
|
||||
transaction.execute(new cmd::SetCelBoundsF(cel, bounds));
|
||||
}
|
||||
else {
|
||||
api.setCelPosition
|
||||
(sprite, cel,
|
||||
(m_flipType == doc::algorithm::FlipHorizontal ?
|
||||
sprite->width() - image->width() - cel->x():
|
||||
cel->x()),
|
||||
(m_flipType == doc::algorithm::FlipVertical ?
|
||||
sprite->height() - image->height() - cel->y():
|
||||
cel->y()));
|
||||
}
|
||||
|
||||
api.flipImage(image, image->bounds(), m_flipType);
|
||||
}
|
||||
|
||||
// Flip the mask.
|
||||
Image* maskBitmap = mask->bitmap();
|
||||
if (maskBitmap) {
|
||||
transaction.execute(new cmd::FlipMask(document, m_flipType));
|
||||
|
||||
// Flip the mask position because the
|
||||
if (!m_flipMask)
|
||||
transaction.execute(
|
||||
new cmd::SetMaskPosition(
|
||||
document,
|
||||
gfx::Point(
|
||||
(m_flipType == doc::algorithm::FlipHorizontal ?
|
||||
sprite->width() - mask->bounds().x2():
|
||||
mask->bounds().x),
|
||||
(m_flipType == doc::algorithm::FlipVertical ?
|
||||
sprite->height() - mask->bounds().y2():
|
||||
mask->bounds().y))));
|
||||
|
||||
document->generateMaskBoundaries();
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
// Flip the mask.
|
||||
Image* maskBitmap = mask->bitmap();
|
||||
if (maskBitmap) {
|
||||
transaction.execute(new cmd::FlipMask(document, m_flipType));
|
||||
|
||||
// Flip the mask position because the
|
||||
if (!m_flipMask)
|
||||
transaction.execute(
|
||||
new cmd::SetMaskPosition(
|
||||
document,
|
||||
gfx::Point(
|
||||
(m_flipType == doc::algorithm::FlipHorizontal ?
|
||||
sprite->width() - mask->bounds().x2():
|
||||
mask->bounds().x),
|
||||
(m_flipType == doc::algorithm::FlipVertical ?
|
||||
sprite->height() - mask->bounds().y2():
|
||||
mask->bounds().y))));
|
||||
|
||||
document->generateMaskBoundaries();
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
update_screen_for_document(document);
|
||||
}
|
||||
|
||||
|
@ -2238,6 +2238,14 @@ void Editor::startSelectionTransformation(const gfx::Point& move, double angle)
|
||||
}
|
||||
}
|
||||
|
||||
void Editor::startFlipTransformation(doc::algorithm::FlipType flipType)
|
||||
{
|
||||
if (MovingPixelsState* movingPixels = dynamic_cast<MovingPixelsState*>(m_state.get()))
|
||||
movingPixels->flip(flipType);
|
||||
else if (StandbyState* standby = dynamic_cast<StandbyState*>(m_state.get()))
|
||||
standby->startFlipTransformation(this, flipType);
|
||||
}
|
||||
|
||||
void Editor::notifyScrollChanged()
|
||||
{
|
||||
m_observers.notifyScrollChanged(this);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "app/ui/editor/editor_observers.h"
|
||||
#include "app/ui/editor/editor_state.h"
|
||||
#include "app/ui/editor/editor_states_history.h"
|
||||
#include "doc/algorithm/flip_type.h"
|
||||
#include "doc/document_observer.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/image_buffer.h"
|
||||
@ -218,6 +219,8 @@ namespace app {
|
||||
void pasteImage(const Image* image, const Mask* mask = nullptr);
|
||||
|
||||
void startSelectionTransformation(const gfx::Point& move, double angle);
|
||||
|
||||
void startFlipTransformation(doc::algorithm::FlipType flipType);
|
||||
|
||||
// Used by EditorView to notify changes in the view's scroll
|
||||
// position.
|
||||
|
@ -133,6 +133,11 @@ void MovingPixelsState::rotate(double angle)
|
||||
{
|
||||
m_pixelsMovement->rotate(angle);
|
||||
}
|
||||
|
||||
void MovingPixelsState::flip(doc::algorithm::FlipType flipType)
|
||||
{
|
||||
m_pixelsMovement->flipImage(flipType);
|
||||
}
|
||||
|
||||
void MovingPixelsState::onEnterState(Editor* editor)
|
||||
{
|
||||
|
@ -34,6 +34,7 @@ namespace app {
|
||||
|
||||
void translate(const gfx::Point& delta);
|
||||
void rotate(double angle);
|
||||
void flip(doc::algorithm::FlipType flipType);
|
||||
|
||||
// EditorState
|
||||
virtual void onEnterState(Editor* editor) override;
|
||||
|
@ -673,6 +673,14 @@ void StandbyState::startSelectionTransformation(Editor* editor,
|
||||
}
|
||||
}
|
||||
|
||||
void StandbyState::startFlipTransformation(Editor* editor, doc::algorithm::FlipType flipType)
|
||||
{
|
||||
transformSelection(editor, NULL, NoHandle);
|
||||
|
||||
if (MovingPixelsState* movingPixels = dynamic_cast<MovingPixelsState*>(editor->getState().get()))
|
||||
movingPixels->flip(flipType);
|
||||
}
|
||||
|
||||
void StandbyState::transformSelection(Editor* editor, MouseMessage* msg, HandleType handle)
|
||||
{
|
||||
Document* document = editor->document();
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "app/ui/editor/editor_decorator.h"
|
||||
#include "app/ui/editor/handle_type.h"
|
||||
#include "app/ui/editor/state_with_wheel_behavior.h"
|
||||
#include "doc/algorithm/flip_type.h"
|
||||
#include "obs/connection.h"
|
||||
|
||||
namespace app {
|
||||
@ -47,7 +48,9 @@ namespace app {
|
||||
virtual Transformation getTransformation(Editor* editor);
|
||||
|
||||
void startSelectionTransformation(Editor* editor, const gfx::Point& move, double angle);
|
||||
|
||||
|
||||
void startFlipTransformation(Editor* editor, doc::algorithm::FlipType flipType);
|
||||
|
||||
protected:
|
||||
void callEyedropper(Editor* editor, const ui::MouseMessage* msg);
|
||||
bool checkStartDrawingStraightLine(Editor* editor, const ui::MouseMessage* msg);
|
||||
|
Loading…
x
Reference in New Issue
Block a user