mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-04 08:46:09 +00:00
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:
parent
dd9ef64668
commit
414165f078
@ -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
|
||||
|
74
src/app/cmd/replace_tileset.cpp
Normal file
74
src/app/cmd/replace_tileset.cpp
Normal 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
|
47
src/app/cmd/replace_tileset.h
Normal file
47
src/app/cmd/replace_tileset.h
Normal 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
|
@ -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?
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user