mirror of https://github.com/aseprite/aseprite.git
				
				
				
			Add RotSprite algorithm for PixelsMovement (issue 121)
* Added app::RotationAlgorithm enum. * Added app::ISelectionSettings::get/setRotationAlgorithm. * Added app::SelectionSettingsObserver::onSetRotationAlgorithm. * Added raster::image_rotsprite() function. * Added ContextBar::RotAlgorithmField class to select the rotation algorithm. * Now the mask isn't updated constantly on PixelsMovement::moveImage8), in this way when the user release the mouse button is when we recalculate the mask (to get better performance when the user is scaling/rotating the image).
This commit is contained in:
		
							parent
							
								
									12a46f5ca3
								
							
						
					
					
						commit
						02597dcdec
					
				|  | @ -0,0 +1,34 @@ | ||||||
|  | /* Aseprite
 | ||||||
|  |  * Copyright (C) 2001-2013  David Capello | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef APP_SETTINGS_ROTATION_ALGORITHM_H_INCLUDED | ||||||
|  | #define APP_SETTINGS_ROTATION_ALGORITHM_H_INCLUDED | ||||||
|  | 
 | ||||||
|  | namespace app { | ||||||
|  | 
 | ||||||
|  |   enum RotationAlgorithm { | ||||||
|  |     kFastRotationAlgorithm, | ||||||
|  |     kRotSpriteRotationAlgorithm, | ||||||
|  | 
 | ||||||
|  |     kFirstRotationAlgorithm = kFastRotationAlgorithm, | ||||||
|  |     kLastRotationAlgorithm = kRotSpriteRotationAlgorithm | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  | } // namespace app
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -21,6 +21,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "app/color.h" | #include "app/color.h" | ||||||
| #include "app/settings/ink_type.h" | #include "app/settings/ink_type.h" | ||||||
|  | #include "app/settings/rotation_algorithm.h" | ||||||
| #include "gfx/point.h" | #include "gfx/point.h" | ||||||
| #include "gfx/rect.h" | #include "gfx/rect.h" | ||||||
| #include "raster/pen_type.h" | #include "raster/pen_type.h" | ||||||
|  | @ -126,8 +127,10 @@ namespace app { | ||||||
| 
 | 
 | ||||||
|     // Mask color used during a move operation
 |     // Mask color used during a move operation
 | ||||||
|     virtual app::Color getMoveTransparentColor() = 0; |     virtual app::Color getMoveTransparentColor() = 0; | ||||||
|  |     virtual RotationAlgorithm getRotationAlgorithm() = 0; | ||||||
| 
 | 
 | ||||||
|     virtual void setMoveTransparentColor(app::Color color) = 0; |     virtual void setMoveTransparentColor(app::Color color) = 0; | ||||||
|  |     virtual void setRotationAlgorithm(RotationAlgorithm algorithm) = 0; | ||||||
| 
 | 
 | ||||||
|     virtual void addObserver(SelectionSettingsObserver* observer) = 0; |     virtual void addObserver(SelectionSettingsObserver* observer) = 0; | ||||||
|     virtual void removeObserver(SelectionSettingsObserver* observer) = 0; |     virtual void removeObserver(SelectionSettingsObserver* observer) = 0; | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "app/color.h" | #include "app/color.h" | ||||||
| #include "app/settings/ink_type.h" | #include "app/settings/ink_type.h" | ||||||
|  | #include "app/settings/rotation_algorithm.h" | ||||||
| #include "raster/pen_type.h" | #include "raster/pen_type.h" | ||||||
| 
 | 
 | ||||||
| namespace app { | namespace app { | ||||||
|  | @ -57,6 +58,7 @@ namespace app { | ||||||
|     virtual ~SelectionSettingsObserver() {} |     virtual ~SelectionSettingsObserver() {} | ||||||
| 
 | 
 | ||||||
|     virtual void onSetMoveTransparentColor(app::Color newColor) {} |     virtual void onSetMoveTransparentColor(app::Color newColor) {} | ||||||
|  |     virtual void onSetRotationAlgorithm(RotationAlgorithm algorithm) {} | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   class GlobalSettingsObserver { |   class GlobalSettingsObserver { | ||||||
|  |  | ||||||
|  | @ -149,15 +149,18 @@ public: | ||||||
|   UISelectionSettingsImpl(); |   UISelectionSettingsImpl(); | ||||||
|   ~UISelectionSettingsImpl(); |   ~UISelectionSettingsImpl(); | ||||||
| 
 | 
 | ||||||
|   void setMoveTransparentColor(app::Color color); |  | ||||||
| 
 |  | ||||||
|   app::Color getMoveTransparentColor(); |   app::Color getMoveTransparentColor(); | ||||||
|  |   RotationAlgorithm getRotationAlgorithm(); | ||||||
|  | 
 | ||||||
|  |   void setMoveTransparentColor(app::Color color); | ||||||
|  |   void setRotationAlgorithm(RotationAlgorithm algorithm); | ||||||
| 
 | 
 | ||||||
|   void addObserver(SelectionSettingsObserver* observer); |   void addObserver(SelectionSettingsObserver* observer); | ||||||
|   void removeObserver(SelectionSettingsObserver* observer); |   void removeObserver(SelectionSettingsObserver* observer); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|   app::Color m_moveTransparentColor; |   app::Color m_moveTransparentColor; | ||||||
|  |   RotationAlgorithm m_rotationAlgorithm; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // anonymous namespace
 | } // anonymous namespace
 | ||||||
|  | @ -640,23 +643,42 @@ IToolSettings* UISettingsImpl::getToolSettings(tools::Tool* tool) | ||||||
| 
 | 
 | ||||||
| namespace { | namespace { | ||||||
| 
 | 
 | ||||||
| UISelectionSettingsImpl::UISelectionSettingsImpl() : m_moveTransparentColor(app::Color::fromMask()) | UISelectionSettingsImpl::UISelectionSettingsImpl() : | ||||||
|  |   m_moveTransparentColor(app::Color::fromMask()), | ||||||
|  |   m_rotationAlgorithm(kFastRotationAlgorithm) | ||||||
| { | { | ||||||
|  |   m_rotationAlgorithm = (RotationAlgorithm)get_config_int("Tools", "RotAlgorithm", m_rotationAlgorithm); | ||||||
|  |   m_rotationAlgorithm = MID( | ||||||
|  |     kFirstRotationAlgorithm, | ||||||
|  |     m_rotationAlgorithm, | ||||||
|  |     kLastRotationAlgorithm); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| UISelectionSettingsImpl::~UISelectionSettingsImpl() | UISelectionSettingsImpl::~UISelectionSettingsImpl() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | app::Color UISelectionSettingsImpl::getMoveTransparentColor() | ||||||
|  | { | ||||||
|  |   return m_moveTransparentColor; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RotationAlgorithm UISelectionSettingsImpl::getRotationAlgorithm() | ||||||
|  | { | ||||||
|  |   return m_rotationAlgorithm; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void UISelectionSettingsImpl::setMoveTransparentColor(app::Color color) | void UISelectionSettingsImpl::setMoveTransparentColor(app::Color color) | ||||||
| { | { | ||||||
|   m_moveTransparentColor = color; |   m_moveTransparentColor = color; | ||||||
|   notifyObservers(&SelectionSettingsObserver::onSetMoveTransparentColor, color); |   notifyObservers(&SelectionSettingsObserver::onSetMoveTransparentColor, color); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| app::Color UISelectionSettingsImpl::getMoveTransparentColor() | void UISelectionSettingsImpl::setRotationAlgorithm(RotationAlgorithm algorithm) | ||||||
| { | { | ||||||
|   return m_moveTransparentColor; |   m_rotationAlgorithm = algorithm; | ||||||
|  |   set_config_int("Tools", "RotAlgorithm", m_rotationAlgorithm); | ||||||
|  |   notifyObservers(&SelectionSettingsObserver::onSetRotationAlgorithm, algorithm); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void UISelectionSettingsImpl::addObserver(SelectionSettingsObserver* observer) | void UISelectionSettingsImpl::addObserver(SelectionSettingsObserver* observer) | ||||||
|  |  | ||||||
|  | @ -44,6 +44,7 @@ | ||||||
| #include "ui/combobox.h" | #include "ui/combobox.h" | ||||||
| #include "ui/int_entry.h" | #include "ui/int_entry.h" | ||||||
| #include "ui/label.h" | #include "ui/label.h" | ||||||
|  | #include "ui/listitem.h" | ||||||
| #include "ui/popup_window.h" | #include "ui/popup_window.h" | ||||||
| #include "ui/preferred_size_event.h" | #include "ui/preferred_size_event.h" | ||||||
| #include "ui/theme.h" | #include "ui/theme.h" | ||||||
|  | @ -353,6 +354,39 @@ protected: | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class ContextBar::RotAlgorithmField : public ComboBox | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |   RotAlgorithmField() { | ||||||
|  |     addItem(new Item("Fast", kFastRotationAlgorithm)); | ||||||
|  |     addItem(new Item("RotSprite", kRotSpriteRotationAlgorithm)); | ||||||
|  | 
 | ||||||
|  |     setSelectedItemIndex((int) | ||||||
|  |       UIContext::instance()->settings()->selection() | ||||||
|  |       ->getRotationAlgorithm()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |   void onChange() OVERRIDE { | ||||||
|  |     UIContext::instance()->settings()->selection() | ||||||
|  |       ->setRotationAlgorithm(static_cast<Item*>(getSelectedItem())->algo()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |   class Item : public ListItem { | ||||||
|  |   public: | ||||||
|  |     Item(const std::string& text, RotationAlgorithm algo) : | ||||||
|  |       ListItem(text), | ||||||
|  |       m_algo(algo) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     RotationAlgorithm algo() const { return m_algo; } | ||||||
|  | 
 | ||||||
|  |   private: | ||||||
|  |     RotationAlgorithm m_algo; | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| ContextBar::ContextBar() | ContextBar::ContextBar() | ||||||
|   : Box(JI_HORIZONTAL) |   : Box(JI_HORIZONTAL) | ||||||
| { | { | ||||||
|  | @ -386,6 +420,8 @@ ContextBar::ContextBar() | ||||||
|   addChild(m_selectionOptionsBox = new HBox()); |   addChild(m_selectionOptionsBox = new HBox()); | ||||||
|   m_selectionOptionsBox->addChild(new Label("Transparent Color:")); |   m_selectionOptionsBox->addChild(new Label("Transparent Color:")); | ||||||
|   m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField); |   m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField); | ||||||
|  |   m_selectionOptionsBox->addChild(new Label("Algorithm:")); | ||||||
|  |   m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField()); | ||||||
| 
 | 
 | ||||||
|   TooltipManager* tooltipManager = new TooltipManager(); |   TooltipManager* tooltipManager = new TooltipManager(); | ||||||
|   addChild(tooltipManager); |   addChild(tooltipManager); | ||||||
|  |  | ||||||
|  | @ -51,6 +51,7 @@ namespace app { | ||||||
|     class SprayWidthField; |     class SprayWidthField; | ||||||
|     class SpraySpeedField; |     class SpraySpeedField; | ||||||
|     class TransparentColorField; |     class TransparentColorField; | ||||||
|  |     class RotAlgorithmField; | ||||||
| 
 | 
 | ||||||
|     ui::Label* m_brushLabel; |     ui::Label* m_brushLabel; | ||||||
|     BrushTypeField* m_brushType; |     BrushTypeField* m_brushType; | ||||||
|  | @ -67,6 +68,7 @@ namespace app { | ||||||
|     SpraySpeedField* m_spraySpeed; |     SpraySpeedField* m_spraySpeed; | ||||||
|     ui::Box* m_selectionOptionsBox; |     ui::Box* m_selectionOptionsBox; | ||||||
|     TransparentColorField* m_transparentColor; |     TransparentColorField* m_transparentColor; | ||||||
|  |     RotAlgorithmField* m_rotAlgo; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| } // namespace app
 | } // namespace app
 | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ | ||||||
| #include "raster/image.h" | #include "raster/image.h" | ||||||
| #include "raster/mask.h" | #include "raster/mask.h" | ||||||
| #include "raster/rotate.h" | #include "raster/rotate.h" | ||||||
|  | #include "raster/rotsprite.h" | ||||||
| #include "raster/sprite.h" | #include "raster/sprite.h" | ||||||
| 
 | 
 | ||||||
| namespace app { | namespace app { | ||||||
|  | @ -73,10 +74,14 @@ PixelsMovement::PixelsMovement(Context* context, | ||||||
| 
 | 
 | ||||||
|   m_initialMask = new Mask(*m_document->getMask()); |   m_initialMask = new Mask(*m_document->getMask()); | ||||||
|   m_currentMask = new Mask(*m_document->getMask()); |   m_currentMask = new Mask(*m_document->getMask()); | ||||||
|  | 
 | ||||||
|  |   UIContext::instance()->getSettings()->selection()->addObserver(this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PixelsMovement::~PixelsMovement() | PixelsMovement::~PixelsMovement() | ||||||
| { | { | ||||||
|  |   UIContext::instance()->getSettings()->selection()->removeObserver(this); | ||||||
|  | 
 | ||||||
|   delete m_originalImage; |   delete m_originalImage; | ||||||
|   delete m_initialMask; |   delete m_initialMask; | ||||||
|   delete m_currentMask; |   delete m_currentMask; | ||||||
|  | @ -394,12 +399,6 @@ void PixelsMovement::moveImage(int x, int y, MoveModifier moveModifier) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   redrawExtraImage(); |   redrawExtraImage(); | ||||||
|   redrawCurrentMask(); |  | ||||||
| 
 |  | ||||||
|   if (m_firstDrop) |  | ||||||
|     m_document->getApi().copyToCurrentMask(m_currentMask); |  | ||||||
|   else |  | ||||||
|     m_document->setMask(m_currentMask); |  | ||||||
| 
 | 
 | ||||||
|   m_document->setTransformation(m_currentData); |   m_document->setTransformation(m_currentData); | ||||||
| 
 | 
 | ||||||
|  | @ -440,11 +439,7 @@ Image* PixelsMovement::getDraggedImageCopy(gfx::Point& origin) | ||||||
|   int height = rightBottom.y - leftTop.y; |   int height = rightBottom.y - leftTop.y; | ||||||
|   base::UniquePtr<Image> image(Image::create(m_sprite->getPixelFormat(), width, height)); |   base::UniquePtr<Image> image(Image::create(m_sprite->getPixelFormat(), width, height)); | ||||||
|   clear_image(image, image->getMaskColor()); |   clear_image(image, image->getMaskColor()); | ||||||
|   image_parallelogram(image, m_originalImage, |   drawParallelogram(image, m_originalImage, corners, leftTop); | ||||||
|                       corners.leftTop().x-leftTop.x, corners.leftTop().y-leftTop.y, |  | ||||||
|                       corners.rightTop().x-leftTop.x, corners.rightTop().y-leftTop.y, |  | ||||||
|                       corners.rightBottom().x-leftTop.x, corners.rightBottom().y-leftTop.y, |  | ||||||
|                       corners.leftBottom().x-leftTop.x, corners.leftBottom().y-leftTop.y); |  | ||||||
| 
 | 
 | ||||||
|   origin = leftTop; |   origin = leftTop; | ||||||
| 
 | 
 | ||||||
|  | @ -513,7 +508,9 @@ void PixelsMovement::dropImageTemporarily() | ||||||
|       m_currentData.displacePivotTo(gfx::Point(newPivot.x, newPivot.y)); |       m_currentData.displacePivotTo(gfx::Point(newPivot.x, newPivot.y)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     m_document->generateMaskBoundaries(m_currentMask); |     redrawCurrentMask(); | ||||||
|  |     updateDocumentMask(); | ||||||
|  | 
 | ||||||
|     update_screen_for_document(m_document); |     update_screen_for_document(m_document); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -593,11 +590,7 @@ void PixelsMovement::redrawExtraImage() | ||||||
|   // Transform the extra-cel which is the chunk of pixels that the user is moving.
 |   // Transform the extra-cel which is the chunk of pixels that the user is moving.
 | ||||||
|   Image* extraImage = m_document->getExtraCelImage(); |   Image* extraImage = m_document->getExtraCelImage(); | ||||||
|   clear_image(extraImage, extraImage->getMaskColor()); |   clear_image(extraImage, extraImage->getMaskColor()); | ||||||
|   image_parallelogram(extraImage, m_originalImage, |   drawParallelogram(extraImage, m_originalImage, corners, gfx::Point(0, 0)); | ||||||
|                       corners.leftTop().x, corners.leftTop().y, |  | ||||||
|                       corners.rightTop().x, corners.rightTop().y, |  | ||||||
|                       corners.rightBottom().x, corners.rightBottom().y, |  | ||||||
|                       corners.leftBottom().x, corners.leftBottom().y); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PixelsMovement::redrawCurrentMask() | void PixelsMovement::redrawCurrentMask() | ||||||
|  | @ -610,13 +603,56 @@ void PixelsMovement::redrawCurrentMask() | ||||||
|   m_currentMask->replace(0, 0, m_sprite->getWidth(), m_sprite->getHeight()); |   m_currentMask->replace(0, 0, m_sprite->getWidth(), m_sprite->getHeight()); | ||||||
|   m_currentMask->freeze(); |   m_currentMask->freeze(); | ||||||
|   clear_image(m_currentMask->getBitmap(), 0); |   clear_image(m_currentMask->getBitmap(), 0); | ||||||
|   image_parallelogram(m_currentMask->getBitmap(), |   drawParallelogram(m_currentMask->getBitmap(), m_initialMask->getBitmap(), | ||||||
|                       m_initialMask->getBitmap(), |     corners, gfx::Point(0, 0)); | ||||||
|                       corners.leftTop().x, corners.leftTop().y, | 
 | ||||||
|                       corners.rightTop().x, corners.rightTop().y, |  | ||||||
|                       corners.rightBottom().x, corners.rightBottom().y, |  | ||||||
|                       corners.leftBottom().x, corners.leftBottom().y); |  | ||||||
|   m_currentMask->unfreeze(); |   m_currentMask->unfreeze(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void PixelsMovement::drawParallelogram(raster::Image* dst, raster::Image* src, | ||||||
|  |   const gfx::Transformation::Corners& corners, | ||||||
|  |   const gfx::Point& leftTop) | ||||||
|  | { | ||||||
|  |   switch (UIContext::instance()->getSettings()->selection()->getRotationAlgorithm()) { | ||||||
|  | 
 | ||||||
|  |     case kFastRotationAlgorithm: | ||||||
|  |       image_parallelogram(dst, src, | ||||||
|  |         corners.leftTop().x-leftTop.x, corners.leftTop().y-leftTop.y, | ||||||
|  |         corners.rightTop().x-leftTop.x, corners.rightTop().y-leftTop.y, | ||||||
|  |         corners.rightBottom().x-leftTop.x, corners.rightBottom().y-leftTop.y, | ||||||
|  |         corners.leftBottom().x-leftTop.x, corners.leftBottom().y-leftTop.y); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case kRotSpriteRotationAlgorithm: | ||||||
|  |       image_rotsprite(dst, src, | ||||||
|  |         corners.leftTop().x-leftTop.x, corners.leftTop().y-leftTop.y, | ||||||
|  |         corners.rightTop().x-leftTop.x, corners.rightTop().y-leftTop.y, | ||||||
|  |         corners.rightBottom().x-leftTop.x, corners.rightBottom().y-leftTop.y, | ||||||
|  |         corners.leftBottom().x-leftTop.x, corners.leftBottom().y-leftTop.y); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PixelsMovement::onSetRotationAlgorithm(RotationAlgorithm algorithm) | ||||||
|  | { | ||||||
|  |   redrawExtraImage(); | ||||||
|  |   redrawCurrentMask(); | ||||||
|  |   updateDocumentMask(); | ||||||
|  | 
 | ||||||
|  |   update_screen_for_document(m_document); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PixelsMovement::updateDocumentMask() | ||||||
|  | { | ||||||
|  |   if (m_firstDrop) { | ||||||
|  |     m_firstDrop = false; | ||||||
|  |     m_document->getApi().copyToCurrentMask(m_currentMask); | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |     m_document->setMask(m_currentMask); | ||||||
|  | 
 | ||||||
|  |   m_document->generateMaskBoundaries(m_currentMask); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace app
 | } // namespace app
 | ||||||
|  |  | ||||||
|  | @ -19,11 +19,13 @@ | ||||||
| #ifndef APP_UI_EDITOR_PIXELS_MOVEMENT_H_INCLUDED | #ifndef APP_UI_EDITOR_PIXELS_MOVEMENT_H_INCLUDED | ||||||
| #define APP_UI_EDITOR_PIXELS_MOVEMENT_H_INCLUDED | #define APP_UI_EDITOR_PIXELS_MOVEMENT_H_INCLUDED | ||||||
| 
 | 
 | ||||||
| #include "app/ui/editor/handle_type.h" |  | ||||||
| #include "app/context_access.h" | #include "app/context_access.h" | ||||||
|  | #include "app/settings/settings_observers.h" | ||||||
|  | #include "app/ui/editor/handle_type.h" | ||||||
|  | #include "app/undo_transaction.h" | ||||||
|  | #include "base/compiler_specific.h" | ||||||
| #include "gfx/size.h" | #include "gfx/size.h" | ||||||
| #include "raster/algorithm/flip_type.h" | #include "raster/algorithm/flip_type.h" | ||||||
| #include "app/undo_transaction.h" |  | ||||||
| 
 | 
 | ||||||
| namespace raster { | namespace raster { | ||||||
|   class Image; |   class Image; | ||||||
|  | @ -38,7 +40,7 @@ namespace app { | ||||||
|   // feedback, drag, and drop the specified image in the constructor
 |   // feedback, drag, and drop the specified image in the constructor
 | ||||||
|   // (which generally would be the selected region or the clipboard
 |   // (which generally would be the selected region or the clipboard
 | ||||||
|   // content).
 |   // content).
 | ||||||
|   class PixelsMovement { |   class PixelsMovement : public SelectionSettingsObserver { | ||||||
|   public: |   public: | ||||||
|     enum MoveModifier { |     enum MoveModifier { | ||||||
|       NormalMovement = 1, |       NormalMovement = 1, | ||||||
|  | @ -94,9 +96,16 @@ namespace app { | ||||||
| 
 | 
 | ||||||
|     const gfx::Transformation& getTransformation() const { return m_currentData; } |     const gfx::Transformation& getTransformation() const { return m_currentData; } | ||||||
| 
 | 
 | ||||||
|  |   protected: | ||||||
|  |     void onSetRotationAlgorithm(RotationAlgorithm algorithm) OVERRIDE; | ||||||
|  | 
 | ||||||
|   private: |   private: | ||||||
|     void redrawExtraImage(); |     void redrawExtraImage(); | ||||||
|     void redrawCurrentMask(); |     void redrawCurrentMask(); | ||||||
|  |     void drawParallelogram(raster::Image* dst, raster::Image* src, | ||||||
|  |       const gfx::Transformation::Corners& corners, | ||||||
|  |       const gfx::Point& leftTop); | ||||||
|  |     void updateDocumentMask(); | ||||||
| 
 | 
 | ||||||
|     const ContextReader m_reader; |     const ContextReader m_reader; | ||||||
|     Document* m_document; |     Document* m_document; | ||||||
|  |  | ||||||
|  | @ -32,5 +32,6 @@ add_library(raster-lib | ||||||
|   quantization.cpp |   quantization.cpp | ||||||
|   rgbmap.cpp |   rgbmap.cpp | ||||||
|   rotate.cpp |   rotate.cpp | ||||||
|  |   rotsprite.cpp | ||||||
|   sprite.cpp |   sprite.cpp | ||||||
|   stock.cpp) |   stock.cpp) | ||||||
|  |  | ||||||
|  | @ -447,6 +447,12 @@ namespace raster { | ||||||
|       return *this; |       return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     ImageIteratorT& operator+=(int diff) { | ||||||
|  |       while (diff-- > 0) | ||||||
|  |         operator++(); | ||||||
|  |       return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     ImageIteratorT operator++(int) { |     ImageIteratorT operator++(int) { | ||||||
|       ImageIteratorT tmp(*this); |       ImageIteratorT tmp(*this); | ||||||
|       operator++(); |       operator++(); | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ | ||||||
| #include "raster/image.h" | #include "raster/image.h" | ||||||
| #include "raster/image_bits.h" | #include "raster/image_bits.h" | ||||||
| #include "raster/primitives.h" | #include "raster/primitives.h" | ||||||
|  | #include "raster/primitives_fast.h" | ||||||
| 
 | 
 | ||||||
| #include <allegro.h> | #include <allegro.h> | ||||||
| #include <allegro/internal/aintern.h> | #include <allegro/internal/aintern.h> | ||||||
|  | @ -39,37 +40,58 @@ static void ase_rotate_scale_flip_coordinates(fixed w, fixed h, | ||||||
|                                               int h_flip, int v_flip, |                                               int h_flip, int v_flip, | ||||||
|                                               fixed xs[4], fixed ys[4]); |                                               fixed xs[4], fixed ys[4]); | ||||||
| 
 | 
 | ||||||
|  | template<typename ImageTraits, typename BlendFunc> | ||||||
|  | static void image_scale_tpl(Image* dst, const Image* src, int x, int y, int w, int h, BlendFunc blend) | ||||||
|  | { | ||||||
|  |   int src_w = src->getWidth(); | ||||||
|  |   int src_h = src->getHeight(); | ||||||
|  | 
 | ||||||
|  |   for (int v=0; v<h; ++v) { | ||||||
|  |     for (int u=0; u<w; ++u) { | ||||||
|  |       color_t c = get_pixel_fast<ImageTraits>(src, src_w*u/w, src_h*v/h); | ||||||
|  |       put_pixel_fast<ImageTraits>(dst, x+u, y+v, | ||||||
|  |         blend(get_pixel_fast<ImageTraits>(dst, x+u, y+v), c)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static color_t rgba_blender(color_t back, color_t front) { | ||||||
|  |   return rgba_blenders[BLEND_MODE_NORMAL](back, front, 255); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static color_t grayscale_blender(color_t back, color_t front) { | ||||||
|  |   return graya_blenders[BLEND_MODE_NORMAL](back, front, 255); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static color_t if_blender(color_t back, color_t front) { | ||||||
|  |   if (front != 0) | ||||||
|  |     return front; | ||||||
|  |   else | ||||||
|  |     return back; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void image_scale(Image *dst, Image *src, int x, int y, int w, int h) | void image_scale(Image *dst, Image *src, int x, int y, int w, int h) | ||||||
| { | { | ||||||
|   if (w == src->getWidth() && src->getHeight() == h) |   if (w == src->getWidth() && src->getHeight() == h) | ||||||
|     composite_image(dst, src, x, y, 255, BLEND_MODE_NORMAL); |     composite_image(dst, src, x, y, 255, BLEND_MODE_NORMAL); | ||||||
|   else { |   else { | ||||||
|     BLEND_COLOR blender = NULL; |     switch (dst->getPixelFormat()) { | ||||||
|     int u, v, c; |  | ||||||
| 
 | 
 | ||||||
|     if (dst->getPixelFormat() == IMAGE_RGB) |       case IMAGE_RGB: | ||||||
|       blender = rgba_blenders[BLEND_MODE_NORMAL]; |         image_scale_tpl<RgbTraits>(dst, src, x, y, w, h, rgba_blender); | ||||||
|     else if (dst->getPixelFormat() == IMAGE_GRAYSCALE) |         break; | ||||||
|       blender = graya_blenders[BLEND_MODE_NORMAL]; |  | ||||||
| 
 | 
 | ||||||
|     for (v=0; v<h; v++) { |       case IMAGE_GRAYSCALE: | ||||||
|       for (u=0; u<w; u++) { |         image_scale_tpl<GrayscaleTraits>(dst, src, x, y, w, h, grayscale_blender); | ||||||
|         c = get_pixel(src, src->getWidth()*u/w, src->getHeight()*v/h); |         break; | ||||||
| 
 | 
 | ||||||
|         switch (dst->getPixelFormat()) { |       case IMAGE_INDEXED: | ||||||
|  |         image_scale_tpl<IndexedTraits>(dst, src, x, y, w, h, if_blender); | ||||||
|  |         break; | ||||||
| 
 | 
 | ||||||
|           case IMAGE_RGB: |       case IMAGE_BITMAP: | ||||||
|           case IMAGE_GRAYSCALE: |         image_scale_tpl<BitmapTraits>(dst, src, x, y, w, h, if_blender); | ||||||
|             put_pixel(dst, x+u, y+v, |         break; | ||||||
|                       blender(get_pixel(dst, x+u, y+v), c, 255)); |  | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|           case IMAGE_INDEXED: |  | ||||||
|             if (c != 0) |  | ||||||
|               put_pixel(dst, x+u, y+v, c); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,199 @@ | ||||||
|  | /* Aseprite
 | ||||||
|  |  * Copyright (C) 2001-2013  David Capello | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifdef HAVE_CONFIG_H | ||||||
|  | #include "config.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include "base/unique_ptr.h" | ||||||
|  | #include "raster/blend.h" | ||||||
|  | #include "raster/image.h" | ||||||
|  | #include "raster/image_bits.h" | ||||||
|  | #include "raster/primitives.h" | ||||||
|  | #include "raster/primitives_fast.h" | ||||||
|  | #include "raster/rotate.h" | ||||||
|  | 
 | ||||||
|  | namespace raster { | ||||||
|  | 
 | ||||||
|  | // More information about EPX/Scale2x:
 | ||||||
|  | // http://en.wikipedia.org/wiki/Pixel_art_scaling_algorithms#EPX.2FScale2.C3.97.2FAdvMAME2.C3.97
 | ||||||
|  | // http://scale2x.sourceforge.net/algorithm.html
 | ||||||
|  | // http://scale2x.sourceforge.net/scale2xandepx.html
 | ||||||
|  | template<typename ImageTraits> | ||||||
|  | static void image_scale2x_tpl(Image* dst, const Image* src, int src_w, int src_h) | ||||||
|  | { | ||||||
|  | #if 0      // TODO complete this implementation that should be faster
 | ||||||
|  |            // than using a lot of get/put_pixel_fast calls.
 | ||||||
|  |   int dst_w = src_w*2; | ||||||
|  |   int dst_h = src_h*2; | ||||||
|  | 
 | ||||||
|  |   LockImageBits<ImageTraits> dstBits(dst, Image::WriteLock, gfx::Rect(0, 0, dst_w, dst_h)); | ||||||
|  |   const LockImageBits<ImageTraits> srcBits(src); | ||||||
|  | 
 | ||||||
|  |   LockImageBits<ImageTraits>::iterator dstRow0_it = dstBits.begin(); | ||||||
|  |   LockImageBits<ImageTraits>::iterator dstRow1_it = dstBits.begin(); | ||||||
|  |   LockImageBits<ImageTraits>::iterator dstRow0_end = dstBits.end(); | ||||||
|  |   LockImageBits<ImageTraits>::iterator dstRow1_end = dstBits.end(); | ||||||
|  | 
 | ||||||
|  |   // Iterators:
 | ||||||
|  |   //   A
 | ||||||
|  |   // C P B
 | ||||||
|  |   //   D
 | ||||||
|  |   //
 | ||||||
|  |   // These iterators are displaced through src image and are modified in this way:
 | ||||||
|  |   //
 | ||||||
|  |   // P: is the simplest one, we just start from (0, 0) to srcEnd.
 | ||||||
|  |   // A: starts from row 0 (so A = P in the first row), then we start
 | ||||||
|  |   //    again from the row 0.
 | ||||||
|  |   // B: It starts from (1, row) and in the last pixel we don't moved it.
 | ||||||
|  |   // C: It starts from (0, 0) and then it is moved with a delay.
 | ||||||
|  |   // D: It starts from row 1 and continues until we reach the last
 | ||||||
|  |   //    row, in that case we start D iterator again.
 | ||||||
|  |   //
 | ||||||
|  |   LockImageBits<ImageTraits>::const_iterator itP, itA, itB, itC, itD, savedD; | ||||||
|  |   LockImageBits<ImageTraits>::const_iterator srcEnd = srcBits.end(); | ||||||
|  |   color_t P, A, B, C, D; | ||||||
|  | 
 | ||||||
|  |   // Adjust iterators
 | ||||||
|  |   itP = itA = itB = itC = itD = savedD = srcBits.begin(); | ||||||
|  |   dstRow1_it += dst_w; | ||||||
|  |   itD += src->getWidth(); | ||||||
|  | 
 | ||||||
|  |   for (int y=0; y<src_h; ++y) { | ||||||
|  |     if (y == 1) itA = srcBits.begin(); | ||||||
|  |     if (y == src_h-2) savedD = itD; | ||||||
|  |     if (y == src_h-1) itD = savedD; | ||||||
|  |     ++itB; | ||||||
|  | 
 | ||||||
|  |     for (int x=0; x<src_w; ++x) { | ||||||
|  |       ASSERT(itP != srcEnd); | ||||||
|  |       ASSERT(itA != srcEnd); | ||||||
|  |       ASSERT(itB != srcEnd); | ||||||
|  |       ASSERT(itC != srcEnd); | ||||||
|  |       ASSERT(itD != srcEnd); | ||||||
|  |       ASSERT(dstRow0_it != dstRow0_end); | ||||||
|  |       ASSERT(dstRow1_it != dstRow1_end); | ||||||
|  | 
 | ||||||
|  |       P = *itP; | ||||||
|  |       A = *itA;                 // y-1
 | ||||||
|  |       B = *itB;                 // x+1
 | ||||||
|  |       C = *itC;                 // x-1
 | ||||||
|  |       D = *itD;                 // y+1
 | ||||||
|  | 
 | ||||||
|  |       *dstRow0_it = (C == A && C != D && A != B ? A: P); | ||||||
|  |       ++dstRow0_it; | ||||||
|  |       *dstRow0_it = (A == B && A != C && B != D ? B: P); | ||||||
|  |       ++dstRow0_it; | ||||||
|  | 
 | ||||||
|  |       *dstRow1_it = (D == C && D != B && C != A ? C: P); | ||||||
|  |       ++dstRow1_it; | ||||||
|  |       *dstRow1_it = (B == D && B != A && D != C ? D: P); | ||||||
|  |       ++dstRow1_it; | ||||||
|  | 
 | ||||||
|  |       ++itP; | ||||||
|  |       ++itA; | ||||||
|  |       if (x < src_w-2) ++itB; | ||||||
|  |       if (x > 0) ++itC; | ||||||
|  |       ++itD; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Adjust iterators for the next two rows.
 | ||||||
|  |     ++itB; | ||||||
|  |     ++itC; | ||||||
|  |     dstRow0_it += dst_w; | ||||||
|  |     if (y < src_h-1) | ||||||
|  |       dstRow1_it += dst_w; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // ASSERT(itP == srcEnd);
 | ||||||
|  |   // ASSERT(itA == srcEnd);
 | ||||||
|  |   // ASSERT(itB == srcEnd);
 | ||||||
|  |   // ASSERT(itC == srcEnd);
 | ||||||
|  |   // ASSERT(itD == srcEnd);
 | ||||||
|  |   ASSERT(dstRow0_it == dstRow0_end); | ||||||
|  |   ASSERT(dstRow1_it == dstRow1_end); | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | #define A c[0] | ||||||
|  | #define B c[1] | ||||||
|  | #define C c[2] | ||||||
|  | #define D c[3] | ||||||
|  | #define P c[4] | ||||||
|  | 
 | ||||||
|  |   color_t c[5]; | ||||||
|  |   for (int y=0; y<src_h; ++y) { | ||||||
|  |     for (int x=0; x<src_w; ++x) { | ||||||
|  |       P = get_pixel_fast<ImageTraits>(src, x, y); | ||||||
|  |       A = (y > 0 ? get_pixel_fast<ImageTraits>(src, x, y-1): P); | ||||||
|  |       B = (x < src_w-1 ? get_pixel_fast<ImageTraits>(src, x+1, y): P); | ||||||
|  |       C = (x > 0 ? get_pixel_fast<ImageTraits>(src, x-1, y): P); | ||||||
|  |       D = (y < src_h-1 ? get_pixel_fast<ImageTraits>(src, x, y+1): P); | ||||||
|  | 
 | ||||||
|  |       put_pixel_fast<ImageTraits>(dst, 2*x,   2*y,   (C == A && C != D && A != B ? A: P)); | ||||||
|  |       put_pixel_fast<ImageTraits>(dst, 2*x+1, 2*y,   (A == B && A != C && B != D ? B: P)); | ||||||
|  |       put_pixel_fast<ImageTraits>(dst, 2*x,   2*y+1, (D == C && D != B && C != A ? C: P)); | ||||||
|  |       put_pixel_fast<ImageTraits>(dst, 2*x+1, 2*y+1, (B == D && B != A && D != C ? D: P)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void image_scale2x(Image* dst, const Image* src, int src_w, int src_h) | ||||||
|  | { | ||||||
|  |   switch (src->getPixelFormat()) { | ||||||
|  |     case IMAGE_RGB:       image_scale2x_tpl<RgbTraits>(dst, src, src_w, src_h); break; | ||||||
|  |     case IMAGE_GRAYSCALE: image_scale2x_tpl<GrayscaleTraits>(dst, src, src_w, src_h); break; | ||||||
|  |     case IMAGE_INDEXED:   image_scale2x_tpl<IndexedTraits>(dst, src, src_w, src_h); break; | ||||||
|  |     case IMAGE_BITMAP:    image_scale2x_tpl<BitmapTraits>(dst, src, src_w, src_h); break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void image_rotsprite(Image* bmp, Image* spr, | ||||||
|  |                      int x1, int y1, int x2, int y2, | ||||||
|  |                      int x3, int y3, int x4, int y4) | ||||||
|  | { | ||||||
|  |   static ImageBufferPtr buf1, buf2, buf3; | ||||||
|  | 
 | ||||||
|  |   if (!buf1) buf1.reset(new ImageBuffer(1)); | ||||||
|  |   if (!buf2) buf2.reset(new ImageBuffer(1)); | ||||||
|  |   if (!buf3) buf3.reset(new ImageBuffer(1)); | ||||||
|  | 
 | ||||||
|  |   int scale = 8; | ||||||
|  |   base::UniquePtr<Image> bmp_copy(Image::create(bmp->getPixelFormat(), bmp->getWidth()*scale, bmp->getHeight()*scale, buf1)); | ||||||
|  |   base::UniquePtr<Image> tmp_copy(Image::create(spr->getPixelFormat(), spr->getWidth()*scale, spr->getHeight()*scale, buf2)); | ||||||
|  |   base::UniquePtr<Image> spr_copy(Image::create(spr->getPixelFormat(), spr->getWidth()*scale, spr->getHeight()*scale, buf3)); | ||||||
|  | 
 | ||||||
|  |   bmp_copy->clear(0); | ||||||
|  |   spr_copy->clear(0); | ||||||
|  |   spr_copy->copy(spr, 0, 0); | ||||||
|  | 
 | ||||||
|  |   for (int i=0; i<3; ++i) { | ||||||
|  |     tmp_copy->clear(0); | ||||||
|  |     image_scale2x(tmp_copy, spr_copy, spr->getWidth()*(1<<i), spr->getHeight()*(1<<i)); | ||||||
|  |     spr_copy->copy(tmp_copy, 0, 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   image_parallelogram(bmp_copy, spr_copy, | ||||||
|  |     x1*scale, y1*scale, x2*scale, y2*scale, | ||||||
|  |     x3*scale, y3*scale, x4*scale, y4*scale); | ||||||
|  | 
 | ||||||
|  |   image_scale(bmp, bmp_copy, 0, 0, bmp->getWidth(), bmp->getHeight()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace raster
 | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | /* Aseprite
 | ||||||
|  |  * Copyright (C) 2001-2013  David Capello | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef RASTER_ROTSPRITE_H_INCLUDED | ||||||
|  | #define RASTER_ROTSPRITE_H_INCLUDED | ||||||
|  | 
 | ||||||
|  | namespace raster { | ||||||
|  |   class Image; | ||||||
|  | 
 | ||||||
|  |   void image_rotsprite(Image* bmp, Image* spr, | ||||||
|  |     int x1, int y1, int x2, int y2, | ||||||
|  |     int x3, int y3, int x4, int y4); | ||||||
|  | 
 | ||||||
|  | } // namespace raster
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
		Loading…
	
		Reference in New Issue