From 02597dcdeca403b87c770c9cce6af42110e17ba9 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sun, 29 Dec 2013 17:11:56 -0300 Subject: [PATCH] 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). --- src/app/settings/rotation_algorithm.h | 34 +++++ src/app/settings/settings.h | 3 + src/app/settings/settings_observers.h | 2 + src/app/settings/ui_settings_impl.cpp | 32 ++++- src/app/ui/context_bar.cpp | 36 +++++ src/app/ui/context_bar.h | 2 + src/app/ui/editor/pixels_movement.cpp | 82 ++++++++--- src/app/ui/editor/pixels_movement.h | 15 +- src/raster/CMakeLists.txt | 1 + src/raster/image_iterator.h | 6 + src/raster/rotate.cpp | 66 ++++++--- src/raster/rotsprite.cpp | 199 ++++++++++++++++++++++++++ src/raster/rotsprite.h | 31 ++++ 13 files changed, 456 insertions(+), 53 deletions(-) create mode 100644 src/app/settings/rotation_algorithm.h create mode 100644 src/raster/rotsprite.cpp create mode 100644 src/raster/rotsprite.h diff --git a/src/app/settings/rotation_algorithm.h b/src/app/settings/rotation_algorithm.h new file mode 100644 index 000000000..17a8f0c7f --- /dev/null +++ b/src/app/settings/rotation_algorithm.h @@ -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 diff --git a/src/app/settings/settings.h b/src/app/settings/settings.h index fff16f991..ea8aae755 100644 --- a/src/app/settings/settings.h +++ b/src/app/settings/settings.h @@ -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; diff --git a/src/app/settings/settings_observers.h b/src/app/settings/settings_observers.h index 8d85a2f49..15cb7ea9c 100644 --- a/src/app/settings/settings_observers.h +++ b/src/app/settings/settings_observers.h @@ -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 { diff --git a/src/app/settings/ui_settings_impl.cpp b/src/app/settings/ui_settings_impl.cpp index b5dfbe236..ccdb47291 100644 --- a/src/app/settings/ui_settings_impl.cpp +++ b/src/app/settings/ui_settings_impl.cpp @@ -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) diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 2ecb5b76d..372def582 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -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(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); diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h index 2dc810723..49fd7a877 100644 --- a/src/app/ui/context_bar.h +++ b/src/app/ui/context_bar.h @@ -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 diff --git a/src/app/ui/editor/pixels_movement.cpp b/src/app/ui/editor/pixels_movement.cpp index 8b249ee60..5c168c95b 100644 --- a/src/app/ui/editor/pixels_movement.cpp +++ b/src/app/ui/editor/pixels_movement.cpp @@ -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::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 diff --git a/src/app/ui/editor/pixels_movement.h b/src/app/ui/editor/pixels_movement.h index 95151d6c6..cc5edbf9b 100644 --- a/src/app/ui/editor/pixels_movement.h +++ b/src/app/ui/editor/pixels_movement.h @@ -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; diff --git a/src/raster/CMakeLists.txt b/src/raster/CMakeLists.txt index d9f314edc..8f1c1fbd3 100644 --- a/src/raster/CMakeLists.txt +++ b/src/raster/CMakeLists.txt @@ -32,5 +32,6 @@ add_library(raster-lib quantization.cpp rgbmap.cpp rotate.cpp + rotsprite.cpp sprite.cpp stock.cpp) diff --git a/src/raster/image_iterator.h b/src/raster/image_iterator.h index 680849355..1ae46ba0e 100644 --- a/src/raster/image_iterator.h +++ b/src/raster/image_iterator.h @@ -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++(); diff --git a/src/raster/rotate.cpp b/src/raster/rotate.cpp index e3552994e..aa044d7a1 100644 --- a/src/raster/rotate.cpp +++ b/src/raster/rotate.cpp @@ -15,6 +15,7 @@ #include "raster/image.h" #include "raster/image_bits.h" #include "raster/primitives.h" +#include "raster/primitives_fast.h" #include #include @@ -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 +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(src, src_w*u/w, src_h*v/h); + put_pixel_fast(dst, x+u, y+v, + blend(get_pixel_fast(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(dst, src, x, y, w, h, rgba_blender); + break; - for (v=0; vgetWidth()*u/w, src->getHeight()*v/h); + case IMAGE_GRAYSCALE: + image_scale_tpl(dst, src, x, y, w, h, grayscale_blender); + break; - switch (dst->getPixelFormat()) { + case IMAGE_INDEXED: + image_scale_tpl(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(dst, src, x, y, w, h, if_blender); + break; } } } diff --git a/src/raster/rotsprite.cpp b/src/raster/rotsprite.cpp new file mode 100644 index 000000000..a34f7951e --- /dev/null +++ b/src/raster/rotsprite.cpp @@ -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 +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 dstBits(dst, Image::WriteLock, gfx::Rect(0, 0, dst_w, dst_h)); + const LockImageBits srcBits(src); + + LockImageBits::iterator dstRow0_it = dstBits.begin(); + LockImageBits::iterator dstRow1_it = dstBits.begin(); + LockImageBits::iterator dstRow0_end = dstBits.end(); + LockImageBits::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::const_iterator itP, itA, itB, itC, itD, savedD; + LockImageBits::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 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, x, y); + A = (y > 0 ? get_pixel_fast(src, x, y-1): P); + B = (x < src_w-1 ? get_pixel_fast(src, x+1, y): P); + C = (x > 0 ? get_pixel_fast(src, x-1, y): P); + D = (y < src_h-1 ? get_pixel_fast(src, x, y+1): P); + + put_pixel_fast(dst, 2*x, 2*y, (C == A && C != D && A != B ? A: P)); + put_pixel_fast(dst, 2*x+1, 2*y, (A == B && A != C && B != D ? B: P)); + put_pixel_fast(dst, 2*x, 2*y+1, (D == C && D != B && C != A ? C: P)); + put_pixel_fast(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(dst, src, src_w, src_h); break; + case IMAGE_GRAYSCALE: image_scale2x_tpl(dst, src, src_w, src_h); break; + case IMAGE_INDEXED: image_scale2x_tpl(dst, src, src_w, src_h); break; + case IMAGE_BITMAP: image_scale2x_tpl(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 bmp_copy(Image::create(bmp->getPixelFormat(), bmp->getWidth()*scale, bmp->getHeight()*scale, buf1)); + base::UniquePtr tmp_copy(Image::create(spr->getPixelFormat(), spr->getWidth()*scale, spr->getHeight()*scale, buf2)); + base::UniquePtr 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<getHeight()*(1<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 diff --git a/src/raster/rotsprite.h b/src/raster/rotsprite.h new file mode 100644 index 000000000..8ca619e8b --- /dev/null +++ b/src/raster/rotsprite.h @@ -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