2013-08-09 08:01:20 +08:00
|
|
|
/* Aseprite
|
2015-01-19 09:05:33 +08:00
|
|
|
* Copyright (C) 2001-2015 David Capello
|
2012-01-06 06:45:03 +08:00
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2012-01-06 06:45:03 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2014-11-07 08:04:32 +08:00
|
|
|
#include "app/commands/cmd_sprite_size.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/commands/command.h"
|
2014-11-07 08:04:32 +08:00
|
|
|
#include "app/commands/params.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/context_access.h"
|
|
|
|
|
#include "app/document_api.h"
|
2012-06-16 10:37:59 +08:00
|
|
|
#include "app/find_widget.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/ini_file.h"
|
|
|
|
|
#include "app/job.h"
|
2012-06-16 10:37:59 +08:00
|
|
|
#include "app/load_widget.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/modules/gui.h"
|
|
|
|
|
#include "app/modules/palettes.h"
|
|
|
|
|
#include "app/ui_context.h"
|
2015-01-19 09:05:33 +08:00
|
|
|
#include "app/transaction.h"
|
2012-01-06 06:45:03 +08:00
|
|
|
#include "base/bind.h"
|
2012-01-09 09:34:36 +08:00
|
|
|
#include "base/unique_ptr.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/algorithm/resize_image.h"
|
|
|
|
|
#include "doc/cel.h"
|
|
|
|
|
#include "doc/image.h"
|
|
|
|
|
#include "doc/mask.h"
|
|
|
|
|
#include "doc/primitives.h"
|
|
|
|
|
#include "doc/sprite.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "ui/ui.h"
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-03-31 07:10:54 +08:00
|
|
|
#define PERC_FORMAT "%.1f"
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
namespace app {
|
|
|
|
|
|
|
|
|
|
using namespace ui;
|
2014-10-21 09:21:31 +08:00
|
|
|
using doc::algorithm::ResizeMethod;
|
2013-08-06 08:20:19 +08:00
|
|
|
|
|
|
|
|
class SpriteSizeJob : public Job {
|
2013-03-12 07:29:45 +08:00
|
|
|
ContextWriter m_writer;
|
|
|
|
|
Document* m_document;
|
2012-01-06 06:45:03 +08:00
|
|
|
Sprite* m_sprite;
|
|
|
|
|
int m_new_width;
|
|
|
|
|
int m_new_height;
|
|
|
|
|
ResizeMethod m_resize_method;
|
|
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
inline int scale_x(int x) const { return x * m_new_width / m_sprite->width(); }
|
|
|
|
|
inline int scale_y(int y) const { return y * m_new_height / m_sprite->height(); }
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
2013-03-12 07:29:45 +08:00
|
|
|
SpriteSizeJob(const ContextReader& reader, int new_width, int new_height, ResizeMethod resize_method)
|
2012-01-06 06:45:03 +08:00
|
|
|
: Job("Sprite Size")
|
2013-03-12 07:29:45 +08:00
|
|
|
, m_writer(reader)
|
|
|
|
|
, m_document(m_writer.document())
|
|
|
|
|
, m_sprite(m_writer.sprite())
|
2012-01-06 06:45:03 +08:00
|
|
|
{
|
|
|
|
|
m_new_width = new_width;
|
|
|
|
|
m_new_height = new_height;
|
|
|
|
|
m_resize_method = resize_method;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [working thread]
|
|
|
|
|
*/
|
|
|
|
|
virtual void onJob()
|
|
|
|
|
{
|
2015-01-19 09:05:33 +08:00
|
|
|
Transaction transaction(m_writer.context(), "Sprite Size");
|
|
|
|
|
DocumentApi api = m_writer.document()->getApi(transaction);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
|
// Get all sprite cels
|
|
|
|
|
CelList cels;
|
|
|
|
|
m_sprite->getCels(cels);
|
|
|
|
|
|
|
|
|
|
// For each cel...
|
|
|
|
|
int progress = 0;
|
|
|
|
|
for (CelIterator it = cels.begin(); it != cels.end(); ++it, ++progress) {
|
|
|
|
|
Cel* cel = *it;
|
|
|
|
|
|
|
|
|
|
// Change its location
|
2014-07-30 12:28:15 +08:00
|
|
|
api.setCelPosition(m_sprite, cel, scale_x(cel->x()), scale_y(cel->y()));
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
|
// Get cel's image
|
2014-07-30 12:28:15 +08:00
|
|
|
Image* image = cel->image();
|
2012-01-06 06:45:03 +08:00
|
|
|
if (!image)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Resize the image
|
2014-07-30 12:28:15 +08:00
|
|
|
int w = scale_x(image->width());
|
|
|
|
|
int h = scale_y(image->height());
|
2015-01-04 21:58:14 +08:00
|
|
|
ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)));
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2014-10-21 09:21:31 +08:00
|
|
|
doc::algorithm::fixup_image_transparent_colors(image);
|
|
|
|
|
doc::algorithm::resize_image(image, new_image,
|
2015-01-04 21:58:14 +08:00
|
|
|
m_resize_method,
|
|
|
|
|
m_sprite->palette(cel->frame()),
|
|
|
|
|
m_sprite->rgbMap(cel->frame()));
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2015-01-04 21:58:14 +08:00
|
|
|
api.replaceImage(m_sprite, cel->imageRef(), new_image);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
|
jobProgress((float)progress / cels.size());
|
|
|
|
|
|
|
|
|
|
// cancel all the operation?
|
|
|
|
|
if (isCanceled())
|
2015-01-19 09:05:33 +08:00
|
|
|
return; // Transaction destructor will undo all operations
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resize mask
|
|
|
|
|
if (m_document->isMaskVisible()) {
|
2015-01-04 21:58:14 +08:00
|
|
|
ImageRef old_bitmap
|
2014-07-30 12:28:15 +08:00
|
|
|
(crop_image(m_document->mask()->bitmap(), -1, -1,
|
|
|
|
|
m_document->mask()->bitmap()->width()+2,
|
|
|
|
|
m_document->mask()->bitmap()->height()+2, 0));
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
int w = scale_x(old_bitmap->width());
|
|
|
|
|
int h = scale_y(old_bitmap->height());
|
2013-08-06 08:20:19 +08:00
|
|
|
base::UniquePtr<Mask> new_mask(new Mask);
|
2014-12-14 06:10:54 +08:00
|
|
|
new_mask->replace(
|
|
|
|
|
gfx::Rect(
|
|
|
|
|
scale_x(m_document->mask()->bounds().x-1),
|
|
|
|
|
scale_y(m_document->mask()->bounds().y-1), MAX(1, w), MAX(1, h)));
|
2014-07-30 12:28:15 +08:00
|
|
|
algorithm::resize_image(old_bitmap, new_mask->bitmap(),
|
2015-01-04 21:58:14 +08:00
|
|
|
m_resize_method,
|
|
|
|
|
m_sprite->palette(0), // Ignored
|
|
|
|
|
m_sprite->rgbMap(0)); // Ignored
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-01-10 06:13:48 +08:00
|
|
|
// Reshrink
|
2014-07-30 12:28:15 +08:00
|
|
|
new_mask->intersect(new_mask->bounds());
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-01-10 06:13:48 +08:00
|
|
|
// Copy new mask
|
2013-03-12 07:29:45 +08:00
|
|
|
api.copyToCurrentMask(new_mask);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-01-10 06:13:48 +08:00
|
|
|
// Regenerate mask
|
|
|
|
|
m_document->resetTransformation();
|
2012-01-06 06:45:03 +08:00
|
|
|
m_document->generateMaskBoundaries();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// resize sprite
|
2013-03-12 07:29:45 +08:00
|
|
|
api.setSpriteSize(m_sprite, m_new_width, m_new_height);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
|
// commit changes
|
2015-01-19 09:05:33 +08:00
|
|
|
transaction.commit();
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SpriteSizeCommand::SpriteSizeCommand()
|
|
|
|
|
: Command("SpriteSize",
|
|
|
|
|
"Sprite Size",
|
|
|
|
|
CmdRecordableFlag)
|
|
|
|
|
{
|
2014-11-07 08:04:32 +08:00
|
|
|
m_width = 0;
|
|
|
|
|
m_height = 0;
|
|
|
|
|
m_scaleX = 1.0;
|
|
|
|
|
m_scaleY = 1.0;
|
2014-11-08 06:30:39 +08:00
|
|
|
m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
|
2014-11-07 08:04:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Command* SpriteSizeCommand::clone() const
|
|
|
|
|
{
|
|
|
|
|
return new SpriteSizeCommand(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpriteSizeCommand::onLoadParams(Params* params)
|
|
|
|
|
{
|
|
|
|
|
std::string width = params->get("width");
|
|
|
|
|
if (!width.empty()) {
|
|
|
|
|
m_width = std::strtol(width.c_str(), NULL, 10);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
m_width = 0;
|
|
|
|
|
|
|
|
|
|
std::string height = params->get("height");
|
|
|
|
|
if (!height.empty()) {
|
|
|
|
|
m_height = std::strtol(height.c_str(), NULL, 10);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
m_height = 0;
|
|
|
|
|
|
|
|
|
|
std::string resize_method = params->get("resize-method");
|
|
|
|
|
if (!resize_method.empty()) {
|
|
|
|
|
if (resize_method == "bilinear")
|
2014-11-08 06:30:39 +08:00
|
|
|
m_resizeMethod = doc::algorithm::RESIZE_METHOD_BILINEAR;
|
2014-11-07 08:04:32 +08:00
|
|
|
else
|
2014-11-08 06:30:39 +08:00
|
|
|
m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
|
2014-11-07 08:04:32 +08:00
|
|
|
}
|
|
|
|
|
else
|
2014-11-08 06:30:39 +08:00
|
|
|
m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SpriteSizeCommand::onEnabled(Context* context)
|
|
|
|
|
{
|
|
|
|
|
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
|
|
|
|
ContextFlags::HasActiveSprite);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpriteSizeCommand::onExecute(Context* context)
|
|
|
|
|
{
|
2014-11-07 08:04:32 +08:00
|
|
|
const ContextReader reader(context);
|
2013-03-12 07:29:45 +08:00
|
|
|
const Sprite* sprite(reader.sprite());
|
2014-11-07 08:04:32 +08:00
|
|
|
int new_width = (m_width ? m_width: sprite->width()*m_scaleX);
|
|
|
|
|
int new_height = (m_height ? m_height: sprite->height()*m_scaleY);
|
|
|
|
|
ResizeMethod resize_method = m_resizeMethod;
|
|
|
|
|
|
|
|
|
|
if (context->isUiAvailable()) {
|
|
|
|
|
// load the window widget
|
|
|
|
|
base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size"));
|
|
|
|
|
m_widthPx = app::find_widget<Entry>(window, "width_px");
|
|
|
|
|
m_heightPx = app::find_widget<Entry>(window, "height_px");
|
|
|
|
|
m_widthPerc = app::find_widget<Entry>(window, "width_perc");
|
|
|
|
|
m_heightPerc = app::find_widget<Entry>(window, "height_perc");
|
|
|
|
|
m_lockRatio = app::find_widget<CheckBox>(window, "lock_ratio");
|
|
|
|
|
ComboBox* method = app::find_widget<ComboBox>(window, "method");
|
|
|
|
|
Widget* ok = app::find_widget<Widget>(window, "ok");
|
|
|
|
|
|
|
|
|
|
m_widthPx->setTextf("%d", new_width);
|
|
|
|
|
m_heightPx->setTextf("%d", new_height);
|
|
|
|
|
|
|
|
|
|
m_lockRatio->Click.connect(Bind<void>(&SpriteSizeCommand::onLockRatioClick, this));
|
|
|
|
|
m_widthPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPxChange, this));
|
|
|
|
|
m_heightPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPxChange, this));
|
|
|
|
|
m_widthPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPercChange, this));
|
|
|
|
|
m_heightPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPercChange, this));
|
|
|
|
|
|
|
|
|
|
method->addItem("Nearest-neighbor");
|
|
|
|
|
method->addItem("Bilinear");
|
|
|
|
|
method->setSelectedItemIndex(get_config_int("SpriteSize", "Method",
|
2014-11-08 06:30:39 +08:00
|
|
|
doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR));
|
2014-11-07 08:04:32 +08:00
|
|
|
|
|
|
|
|
window->remapWindow();
|
|
|
|
|
window->centerWindow();
|
|
|
|
|
|
|
|
|
|
load_window_pos(window, "SpriteSize");
|
|
|
|
|
window->setVisible(true);
|
|
|
|
|
window->openWindowInForeground();
|
|
|
|
|
save_window_pos(window, "SpriteSize");
|
|
|
|
|
|
|
|
|
|
if (window->getKiller() != ok)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
new_width = m_widthPx->getTextInt();
|
|
|
|
|
new_height = m_heightPx->getTextInt();
|
|
|
|
|
resize_method = (ResizeMethod)method->getSelectedItemIndex();
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
|
set_config_int("SpriteSize", "Method", resize_method);
|
2014-11-07 08:04:32 +08:00
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2014-11-07 08:04:32 +08:00
|
|
|
{
|
|
|
|
|
SpriteSizeJob job(reader, new_width, new_height, resize_method);
|
|
|
|
|
job.startJob();
|
|
|
|
|
job.waitJob();
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
2014-11-07 08:04:32 +08:00
|
|
|
|
|
|
|
|
ContextWriter writer(reader);
|
|
|
|
|
update_screen_for_document(writer.document());
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpriteSizeCommand::onLockRatioClick()
|
|
|
|
|
{
|
2013-03-12 07:29:45 +08:00
|
|
|
const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
onWidthPxChange();
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
void SpriteSizeCommand::onWidthPxChange()
|
2012-01-06 06:45:03 +08:00
|
|
|
{
|
2013-03-12 07:29:45 +08:00
|
|
|
const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
|
|
|
|
|
const Sprite* sprite(reader.sprite());
|
2012-04-19 09:57:36 +08:00
|
|
|
int width = m_widthPx->getTextInt();
|
2014-07-30 12:28:15 +08:00
|
|
|
double perc = 100.0 * width / sprite->width();
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
m_widthPerc->setTextf(PERC_FORMAT, perc);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
if (m_lockRatio->isSelected()) {
|
|
|
|
|
m_heightPerc->setTextf(PERC_FORMAT, perc);
|
2014-07-30 12:28:15 +08:00
|
|
|
m_heightPx->setTextf("%d", sprite->height() * width / sprite->width());
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
void SpriteSizeCommand::onHeightPxChange()
|
2012-01-06 06:45:03 +08:00
|
|
|
{
|
2013-03-12 07:29:45 +08:00
|
|
|
const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
|
|
|
|
|
const Sprite* sprite(reader.sprite());
|
2012-04-19 09:57:36 +08:00
|
|
|
int height = m_heightPx->getTextInt();
|
2014-07-30 12:28:15 +08:00
|
|
|
double perc = 100.0 * height / sprite->height();
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
m_heightPerc->setTextf(PERC_FORMAT, perc);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
if (m_lockRatio->isSelected()) {
|
|
|
|
|
m_widthPerc->setTextf(PERC_FORMAT, perc);
|
2014-07-30 12:28:15 +08:00
|
|
|
m_widthPx->setTextf("%d", sprite->width() * height / sprite->height());
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
void SpriteSizeCommand::onWidthPercChange()
|
2012-01-06 06:45:03 +08:00
|
|
|
{
|
2013-03-12 07:29:45 +08:00
|
|
|
const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
|
|
|
|
|
const Sprite* sprite(reader.sprite());
|
2012-04-19 09:57:36 +08:00
|
|
|
double width = m_widthPerc->getTextDouble();
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
m_widthPx->setTextf("%d", (int)(sprite->width() * width / 100));
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
if (m_lockRatio->isSelected()) {
|
2014-07-30 12:28:15 +08:00
|
|
|
m_heightPx->setTextf("%d", (int)(sprite->height() * width / 100));
|
2012-04-19 09:57:36 +08:00
|
|
|
m_heightPerc->setText(m_widthPerc->getText());
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
void SpriteSizeCommand::onHeightPercChange()
|
2012-01-06 06:45:03 +08:00
|
|
|
{
|
2013-03-12 07:29:45 +08:00
|
|
|
const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
|
|
|
|
|
const Sprite* sprite(reader.sprite());
|
2012-04-19 09:57:36 +08:00
|
|
|
double height = m_heightPerc->getTextDouble();
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
m_heightPx->setTextf("%d", (int)(sprite->height() * height / 100));
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-04-19 09:57:36 +08:00
|
|
|
if (m_lockRatio->isSelected()) {
|
2014-07-30 12:28:15 +08:00
|
|
|
m_widthPx->setTextf("%d", (int)(sprite->width() * height / 100));
|
2012-04-19 09:57:36 +08:00
|
|
|
m_widthPerc->setText(m_heightPerc->getText());
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Command* CommandFactory::createSpriteSizeCommand()
|
|
|
|
|
{
|
|
|
|
|
return new SpriteSizeCommand;
|
|
|
|
|
}
|
2013-08-06 08:20:19 +08:00
|
|
|
|
|
|
|
|
} // namespace app
|