Add support to resize sprites with tilemaps

This fixes Sprite > Sprite Size and File > Export when a resize
parameter != 100% is given.
This commit is contained in:
David Capello 2021-05-18 14:52:53 -03:00
parent dd9ef64668
commit 414165f078
9 changed files with 224 additions and 14 deletions

View File

@ -463,6 +463,7 @@ add_library(app-lib
cmd/remove_tile.cpp
cmd/remove_tileset.cpp
cmd/replace_image.cpp
cmd/replace_tileset.cpp
cmd/reselect_mask.cpp
cmd/set_cel_bounds.cpp
cmd/set_cel_data.cpp

View File

@ -0,0 +1,74 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/replace_tileset.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/sprite.h"
#include "doc/sprite.h"
#include "doc/tileset.h"
#include "doc/tileset_io.h"
#include "doc/tilesets.h"
namespace app {
namespace cmd {
using namespace doc;
ReplaceTileset::ReplaceTileset(doc::Sprite* sprite,
const doc::tileset_index tsi,
doc::Tileset* newTileset)
: WithSprite(sprite)
, m_tsi(tsi)
, m_newTileset(newTileset)
{
}
void ReplaceTileset::onExecute()
{
Sprite* spr = sprite();
doc::Tileset* actualTileset = spr->tilesets()->get(m_tsi);
doc::Tileset* restoreTileset;
if (m_newTileset) {
restoreTileset = m_newTileset;
m_newTileset = nullptr;
}
else {
restoreTileset = doc::read_tileset(m_stream, spr, true);
}
m_stream.str(std::string());
m_stream.clear();
doc::write_tileset(m_stream, actualTileset);
m_size = size_t(m_stream.tellp());
replaceTileset(restoreTileset);
delete actualTileset;
}
void ReplaceTileset::replaceTileset(Tileset* newTileset)
{
Sprite* spr = sprite();
for (Layer* lay : spr->allLayers()) {
if (lay->isTilemap() &&
static_cast<LayerTilemap*>(lay)->tilesetIndex() == m_tsi) {
lay->incrementVersion();
}
}
spr->replaceTileset(m_tsi, newTileset);
spr->tilesets()->incrementVersion();
spr->incrementVersion();
}
} // namespace cmd
} // namespace app

View File

@ -0,0 +1,47 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_REPLACE_TILESET_H_INCLUDED
#define APP_CMD_REPLACE_TILESET_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_sprite.h"
#include "doc/tileset.h"
#include <sstream>
namespace app {
namespace cmd {
class ReplaceTileset : public Cmd
, public WithSprite {
public:
ReplaceTileset(doc::Sprite* sprite,
const doc::tileset_index tsi,
doc::Tileset* newTileset);
protected:
void onExecute() override;
void onUndo() override { onExecute(); }
void onRedo() override { onExecute(); }
size_t onMemSize() const override {
return sizeof(*this) + m_size;
}
private:
void replaceTileset(doc::Tileset* newTileset);
doc::tileset_index m_tsi;
doc::Tileset* m_newTileset;
std::stringstream m_stream;
size_t m_size = 0;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -9,6 +9,7 @@
#include "config.h"
#endif
#include "app/cmd/replace_tileset.h"
#include "app/cmd/set_cel_bounds.h"
#include "app/cmd/set_slice_key.h"
#include "app/commands/command.h"
@ -27,10 +28,12 @@
#include "doc/cels_range.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/mask.h"
#include "doc/primitives.h"
#include "doc/slice.h"
#include "doc/sprite.h"
#include "doc/tilesets.h"
#include "ui/ui.h"
#include "sprite_size.xml.h"
@ -89,28 +92,91 @@ protected:
// [working thread]
void onJob() override {
DocApi api = writer().document()->getApi(tx());
Tilesets* tilesets = sprite()->tilesets();
int cels_count = 0;
int img_count = 0;
if (tilesets) {
for (Tileset* tileset : *tilesets) {
img_count += tileset->size();
}
}
for (Cel* cel : sprite()->uniqueCels()) { // TODO add size() member function to CelsRange
(void)cel;
++cels_count;
++img_count;
}
int progress = 0;
const gfx::SizeF scale(
double(m_new_width) / double(sprite()->width()),
double(m_new_height) / double(sprite()->height()));
// Resize tilesets
if (tilesets) {
for (tileset_index tsi=0; tsi<tilesets->size(); ++tsi) {
Tileset* tileset = tilesets->get(tsi);
ASSERT(tileset);
gfx::Size newGridSize = tileset->grid().tileSize();
newGridSize.w *= scale.w;
newGridSize.h *= scale.h;
if (newGridSize.w < 1) newGridSize.w = 1;
if (newGridSize.h < 1) newGridSize.h = 1;
doc::Grid newGrid(newGridSize);
auto newTileset = new doc::Tileset(sprite(), newGrid, tileset->size());
doc::tile_index idx = 0;
for (doc::ImageRef tileImg : *tileset) {
if (idx != 0) {
doc::ImageRef newTileImg(
resize_image(
tileImg.get(),
scale,
m_resize_method,
sprite()->palette(0),
sprite()->rgbMap(0))); // TODO first frame?
newTileset->set(idx, newTileImg);
}
jobProgress((float)progress / img_count);
++progress;
++idx;
}
tx()(new cmd::ReplaceTileset(sprite(), tsi, newTileset));
// Cancel all the operation?
if (isCanceled())
return; // Tx destructor will undo all operations
}
}
// For each cel...
int progress = 0;
for (Cel* cel : sprite()->uniqueCels()) {
resize_cel_image(
tx(), cel, scale,
m_resize_method,
cel->layer()->isReference() ?
// We need to adjust only the origin/position of tilemap cels
// (because tiles are resized automatically when we resize the
// tileset).
if (cel->layer()->isTilemap()) {
Tileset* tileset = static_cast<LayerTilemap*>(cel->layer())->tileset();
gfx::Size canvasSize =
tileset->grid().tilemapSizeToCanvas(
gfx::Size(cel->image()->width(),
cel->image()->height()));
gfx::Rect newBounds(cel->x()*scale.w,
cel->y()*scale.h,
canvasSize.w,
canvasSize.h);
tx()(new cmd::SetCelBoundsF(cel, newBounds));
}
else {
resize_cel_image(
tx(), cel, scale,
m_resize_method,
cel->layer()->isReference() ?
-cel->boundsF().origin():
gfx::PointF(-cel->bounds().origin()));
}
jobProgress((float)progress / cels_count);
jobProgress((float)progress / img_count);
++progress;
// Cancel all the operation?

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (c) 2019-2020 Igara Studio S.A.
// Copyright (c) 2019-2021 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -26,7 +26,7 @@
namespace app {
doc::Image* resize_image(
doc::Image* image,
const doc::Image* image,
const gfx::SizeF& scale,
const doc::algorithm::ResizeMethod method,
const Palette* pal,

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2019-2021 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -24,7 +24,7 @@ namespace app {
class Tx;
doc::Image* resize_image(
doc::Image* image,
const doc::Image* image,
const gfx::SizeF& scale,
const doc::algorithm::ResizeMethod method,
const doc::Palette* pal,

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2019-2020 Igara Studio S.A.
// Copyright (c) 2019-2021 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -38,6 +38,10 @@ Grid LayerTilemap::grid() const
void LayerTilemap::setTilesetIndex(tileset_index tsi)
{
// "m_tilesetIndex" could be already equal to "tsi", but this
// function is used by Sprite::replaceTilemap() to update the
// m_tileset pointer even in that case.
m_tilesetIndex = tsi;
m_tileset = sprite()->tilesets()->get(tsi);
}

View File

@ -541,6 +541,22 @@ void Sprite::replaceImage(ObjectId curImageId, const ImageRef& newImage)
}
}
void Sprite::replaceTileset(tileset_index tsi, Tileset* newTileset)
{
ASSERT(hasTilesets());
tilesets()->set(tsi, newTileset);
for (Layer* layer : allLayers()) {
if (layer->isTilemap() &&
static_cast<LayerTilemap*>(layer)->tilesetIndex() == tsi) {
// Set same index just to update the internal tileset pointer in
// LayerTilemap
static_cast<LayerTilemap*>(layer)->setTilesetIndex(tsi);
}
}
}
// TODO replace it with a images iterator
void Sprite::getImages(std::vector<ImageRef>& images) const
{

View File

@ -24,6 +24,7 @@
#include "doc/rgbmap_algorithm.h"
#include "doc/slices.h"
#include "doc/tags.h"
#include "doc/tile.h"
#include "doc/with_user_data.h"
#include "gfx/rect.h"
@ -183,6 +184,7 @@ namespace doc {
// Images
void replaceImage(ObjectId curImageId, const ImageRef& newImage);
void replaceTileset(tileset_index tsi, Tileset* newTileset);
// Returns all sprite images (cel + tiles) that aren't tilemaps
void getImages(std::vector<ImageRef>& images) const;