mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-17 13:20:45 +00:00
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
34
src/app/settings/rotation_algorithm.h
Normal file
34
src/app/settings/rotation_algorithm.h
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
199
src/raster/rotsprite.cpp
Normal file
199
src/raster/rotsprite.cpp
Normal file
@ -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
|
31
src/raster/rotsprite.h
Normal file
31
src/raster/rotsprite.h
Normal file
@ -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…
x
Reference in New Issue
Block a user