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/settings/ink_type.h" | ||||
| #include "app/settings/rotation_algorithm.h" | ||||
| #include "gfx/point.h" | ||||
| #include "gfx/rect.h" | ||||
| #include "raster/pen_type.h" | ||||
|  | @ -126,8 +127,10 @@ namespace app { | |||
| 
 | ||||
|     // Mask color used during a move operation
 | ||||
|     virtual app::Color getMoveTransparentColor() = 0; | ||||
|     virtual RotationAlgorithm getRotationAlgorithm() = 0; | ||||
| 
 | ||||
|     virtual void setMoveTransparentColor(app::Color color) = 0; | ||||
|     virtual void setRotationAlgorithm(RotationAlgorithm algorithm) = 0; | ||||
| 
 | ||||
|     virtual void addObserver(SelectionSettingsObserver* observer) = 0; | ||||
|     virtual void removeObserver(SelectionSettingsObserver* observer) = 0; | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| 
 | ||||
| #include "app/color.h" | ||||
| #include "app/settings/ink_type.h" | ||||
| #include "app/settings/rotation_algorithm.h" | ||||
| #include "raster/pen_type.h" | ||||
| 
 | ||||
| namespace app { | ||||
|  | @ -57,6 +58,7 @@ namespace app { | |||
|     virtual ~SelectionSettingsObserver() {} | ||||
| 
 | ||||
|     virtual void onSetMoveTransparentColor(app::Color newColor) {} | ||||
|     virtual void onSetRotationAlgorithm(RotationAlgorithm algorithm) {} | ||||
|   }; | ||||
| 
 | ||||
|   class GlobalSettingsObserver { | ||||
|  |  | |||
|  | @ -149,15 +149,18 @@ public: | |||
|   UISelectionSettingsImpl(); | ||||
|   ~UISelectionSettingsImpl(); | ||||
| 
 | ||||
|   void setMoveTransparentColor(app::Color color); | ||||
| 
 | ||||
|   app::Color getMoveTransparentColor(); | ||||
|   RotationAlgorithm getRotationAlgorithm(); | ||||
| 
 | ||||
|   void setMoveTransparentColor(app::Color color); | ||||
|   void setRotationAlgorithm(RotationAlgorithm algorithm); | ||||
| 
 | ||||
|   void addObserver(SelectionSettingsObserver* observer); | ||||
|   void removeObserver(SelectionSettingsObserver* observer); | ||||
| 
 | ||||
| private: | ||||
|   app::Color m_moveTransparentColor; | ||||
|   RotationAlgorithm m_rotationAlgorithm; | ||||
| }; | ||||
| 
 | ||||
| } // anonymous namespace
 | ||||
|  | @ -640,23 +643,42 @@ IToolSettings* UISettingsImpl::getToolSettings(tools::Tool* tool) | |||
| 
 | ||||
| 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() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| app::Color UISelectionSettingsImpl::getMoveTransparentColor() | ||||
| { | ||||
|   return m_moveTransparentColor; | ||||
| } | ||||
| 
 | ||||
| RotationAlgorithm UISelectionSettingsImpl::getRotationAlgorithm() | ||||
| { | ||||
|   return m_rotationAlgorithm; | ||||
| } | ||||
| 
 | ||||
| void UISelectionSettingsImpl::setMoveTransparentColor(app::Color color) | ||||
| { | ||||
|   m_moveTransparentColor = 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) | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ | |||
| #include "ui/combobox.h" | ||||
| #include "ui/int_entry.h" | ||||
| #include "ui/label.h" | ||||
| #include "ui/listitem.h" | ||||
| #include "ui/popup_window.h" | ||||
| #include "ui/preferred_size_event.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() | ||||
|   : Box(JI_HORIZONTAL) | ||||
| { | ||||
|  | @ -386,6 +420,8 @@ ContextBar::ContextBar() | |||
|   addChild(m_selectionOptionsBox = new HBox()); | ||||
|   m_selectionOptionsBox->addChild(new Label("Transparent Color:")); | ||||
|   m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField); | ||||
|   m_selectionOptionsBox->addChild(new Label("Algorithm:")); | ||||
|   m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField()); | ||||
| 
 | ||||
|   TooltipManager* tooltipManager = new TooltipManager(); | ||||
|   addChild(tooltipManager); | ||||
|  |  | |||
|  | @ -51,6 +51,7 @@ namespace app { | |||
|     class SprayWidthField; | ||||
|     class SpraySpeedField; | ||||
|     class TransparentColorField; | ||||
|     class RotAlgorithmField; | ||||
| 
 | ||||
|     ui::Label* m_brushLabel; | ||||
|     BrushTypeField* m_brushType; | ||||
|  | @ -67,6 +68,7 @@ namespace app { | |||
|     SpraySpeedField* m_spraySpeed; | ||||
|     ui::Box* m_selectionOptionsBox; | ||||
|     TransparentColorField* m_transparentColor; | ||||
|     RotAlgorithmField* m_rotAlgo; | ||||
|   }; | ||||
| 
 | ||||
| } // namespace app
 | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ | |||
| #include "raster/image.h" | ||||
| #include "raster/mask.h" | ||||
| #include "raster/rotate.h" | ||||
| #include "raster/rotsprite.h" | ||||
| #include "raster/sprite.h" | ||||
| 
 | ||||
| namespace app { | ||||
|  | @ -73,10 +74,14 @@ PixelsMovement::PixelsMovement(Context* context, | |||
| 
 | ||||
|   m_initialMask = new Mask(*m_document->getMask()); | ||||
|   m_currentMask = new Mask(*m_document->getMask()); | ||||
| 
 | ||||
|   UIContext::instance()->getSettings()->selection()->addObserver(this); | ||||
| } | ||||
| 
 | ||||
| PixelsMovement::~PixelsMovement() | ||||
| { | ||||
|   UIContext::instance()->getSettings()->selection()->removeObserver(this); | ||||
| 
 | ||||
|   delete m_originalImage; | ||||
|   delete m_initialMask; | ||||
|   delete m_currentMask; | ||||
|  | @ -394,12 +399,6 @@ void PixelsMovement::moveImage(int x, int y, MoveModifier moveModifier) | |||
|   } | ||||
| 
 | ||||
|   redrawExtraImage(); | ||||
|   redrawCurrentMask(); | ||||
| 
 | ||||
|   if (m_firstDrop) | ||||
|     m_document->getApi().copyToCurrentMask(m_currentMask); | ||||
|   else | ||||
|     m_document->setMask(m_currentMask); | ||||
| 
 | ||||
|   m_document->setTransformation(m_currentData); | ||||
| 
 | ||||
|  | @ -440,11 +439,7 @@ Image* PixelsMovement::getDraggedImageCopy(gfx::Point& origin) | |||
|   int height = rightBottom.y - leftTop.y; | ||||
|   base::UniquePtr<Image> image(Image::create(m_sprite->getPixelFormat(), width, height)); | ||||
|   clear_image(image, image->getMaskColor()); | ||||
|   image_parallelogram(image, m_originalImage, | ||||
|                       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); | ||||
|   drawParallelogram(image, m_originalImage, corners, leftTop); | ||||
| 
 | ||||
|   origin = leftTop; | ||||
| 
 | ||||
|  | @ -513,7 +508,9 @@ void PixelsMovement::dropImageTemporarily() | |||
|       m_currentData.displacePivotTo(gfx::Point(newPivot.x, newPivot.y)); | ||||
|     } | ||||
| 
 | ||||
|     m_document->generateMaskBoundaries(m_currentMask); | ||||
|     redrawCurrentMask(); | ||||
|     updateDocumentMask(); | ||||
| 
 | ||||
|     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.
 | ||||
|   Image* extraImage = m_document->getExtraCelImage(); | ||||
|   clear_image(extraImage, extraImage->getMaskColor()); | ||||
|   image_parallelogram(extraImage, m_originalImage, | ||||
|                       corners.leftTop().x, corners.leftTop().y, | ||||
|                       corners.rightTop().x, corners.rightTop().y, | ||||
|                       corners.rightBottom().x, corners.rightBottom().y, | ||||
|                       corners.leftBottom().x, corners.leftBottom().y); | ||||
|   drawParallelogram(extraImage, m_originalImage, corners, gfx::Point(0, 0)); | ||||
| } | ||||
| 
 | ||||
| void PixelsMovement::redrawCurrentMask() | ||||
|  | @ -610,13 +603,56 @@ void PixelsMovement::redrawCurrentMask() | |||
|   m_currentMask->replace(0, 0, m_sprite->getWidth(), m_sprite->getHeight()); | ||||
|   m_currentMask->freeze(); | ||||
|   clear_image(m_currentMask->getBitmap(), 0); | ||||
|   image_parallelogram(m_currentMask->getBitmap(), | ||||
|                       m_initialMask->getBitmap(), | ||||
|                       corners.leftTop().x, corners.leftTop().y, | ||||
|                       corners.rightTop().x, corners.rightTop().y, | ||||
|                       corners.rightBottom().x, corners.rightBottom().y, | ||||
|                       corners.leftBottom().x, corners.leftBottom().y); | ||||
|   drawParallelogram(m_currentMask->getBitmap(), m_initialMask->getBitmap(), | ||||
|     corners, gfx::Point(0, 0)); | ||||
| 
 | ||||
|   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
 | ||||
|  |  | |||
|  | @ -19,11 +19,13 @@ | |||
| #ifndef 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/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 "raster/algorithm/flip_type.h" | ||||
| #include "app/undo_transaction.h" | ||||
| 
 | ||||
| namespace raster { | ||||
|   class Image; | ||||
|  | @ -38,7 +40,7 @@ namespace app { | |||
|   // feedback, drag, and drop the specified image in the constructor
 | ||||
|   // (which generally would be the selected region or the clipboard
 | ||||
|   // content).
 | ||||
|   class PixelsMovement { | ||||
|   class PixelsMovement : public SelectionSettingsObserver { | ||||
|   public: | ||||
|     enum MoveModifier { | ||||
|       NormalMovement = 1, | ||||
|  | @ -94,9 +96,16 @@ namespace app { | |||
| 
 | ||||
|     const gfx::Transformation& getTransformation() const { return m_currentData; } | ||||
| 
 | ||||
|   protected: | ||||
|     void onSetRotationAlgorithm(RotationAlgorithm algorithm) OVERRIDE; | ||||
| 
 | ||||
|   private: | ||||
|     void redrawExtraImage(); | ||||
|     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; | ||||
|     Document* m_document; | ||||
|  |  | |||
|  | @ -32,5 +32,6 @@ add_library(raster-lib | |||
|   quantization.cpp | ||||
|   rgbmap.cpp | ||||
|   rotate.cpp | ||||
|   rotsprite.cpp | ||||
|   sprite.cpp | ||||
|   stock.cpp) | ||||
|  |  | |||
|  | @ -447,6 +447,12 @@ namespace raster { | |||
|       return *this; | ||||
|     } | ||||
| 
 | ||||
|     ImageIteratorT& operator+=(int diff) { | ||||
|       while (diff-- > 0) | ||||
|         operator++(); | ||||
|       return *this; | ||||
|     } | ||||
| 
 | ||||
|     ImageIteratorT operator++(int) { | ||||
|       ImageIteratorT tmp(*this); | ||||
|       operator++(); | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| #include "raster/image.h" | ||||
| #include "raster/image_bits.h" | ||||
| #include "raster/primitives.h" | ||||
| #include "raster/primitives_fast.h" | ||||
| 
 | ||||
| #include <allegro.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, | ||||
|                                               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) | ||||
| { | ||||
|   if (w == src->getWidth() && src->getHeight() == h) | ||||
|     composite_image(dst, src, x, y, 255, BLEND_MODE_NORMAL); | ||||
|   else { | ||||
|     BLEND_COLOR blender = NULL; | ||||
|     int u, v, c; | ||||
|     switch (dst->getPixelFormat()) { | ||||
| 
 | ||||
|     if (dst->getPixelFormat() == IMAGE_RGB) | ||||
|       blender = rgba_blenders[BLEND_MODE_NORMAL]; | ||||
|     else if (dst->getPixelFormat() == IMAGE_GRAYSCALE) | ||||
|       blender = graya_blenders[BLEND_MODE_NORMAL]; | ||||
|       case IMAGE_RGB: | ||||
|         image_scale_tpl<RgbTraits>(dst, src, x, y, w, h, rgba_blender); | ||||
|         break; | ||||
| 
 | ||||
|     for (v=0; v<h; v++) { | ||||
|       for (u=0; u<w; u++) { | ||||
|         c = get_pixel(src, src->getWidth()*u/w, src->getHeight()*v/h); | ||||
|       case IMAGE_GRAYSCALE: | ||||
|         image_scale_tpl<GrayscaleTraits>(dst, src, x, y, w, h, grayscale_blender); | ||||
|         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_GRAYSCALE: | ||||
|             put_pixel(dst, x+u, y+v, | ||||
|                       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; | ||||
|         } | ||||
|       } | ||||
|       case IMAGE_BITMAP: | ||||
|         image_scale_tpl<BitmapTraits>(dst, src, x, y, w, h, if_blender); | ||||
|         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