mirror of https://github.com/aseprite/aseprite.git
				
				
				
			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