Add new ConvertLayer command ("Layer > Convert To" menu item)

Now we have an easy way to convert between:
  Background <-> Layers <-> Tilemaps

Deprecated (they are kept only for backward compatibility):
  BackgroundFromLayer
  LayerFromBackground
This commit is contained in:
David Capello 2021-02-23 11:32:08 -03:00
parent b6987a0262
commit 44437e0d1f
6 changed files with 299 additions and 14 deletions

View File

@ -840,9 +840,17 @@
</item>
</menu>
<item command="RemoveLayer" text="@.layer_delete_layer" group="layer_remove" />
<menu text="@.layer_convert">
<item command="BackgroundFromLayer" text="@.layer_background_from_layer" />
<item command="LayerFromBackground" text="@.layer_layer_from_background" group="layer_background" />
<menu text="@.layer_convert_to">
<item command="ConvertLayer" text="@.layer_convert_to_background">
<param name="to" value="background" />
</item>
<item command="ConvertLayer" text="@.layer_convert_to_layer">
<param name="to" value="layer" />
</item>
<separator />
<item command="ConvertLayer" text="@.layer_convert_to_tilemap">
<param name="to" value="tilemap" />
</item>
</menu>
<separator />
<item command="DuplicateLayer" text="@.layer_duplicate" group="layer_duplicate" />
@ -1018,9 +1026,17 @@
<param name="group" value="true" />
</item>
<item command="RemoveLayer" text="@main_menu.layer_delete_layer" />
<menu text="@main_menu.layer_convert">
<item command="BackgroundFromLayer" text="@main_menu.layer_background_from_layer" />
<item command="LayerFromBackground" text="@main_menu.layer_layer_from_background" group="layer_popup_background" />
<menu text="@main_menu.layer_convert_to">
<item command="ConvertLayer" text="@main_menu.layer_convert_to_background">
<param name="to" value="background" />
</item>
<item command="ConvertLayer" text="@main_menu.layer_convert_to_layer">
<param name="to" value="layer" />
</item>
<separator />
<item command="ConvertLayer" text="@main_menu.layer_convert_to_tilemap">
<param name="to" value="tilemap" />
</item>
</menu>
<separator />
<item command="DuplicateLayer" text="@main_menu.layer_duplicate" />

View File

@ -273,6 +273,10 @@ ClearCel = Clear Cel
ClearRecentFiles = Clear Recent Files
CloseAllFiles = Close All Files
CloseFile = Close File
ConvertLayer = Convert Layer
ConvertLayer_Background = Convert to Background
ConvertLayer_Layer = Convert to Transparent Layer
ConvertLayer_Tilemap = Convert to Tilemap
ColorCurve = Color Curve
ColorQuantization = Create Palette from Current Sprite (Color Quantization)
ContiguousFill = Switch Contiguous Fill
@ -925,9 +929,10 @@ layer_new_layer_via_cut = New Layer via Cu&t
layer_new_reference_layer_from_file = New &Reference Layer from File
layer_new_tilemap_layer = New Tilemap Layer
layer_delete_layer = Delete Laye&r
layer_convert = Conv&ert
layer_background_from_layer = &Background from Layer
layer_layer_from_background = &Layer from Background
layer_convert_to = Conv&ert To...
layer_convert_to_background = &Background
layer_convert_to_layer = &Layer
layer_convert_to_tilemap = &Tilemap
layer_duplicate = &Duplicate
layer_merge_down = &Merge Down
layer_flatten = &Flatten

View File

@ -1,5 +1,5 @@
# Aseprite
# Copyright (C) 2018-2020 Igara Studio S.A.
# Copyright (C) 2018-2021 Igara Studio S.A.
# Copyright (C) 2001-2018 David Capello
# Generate a ui::Widget for each widget in a XML file
@ -535,6 +535,7 @@ add_library(app-lib
commands/cmd_undo.cpp
commands/command.cpp
commands/commands.cpp
commands/convert_layer.cpp
commands/filters/cmd_brightness_contrast.cpp
commands/filters/cmd_color_curve.cpp
commands/filters/cmd_convolution_matrix.cpp

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -14,6 +14,7 @@ FOR_EACH_COMMAND(CelOpacity)
FOR_EACH_COMMAND(ChangePixelFormat)
FOR_EACH_COMMAND(ColorCurve)
FOR_EACH_COMMAND(ColorQuantization)
FOR_EACH_COMMAND(ConvertLayer)
FOR_EACH_COMMAND(ConvolutionMatrix)
FOR_EACH_COMMAND(CopyColors)
FOR_EACH_COMMAND(CopyTiles)

View File

@ -0,0 +1,258 @@
// 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/add_cel.h"
#include "app/cmd/add_layer.h"
#include "app/cmd/add_tileset.h"
#include "app/cmd/background_from_layer.h"
#include "app/cmd/copy_cel.h"
#include "app/cmd/layer_from_background.h"
#include "app/cmd/remove_layer.h"
#include "app/commands/command.h"
#include "app/commands/new_params.h"
#include "app/context_access.h"
#include "app/i18n/strings.h"
#include "app/modules/gui.h"
#include "app/tx.h"
#include "app/util/cel_ops.h"
#include "doc/grid.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/tileset.h"
#ifdef ENABLE_SCRIPTING
#include "app/script/luacpp.h"
#endif
#include <map>
namespace app {
enum class ConvertLayerParam { None, Background, Layer, Tilemap };
template<>
void Param<ConvertLayerParam>::fromString(const std::string& value)
{
if (value == "background")
setValue(ConvertLayerParam::Background);
else if (value == "layer")
setValue(ConvertLayerParam::Layer);
else if (value == "tilemap")
setValue(ConvertLayerParam::Tilemap);
else
setValue(ConvertLayerParam::None);
}
#ifdef ENABLE_SCRIPTING
template<>
void Param<ConvertLayerParam>::fromLua(lua_State* L, int index)
{
fromString(lua_tostring(L, index));
}
#endif // ENABLE_SCRIPTING
struct ConvertLayerParams : public NewParams {
Param<ConvertLayerParam> to { this, ConvertLayerParam::None, "to" };
};
class ConvertLayerCommand : public CommandWithNewParams<ConvertLayerParams> {
public:
ConvertLayerCommand();
private:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
std::string onGetFriendlyName() const override;
void copyCels(Tx& tx,
Layer* srcLayer,
Layer* newLayer);
};
ConvertLayerCommand::ConvertLayerCommand()
: CommandWithNewParams<ConvertLayerParams>(CommandId::ConvertLayer(), CmdRecordableFlag)
{
}
bool ConvertLayerCommand::onEnabled(Context* ctx)
{
if (!ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveSprite |
ContextFlags::HasActiveLayer |
ContextFlags::ActiveLayerIsVisible |
ContextFlags::ActiveLayerIsEditable))
return false;
// TODO add support to convert reference layers into regular layers or tilemaps
if (ctx->checkFlags(ContextFlags::ActiveLayerIsReference))
return false;
switch (params().to()) {
case ConvertLayerParam::Background:
return
// Doesn't have a background layer
!ctx->checkFlags(ContextFlags::HasBackgroundLayer) &&
// Convert a regular layer or tilemap into background
ctx->checkFlags(ContextFlags::ActiveLayerIsImage) &&
// TODO add support for background tliemaps
!ctx->checkFlags(ContextFlags::ActiveLayerIsTilemap);
case ConvertLayerParam::Layer:
return
// Convert a background layer into a transparent layer
ctx->checkFlags(ContextFlags::ActiveLayerIsImage |
ContextFlags::ActiveLayerIsBackground) ||
// or a tilemap into a regular layer
ctx->checkFlags(ContextFlags::ActiveLayerIsTilemap);
case ConvertLayerParam::Tilemap:
return
ctx->checkFlags(ContextFlags::ActiveLayerIsImage) &&
!ctx->checkFlags(ContextFlags::ActiveLayerIsTilemap) &&
// TODO add support for background tliemaps
!ctx->checkFlags(ContextFlags::ActiveLayerIsBackground);
default:
return false;
}
}
void ConvertLayerCommand::onExecute(Context* ctx)
{
ContextWriter writer(ctx);
Doc* document(writer.document());
{
Tx tx(ctx, friendlyName());
Site site = ctx->activeSite();
Sprite* sprite = site.sprite();
Layer* srcLayer = site.layer();
switch (params().to()) {
case ConvertLayerParam::Background:
// Layer -> Background
if (srcLayer->isTransparent()) {
ASSERT(srcLayer->isImage());
tx(new cmd::BackgroundFromLayer(static_cast<LayerImage*>(srcLayer)));
}
// Tilemap -> Background
else if (srcLayer->isTilemap()) {
auto newLayer = new LayerImage(sprite);
newLayer->configureAsBackground();
newLayer->setName(Strings::commands_NewFile_BackgroundLayer());
newLayer->setContinuous(srcLayer->isContinuous());
tx(new cmd::AddLayer(srcLayer->parent(), newLayer, srcLayer));
CelList srcCels;
srcLayer->getCels(srcCels);
for (Cel* srcCel : srcCels)
create_cel_copy(tx, srcCel, sprite, newLayer, srcCel->frame());
tx(new cmd::RemoveLayer(srcLayer));
}
break;
case ConvertLayerParam::Layer:
// Background -> Layer
if (srcLayer->isBackground()) {
tx(new cmd::LayerFromBackground(srcLayer));
}
// Background -> Tilemap
else if (srcLayer->isTilemap()) {
auto newLayer = new LayerImage(sprite);
newLayer->setName(srcLayer->name());
newLayer->setContinuous(srcLayer->isContinuous());
tx(new cmd::AddLayer(srcLayer->parent(), newLayer, srcLayer));
copyCels(tx, srcLayer, newLayer);
tx(new cmd::RemoveLayer(srcLayer));
}
break;
case ConvertLayerParam::Tilemap:
// Background or Transparent Layer -> Tilemap
if (srcLayer->isImage() &&
(srcLayer->isBackground() ||
srcLayer->isTransparent())) {
auto tileset = new Tileset(sprite, site.grid(), 1);
auto addTileset = new cmd::AddTileset(sprite, tileset);
tx(addTileset);
tileset_index tsi = addTileset->tilesetIndex();
auto newLayer = new LayerTilemap(sprite, tsi);
newLayer->setName(srcLayer->name());
newLayer->setContinuous(srcLayer->isContinuous());
tx(new cmd::AddLayer(srcLayer->parent(), newLayer, srcLayer));
copyCels(tx, srcLayer, newLayer);
tx(new cmd::RemoveLayer(srcLayer));
}
break;
}
tx.commit();
}
#ifdef ENABLE_UI
if (ctx->isUIAvailable())
update_screen_for_document(document);
#endif
}
void ConvertLayerCommand::copyCels(Tx& tx,
Layer* srcLayer,
Layer* newLayer)
{
std::map<doc::ObjectId, doc::Cel*> linkedCels;
CelList srcCels;
srcLayer->getCels(srcCels);
for (Cel* srcCel : srcCels) {
frame_t frame = srcCel->frame();
// Keep linked cels in the new layer
Cel* linkedSrcCel = srcCel->link();
if (linkedSrcCel) {
auto it = linkedCels.find(linkedSrcCel->id());
if (it != linkedCels.end()) {
tx(new cmd::CopyCel(
newLayer, linkedSrcCel->frame(),
newLayer, frame, true));
continue;
}
}
Cel* newCel = create_cel_copy(tx, srcCel, srcLayer->sprite(), newLayer, frame);
tx(new cmd::AddCel(newLayer, newCel));
linkedCels[srcCel->id()] = newCel;
}
}
std::string ConvertLayerCommand::onGetFriendlyName() const
{
switch (params().to()) {
case ConvertLayerParam::Background: return Strings::commands_ConvertLayer_Background(); break;
case ConvertLayerParam::Layer: return Strings::commands_ConvertLayer_Layer(); break;
case ConvertLayerParam::Tilemap: return Strings::commands_ConvertLayer_Tilemap(); break;
default: return getBaseFriendlyName();
}
}
Command* CommandFactory::createConvertLayerCommand()
{
return new ConvertLayerCommand;
}
} // namespace app

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-2017 David Capello
//
// This program is distributed under the terms of
@ -87,8 +87,12 @@ bool AppMenuItem::onProcessMessage(Message* msg)
switch (msg->type()) {
case kCloseMessage:
// disable the menu (the keyboard shortcuts are processed by "manager_msg_proc")
setEnabled(false);
// Don't disable items with submenus
if (!hasSubmenu()) {
// Disable the menu item (the keyboard shortcuts are processed
// by "manager_msg_proc")
setEnabled(false);
}
break;
}