mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-01 10:13:22 +00:00
Add "File > Export > Export Tileset" option (#3240)
Now we can export one (or several) tilesets in one sprite sheet (using the same options that are available in the Export Sprite Sheet dialog, e.g. like extruding tiles, related to #1982 in some way). Some changes: * New "Source" field and fromTilesets param for ExportSpriteSheet command * New ExportTileset command (which acts like ExportSpriteSheet but with fromTilesets=true by default) * Added --export-tileset CLI option
This commit is contained in:
parent
bdee7d98fe
commit
a2f61a3378
11
data/gui.xml
11
data/gui.xml
@ -455,6 +455,9 @@
|
||||
<key command="NewSpriteFromSelection" shortcut="Ctrl+Alt+N" mac="Cmd+Alt+N" />
|
||||
|
||||
<!-- Commands not associated to menu items and without shortcuts by default -->
|
||||
<key command="ExportSpriteSheet">
|
||||
<param name="source" value="tileset" />
|
||||
</key>
|
||||
<key command="NewLayer">
|
||||
<param name="tilemap" value="true" />
|
||||
</key>
|
||||
@ -675,12 +678,14 @@
|
||||
<separator />
|
||||
<menu text="@.file_export" group="file_export">
|
||||
<item command="SaveFileCopyAs" text="@.file_export_as" />
|
||||
<item command="ExportSpriteSheet" text="@.file_export_sprite_sheet" group="file_export_sub" />
|
||||
<item command="ExportSpriteSheet" text="@.file_export_sprite_sheet" group="file_export_1" />
|
||||
<separator />
|
||||
<item command="RepeatLastExport" text="@.file_repeat_last_export" />
|
||||
<item command="ExportTileset" text="@.file_export_tileset" group="file_export_2" />
|
||||
<separator />
|
||||
<item command="RepeatLastExport" text="@.file_repeat_last_export" group="file_export_last" />
|
||||
</menu>
|
||||
<menu text="@.file_import" group="file_import">
|
||||
<item command="ImportSpriteSheet" text="@.file_import_sprite_sheet" group="file_import_sub" />
|
||||
<item command="ImportSpriteSheet" text="@.file_import_sprite_sheet" group="file_import_1" />
|
||||
</menu>
|
||||
<separator id="scripts_menu_separator" />
|
||||
<menu id="scripts_menu" text="@.file_scripts" group="file_scripts">
|
||||
|
@ -307,6 +307,7 @@ DuplicateSprite = Duplicate Sprite
|
||||
DuplicateView = Duplicate View
|
||||
Exit = Exit
|
||||
ExportSpriteSheet = Export Sprite Sheet
|
||||
ExportTileset = Export Tileset
|
||||
Eyedropper = Eyedropper
|
||||
Fill = Fill Selection with Foreground Color
|
||||
FitScreen = Fit on Screen
|
||||
@ -706,6 +707,7 @@ merge_dups = Merge Duplicates
|
||||
merge_dups_tooltip = Similar frames can use the same sprite sheet rectangular area
|
||||
ignore_empty = Ignore Empty
|
||||
ignore_empty_tooltip = Do not include empty/transparent frames in the sprite sheet
|
||||
source = Source:
|
||||
layers = Layers:
|
||||
split_layers = Split Layers
|
||||
split_layers_tooltip = Generates one sprite for each layer
|
||||
@ -891,6 +893,7 @@ file_close_all = Close All
|
||||
file_export = &Export
|
||||
file_export_as = &Export As...
|
||||
file_export_sprite_sheet = Export &Sprite Sheet
|
||||
file_export_tileset = Export &Tileset
|
||||
file_repeat_last_export = Repeat &Last Export
|
||||
file_import = &Import
|
||||
file_import_sprite_sheet = &Import Sprite Sheet
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2019 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2019-2022 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2001-2018 David Capello -->
|
||||
<gui>
|
||||
<window id="export_sprite_sheet" text="@.title">
|
||||
@ -50,6 +50,10 @@
|
||||
<button id="close_sprite_section" icon="window_close_icon" />
|
||||
</hbox>
|
||||
<grid id="section_sprite" columns="4" expansive="true">
|
||||
<label text="@.source" />
|
||||
<combobox id="source" text="" cell_hspan="2" cell_align="horizontal" />
|
||||
<boxfiller />
|
||||
|
||||
<label text="@.layers" />
|
||||
<combobox id="layers" text="" cell_hspan="2" cell_align="horizontal" />
|
||||
<check id="split_layers" text="@.split_layers" tooltip="@.split_layers_tooltip" tooltip_dir="bottom" />
|
||||
|
@ -570,6 +570,7 @@ add_library(app-lib
|
||||
commands/command.cpp
|
||||
commands/commands.cpp
|
||||
commands/convert_layer.cpp
|
||||
commands/export_tileset.cpp
|
||||
commands/filters/cmd_brightness_contrast.cpp
|
||||
commands/filters/cmd_color_curve.cpp
|
||||
commands/filters/cmd_convolution_matrix.cpp
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -73,6 +73,7 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
, m_listTags(m_po.add("list-tags").description("List tags of the next given sprite\nor include frame tags in JSON data"))
|
||||
, m_listSlices(m_po.add("list-slices").description("List slices of the next given sprite\nor include slices in JSON data"))
|
||||
, m_oneFrame(m_po.add("oneframe").description("Load just the first frame"))
|
||||
, m_exportTileset(m_po.add("export-tileset").description("Export only tilesets from visible tilemap layers"))
|
||||
, m_verbose(m_po.add("verbose").mnemonic('v').description("Explain what is being done"))
|
||||
, m_debug(m_po.add("debug").description("Extreme verbose mode and\ncopy log to desktop"))
|
||||
#ifdef _WIN32
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -89,6 +89,7 @@ public:
|
||||
const Option& listTags() const { return m_listTags; }
|
||||
const Option& listSlices() const { return m_listSlices; }
|
||||
const Option& oneFrame() const { return m_oneFrame; }
|
||||
const Option& exportTileset() const { return m_exportTileset; }
|
||||
|
||||
bool hasExporterParams() const;
|
||||
#ifdef _WIN32
|
||||
@ -155,6 +156,7 @@ private:
|
||||
Option& m_listTags;
|
||||
Option& m_listSlices;
|
||||
Option& m_oneFrame;
|
||||
Option& m_exportTileset;
|
||||
|
||||
Option& m_verbose;
|
||||
Option& m_debug;
|
||||
|
@ -19,25 +19,6 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
CliOpenFile::CliOpenFile()
|
||||
{
|
||||
document = nullptr;
|
||||
fromFrame = -1;
|
||||
toFrame = -1;
|
||||
splitLayers = false;
|
||||
splitTags = false;
|
||||
splitSlices = false;
|
||||
allLayers = false;
|
||||
listLayers = false;
|
||||
listTags = false;
|
||||
listSlices = false;
|
||||
ignoreEmpty = false;
|
||||
trim = false;
|
||||
trimByGrid = false;
|
||||
oneFrame = false;
|
||||
crop = gfx::Rect();
|
||||
}
|
||||
|
||||
FileOpROI CliOpenFile::roi() const
|
||||
{
|
||||
ASSERT(document);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2016-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -21,29 +21,29 @@ namespace app {
|
||||
class FileOpROI;
|
||||
|
||||
struct CliOpenFile {
|
||||
Doc* document;
|
||||
Doc* document = nullptr;
|
||||
std::string filename;
|
||||
std::string filenameFormat;
|
||||
std::string tag;
|
||||
std::string slice;
|
||||
std::vector<std::string> includeLayers;
|
||||
std::vector<std::string> excludeLayers;
|
||||
doc::frame_t fromFrame, toFrame;
|
||||
bool splitLayers;
|
||||
bool splitTags;
|
||||
bool splitSlices;
|
||||
bool allLayers;
|
||||
bool listLayers;
|
||||
bool listTags;
|
||||
bool listSlices;
|
||||
bool ignoreEmpty;
|
||||
bool trim;
|
||||
bool trimByGrid;
|
||||
bool oneFrame;
|
||||
doc::frame_t fromFrame = -1;
|
||||
doc::frame_t toFrame = -1;
|
||||
bool splitLayers = false;
|
||||
bool splitTags = false;
|
||||
bool splitSlices = false;
|
||||
bool allLayers = false;
|
||||
bool listLayers = false;
|
||||
bool listTags = false;
|
||||
bool listSlices = false;
|
||||
bool ignoreEmpty = false;
|
||||
bool trim = false;
|
||||
bool trimByGrid = false;
|
||||
bool oneFrame = false;
|
||||
bool exportTileset = false;
|
||||
gfx::Rect crop;
|
||||
|
||||
CliOpenFile();
|
||||
|
||||
bool hasTag() const {
|
||||
return (!tag.empty());
|
||||
}
|
||||
|
@ -578,6 +578,10 @@ int CliProcessor::process(Context* ctx)
|
||||
else if (opt == &m_options.oneFrame()) {
|
||||
cof.oneFrame = true;
|
||||
}
|
||||
// --export-tileset
|
||||
else if (opt == &m_options.exportTileset()) {
|
||||
cof.exportTileset = true;
|
||||
}
|
||||
}
|
||||
// File names aren't associated to any option
|
||||
else {
|
||||
@ -677,12 +681,19 @@ bool CliProcessor::openFile(Context* ctx, CliOpenFile& cof)
|
||||
if (cof.hasLayersFilter())
|
||||
filterLayers(doc->sprite(), cof, filteredLayers);
|
||||
|
||||
m_exporter->addDocumentSamples(
|
||||
doc, tag,
|
||||
cof.splitLayers,
|
||||
cof.splitTags,
|
||||
(cof.hasLayersFilter() ? &filteredLayers: nullptr),
|
||||
(!selFrames.empty() ? &selFrames: nullptr));
|
||||
if (cof.exportTileset) {
|
||||
m_exporter->addTilesetsSamples(
|
||||
doc,
|
||||
(cof.hasLayersFilter() ? &filteredLayers: nullptr));
|
||||
}
|
||||
else {
|
||||
m_exporter->addDocumentSamples(
|
||||
doc, tag,
|
||||
cof.splitLayers,
|
||||
cof.splitTags,
|
||||
(cof.hasLayersFilter() ? &filteredLayers: nullptr),
|
||||
(!selFrames.empty() ? &selFrames: nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/new_params.h"
|
||||
#include "app/commands/cmd_export_sprite_sheet.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/doc.h"
|
||||
@ -37,7 +37,10 @@
|
||||
#include "base/string.h"
|
||||
#include "base/thread.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/tag.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tilesets.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/system.h"
|
||||
@ -53,37 +56,6 @@ using namespace ui;
|
||||
|
||||
namespace {
|
||||
|
||||
struct ExportSpriteSheetParams : public NewParams {
|
||||
Param<bool> ui { this, true, "ui" };
|
||||
Param<bool> askOverwrite { this, true, { "askOverwrite", "ask-overwrite" } };
|
||||
Param<app::SpriteSheetType> type { this, app::SpriteSheetType::None, "type" };
|
||||
Param<int> columns { this, 0, "columns" };
|
||||
Param<int> rows { this, 0, "rows" };
|
||||
Param<int> width { this, 0, "width" };
|
||||
Param<int> height { this, 0, "height" };
|
||||
Param<std::string> textureFilename { this, std::string(), "textureFilename" };
|
||||
Param<std::string> dataFilename { this, std::string(), "dataFilename" };
|
||||
Param<SpriteSheetDataFormat> dataFormat { this, SpriteSheetDataFormat::Default, "dataFormat" };
|
||||
Param<std::string> filenameFormat { this, std::string(), "filenameFormat" };
|
||||
Param<int> borderPadding { this, 0, "borderPadding" };
|
||||
Param<int> shapePadding { this, 0, "shapePadding" };
|
||||
Param<int> innerPadding { this, 0, "innerPadding" };
|
||||
Param<bool> trimSprite { this, false, "trimSprite" };
|
||||
Param<bool> trim { this, false, "trim" };
|
||||
Param<bool> trimByGrid { this, false, "trimByGrid" };
|
||||
Param<bool> extrude { this, false, "extrude" };
|
||||
Param<bool> ignoreEmpty { this, false, "ignoreEmpty" };
|
||||
Param<bool> mergeDuplicates { this, false, "mergeDuplicates" };
|
||||
Param<bool> openGenerated { this, false, "openGenerated" };
|
||||
Param<std::string> layer { this, std::string(), "layer" };
|
||||
Param<std::string> tag { this, std::string(), "tag" };
|
||||
Param<bool> splitLayers { this, false, "splitLayers" };
|
||||
Param<bool> splitTags { this, false, "splitTags" };
|
||||
Param<bool> listLayers { this, true, "listLayers" };
|
||||
Param<bool> listTags { this, true, "listTags" };
|
||||
Param<bool> listSlices { this, true, "listSlices" };
|
||||
};
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
|
||||
enum Section {
|
||||
@ -93,6 +65,11 @@ enum Section {
|
||||
kSectionOutput,
|
||||
};
|
||||
|
||||
enum Source {
|
||||
kSource_Sprite,
|
||||
kSource_Tilesets,
|
||||
};
|
||||
|
||||
enum ConstraintType {
|
||||
kConstraintType_None,
|
||||
kConstraintType_Cols,
|
||||
@ -197,6 +174,7 @@ Doc* generate_sprite_sheet_from_params(
|
||||
const bool listLayers = params.listLayers();
|
||||
const bool listTags = params.listTags();
|
||||
const bool listSlices = params.listSlices();
|
||||
const bool fromTilesets = params.fromTilesets();
|
||||
|
||||
SelectedFrames selFrames;
|
||||
Tag* tag = calculate_selected_frames(site, tagName, selFrames);
|
||||
@ -226,10 +204,16 @@ Doc* generate_sprite_sheet_from_params(
|
||||
}
|
||||
|
||||
exporter.reset();
|
||||
exporter.addDocumentSamples(
|
||||
doc, tag, splitLayers, splitTags,
|
||||
!selLayers.empty() ? &selLayers: nullptr,
|
||||
!selFrames.empty() ? &selFrames: nullptr);
|
||||
if (fromTilesets)
|
||||
exporter.addTilesetsSamples(
|
||||
doc,
|
||||
!selLayers.empty() ? &selLayers: nullptr);
|
||||
else {
|
||||
exporter.addDocumentSamples(
|
||||
doc, tag, splitLayers, splitTags,
|
||||
!selLayers.empty() ? &selLayers: nullptr,
|
||||
!selFrames.empty() ? &selFrames: nullptr);
|
||||
}
|
||||
|
||||
if (saveData) {
|
||||
if (!filename.empty())
|
||||
@ -373,6 +357,14 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
static_assert(kSource_Sprite == 0 &&
|
||||
kSource_Tilesets == 1,
|
||||
"Source enum has changed");
|
||||
source()->addItem(new ListItem("Sprite"));
|
||||
source()->addItem(new ListItem("Tilesets"));
|
||||
if (params.fromTilesets())
|
||||
source()->setSelectedItemIndex(int(kSource_Tilesets));
|
||||
|
||||
fill_layers_combobox(
|
||||
m_sprite, layers(), params.layer());
|
||||
|
||||
@ -446,6 +438,7 @@ public:
|
||||
trimSpriteEnabled()->Click.connect([this]{ onTrimEnabledChange(); });
|
||||
trimEnabled()->Click.connect([this]{ onTrimEnabledChange(); });
|
||||
gridTrimEnabled()->Click.connect([this]{ generatePreview(); });
|
||||
source()->Change.connect([this]{ generatePreview(); });
|
||||
layers()->Change.connect([this]{ generatePreview(); });
|
||||
splitLayers()->Click.connect([this]{ onSplitLayersOrFrames(); });
|
||||
splitTags()->Click.connect([this]{ onSplitLayersOrFrames(); });
|
||||
@ -536,6 +529,7 @@ public:
|
||||
params.listLayers (listLayersValue());
|
||||
params.listTags (listTagsValue());
|
||||
params.listSlices (listSlicesValue());
|
||||
params.fromTilesets (source()->getSelectedItemIndex() == int(kSource_Tilesets));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1169,16 +1163,8 @@ private:
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class ExportSpriteSheetCommand : public CommandWithNewParams<ExportSpriteSheetParams> {
|
||||
public:
|
||||
ExportSpriteSheetCommand();
|
||||
protected:
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
};
|
||||
|
||||
ExportSpriteSheetCommand::ExportSpriteSheetCommand()
|
||||
: CommandWithNewParams(CommandId::ExportSpriteSheet(), CmdRecordableFlag)
|
||||
ExportSpriteSheetCommand::ExportSpriteSheetCommand(const char* id)
|
||||
: CommandWithNewParams(id, CmdRecordableFlag)
|
||||
{
|
||||
}
|
||||
|
||||
|
62
src/app/commands/cmd_export_sprite_sheet.h
Normal file
62
src/app/commands/cmd_export_sprite_sheet.h
Normal file
@ -0,0 +1,62 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_COMMANDS_CMD_EXPORT_SPRITE_SHEET_H_INCLUDED
|
||||
#define APP_COMMANDS_CMD_EXPORT_SPRITE_SHEET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/commands/new_params.h"
|
||||
#include "app/sprite_sheet_data_format.h"
|
||||
#include "app/sprite_sheet_type.h"
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
namespace app {
|
||||
|
||||
struct ExportSpriteSheetParams : public NewParams {
|
||||
Param<bool> ui { this, true, "ui" };
|
||||
Param<bool> askOverwrite { this, true, { "askOverwrite", "ask-overwrite" } };
|
||||
Param<app::SpriteSheetType> type { this, app::SpriteSheetType::None, "type" };
|
||||
Param<int> columns { this, 0, "columns" };
|
||||
Param<int> rows { this, 0, "rows" };
|
||||
Param<int> width { this, 0, "width" };
|
||||
Param<int> height { this, 0, "height" };
|
||||
Param<std::string> textureFilename { this, std::string(), "textureFilename" };
|
||||
Param<std::string> dataFilename { this, std::string(), "dataFilename" };
|
||||
Param<SpriteSheetDataFormat> dataFormat { this, SpriteSheetDataFormat::Default, "dataFormat" };
|
||||
Param<std::string> filenameFormat { this, std::string(), "filenameFormat" };
|
||||
Param<int> borderPadding { this, 0, "borderPadding" };
|
||||
Param<int> shapePadding { this, 0, "shapePadding" };
|
||||
Param<int> innerPadding { this, 0, "innerPadding" };
|
||||
Param<bool> trimSprite { this, false, "trimSprite" };
|
||||
Param<bool> trim { this, false, "trim" };
|
||||
Param<bool> trimByGrid { this, false, "trimByGrid" };
|
||||
Param<bool> extrude { this, false, "extrude" };
|
||||
Param<bool> ignoreEmpty { this, false, "ignoreEmpty" };
|
||||
Param<bool> mergeDuplicates { this, false, "mergeDuplicates" };
|
||||
Param<bool> openGenerated { this, false, "openGenerated" };
|
||||
Param<std::string> layer { this, std::string(), "layer" };
|
||||
Param<std::string> tag { this, std::string(), "tag" };
|
||||
Param<bool> splitLayers { this, false, "splitLayers" };
|
||||
Param<bool> splitTags { this, false, "splitTags" };
|
||||
Param<bool> listLayers { this, true, "listLayers" };
|
||||
Param<bool> listTags { this, true, "listTags" };
|
||||
Param<bool> listSlices { this, true, "listSlices" };
|
||||
Param<bool> fromTilesets { this, false, "fromTilesets" };
|
||||
};
|
||||
|
||||
class ExportSpriteSheetCommand : public CommandWithNewParams<ExportSpriteSheetParams> {
|
||||
public:
|
||||
ExportSpriteSheetCommand(const char* id = CommandId::ExportSpriteSheet());
|
||||
protected:
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif // APP_COMMANDS_CMD_EXPORT_SPRITE_SHEET_H_INCLUDED
|
@ -21,6 +21,7 @@ FOR_EACH_COMMAND(CopyTiles)
|
||||
FOR_EACH_COMMAND(CropSprite)
|
||||
FOR_EACH_COMMAND(Despeckle)
|
||||
FOR_EACH_COMMAND(ExportSpriteSheet)
|
||||
FOR_EACH_COMMAND(ExportTileset)
|
||||
FOR_EACH_COMMAND(Fill)
|
||||
FOR_EACH_COMMAND(FlattenLayers)
|
||||
FOR_EACH_COMMAND(Flip)
|
||||
|
65
src/app/commands/export_tileset.cpp
Normal file
65
src/app/commands/export_tileset.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 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/commands/cmd_export_sprite_sheet.h"
|
||||
#include "app/commands/command_ids.h"
|
||||
#include "app/context.h"
|
||||
#include "app/site.h"
|
||||
#include "app/ui/layer_frame_comboboxes.h"
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class ExportTilesetCommand : public ExportSpriteSheetCommand {
|
||||
public:
|
||||
ExportTilesetCommand();
|
||||
protected:
|
||||
void onResetValues() override;
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
};
|
||||
|
||||
ExportTilesetCommand::ExportTilesetCommand()
|
||||
: ExportSpriteSheetCommand(CommandId::ExportTileset())
|
||||
{
|
||||
}
|
||||
|
||||
void ExportTilesetCommand::onResetValues()
|
||||
{
|
||||
ExportSpriteSheetCommand::onResetValues();
|
||||
|
||||
// Default values for Export Tileset
|
||||
params().fromTilesets(true);
|
||||
params().layer(kSelectedLayers);
|
||||
params().dataFormat(SpriteSheetDataFormat::JsonArray);
|
||||
}
|
||||
|
||||
bool ExportTilesetCommand::onEnabled(Context* ctx)
|
||||
{
|
||||
if (ExportSpriteSheetCommand::onEnabled(ctx) &&
|
||||
ctx->checkFlags(ContextFlags::HasActiveLayer)) {
|
||||
Site site = ctx->activeSite();
|
||||
if (site.layer() && site.layer()->isTilemap())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ExportTilesetCommand::onExecute(Context* ctx)
|
||||
{
|
||||
ExportSpriteSheetCommand::onExecute(ctx);
|
||||
}
|
||||
|
||||
Command* CommandFactory::createExportTilesetCommand()
|
||||
{
|
||||
return new ExportTilesetCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -139,7 +139,7 @@ namespace app {
|
||||
T& params() { return m_params; }
|
||||
const T& params() const { return m_params; }
|
||||
|
||||
private:
|
||||
protected:
|
||||
void onResetValues() override {
|
||||
m_params.resetValues();
|
||||
}
|
||||
@ -148,6 +148,7 @@ namespace app {
|
||||
return m_params.getParam(k);
|
||||
}
|
||||
|
||||
private:
|
||||
T m_params;
|
||||
};
|
||||
|
||||
|
@ -104,6 +104,13 @@ DocExporter::Item::Item(Doc* doc,
|
||||
{
|
||||
}
|
||||
|
||||
DocExporter::Item::Item(Doc* doc,
|
||||
const doc::ImageRef& image)
|
||||
: doc(doc)
|
||||
, image(image)
|
||||
{
|
||||
}
|
||||
|
||||
DocExporter::Item::Item(Item&& other) = default;
|
||||
DocExporter::Item::~Item() = default;
|
||||
|
||||
@ -129,6 +136,8 @@ doc::SelectedFrames DocExporter::Item::getSelectedFrames() const
|
||||
frames.insert(std::clamp(tag->fromFrame(), 0, doc->sprite()->lastFrame()),
|
||||
std::clamp(tag->toFrame(), 0, doc->sprite()->lastFrame()));
|
||||
}
|
||||
else if (isOneImageOnly())
|
||||
frames.insert(0);
|
||||
else {
|
||||
frames.insert(0, doc->sprite()->lastFrame());
|
||||
}
|
||||
@ -137,7 +146,10 @@ doc::SelectedFrames DocExporter::Item::getSelectedFrames() const
|
||||
|
||||
class DocExporter::Sample {
|
||||
public:
|
||||
Sample(Doc* document, Sprite* sprite, SelectedLayers* selLayers,
|
||||
Sample(Doc* document,
|
||||
Sprite* sprite,
|
||||
const ImageRef& image,
|
||||
SelectedLayers* selLayers,
|
||||
frame_t frame,
|
||||
const Tag* tag,
|
||||
const std::string& filename,
|
||||
@ -145,6 +157,7 @@ public:
|
||||
const bool extrude) :
|
||||
m_document(document),
|
||||
m_sprite(sprite),
|
||||
m_image(image),
|
||||
m_selLayers(selLayers),
|
||||
m_frame(frame),
|
||||
m_tag(tag),
|
||||
@ -153,9 +166,10 @@ public:
|
||||
m_extrude(extrude),
|
||||
m_isLinked(false),
|
||||
m_isDuplicated(false),
|
||||
m_originalSize(sprite->width(), sprite->height()),
|
||||
m_trimmedBounds(0, 0, sprite->width(), sprite->height()),
|
||||
m_inTextureBounds(std::make_shared<gfx::Rect>(0, 0, sprite->width(), sprite->height())) {
|
||||
m_originalSize(image ? image->width(): sprite->width(),
|
||||
image ? image->height(): sprite->height()),
|
||||
m_trimmedBounds(m_originalSize),
|
||||
m_inTextureBounds(std::make_shared<gfx::Rect>(m_trimmedBounds)) {
|
||||
}
|
||||
|
||||
Doc* document() const { return m_document; }
|
||||
@ -221,6 +235,11 @@ public:
|
||||
ImageRef createRender(ImageBufferPtr& imageBuf) {
|
||||
ASSERT(m_sprite);
|
||||
|
||||
// We use the m_image as it is, it doesn't require a special
|
||||
// render.
|
||||
if (m_image)
|
||||
return m_image;
|
||||
|
||||
ImageRef render(
|
||||
Image::create(m_sprite->pixelFormat(),
|
||||
m_trimmedBounds.w,
|
||||
@ -266,19 +285,32 @@ public:
|
||||
for (int j=0; j<3; ++j) {
|
||||
for (int i=0; i<3; ++i) {
|
||||
gfx::Clip clip(x+dx[i], y+dy[j], gfx::RectT<int>(srcx[i], srcy[j], szx[i], szy[j]));
|
||||
render.renderSprite(dst, m_sprite, m_frame, clip);
|
||||
if (m_image) {
|
||||
dst->copy(m_image.get(), clip);
|
||||
}
|
||||
else {
|
||||
render.renderSprite(dst, m_sprite, m_frame, clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
gfx::Clip clip(x, y, m_trimmedBounds);
|
||||
render.renderSprite(dst, m_sprite, m_frame, clip);
|
||||
if (m_image) {
|
||||
dst->copy(m_image.get(), clip);
|
||||
}
|
||||
else {
|
||||
render.renderSprite(dst, m_sprite, m_frame, clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Doc* m_document;
|
||||
Sprite* m_sprite;
|
||||
// In case that this Sample references just one image to export
|
||||
// (e.g. like a Tileset tile image) this can be != nullptr.
|
||||
ImageRef m_image;
|
||||
SelectedLayers* m_selLayers;
|
||||
frame_t m_frame;
|
||||
const Tag* m_tag;
|
||||
@ -347,7 +379,7 @@ public:
|
||||
int shapePadding,
|
||||
int& width, int& height,
|
||||
base::task_token& token) override {
|
||||
DX_TRACE("SimpleLayoutSamples type", (int)m_type, width, height);
|
||||
DX_TRACE("DX: SimpleLayoutSamples type", (int)m_type, width, height);
|
||||
|
||||
const bool breakBands =
|
||||
(m_type == SpriteSheetType::Columns ||
|
||||
@ -479,7 +511,7 @@ public:
|
||||
++i;
|
||||
}
|
||||
|
||||
DX_TRACE("-> SimpleLayoutSamples", width, height);
|
||||
DX_TRACE("DX: -> SimpleLayoutSamples", width, height);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -675,7 +707,7 @@ Doc* DocExporter::exportSheet(Context* ctx, base::task_token& token)
|
||||
|
||||
// Save the image files.
|
||||
if (!m_textureFilename.empty()) {
|
||||
DX_TRACE("DocExporter::exportSheet", m_textureFilename);
|
||||
DX_TRACE("DX: exportSheet", m_textureFilename);
|
||||
textureDocument->setFilename(m_textureFilename.c_str());
|
||||
int ret = save_document(ctx, textureDocument.get());
|
||||
if (ret == 0)
|
||||
@ -702,11 +734,18 @@ void DocExporter::addDocument(
|
||||
const doc::SelectedLayers* selLayers,
|
||||
const doc::SelectedFrames* selFrames)
|
||||
{
|
||||
DX_TRACE("DocExporter::addDocument doc=", doc, "tag=", tag);
|
||||
|
||||
DX_TRACE("DX: addDocument doc=", doc, "tag=", tag);
|
||||
m_documents.push_back(Item(doc, tag, selLayers, selFrames));
|
||||
}
|
||||
|
||||
void DocExporter::addImage(
|
||||
Doc* doc,
|
||||
const doc::ImageRef& image)
|
||||
{
|
||||
DX_TRACE("DX: addImage doc=", doc, "image=", image.get());
|
||||
m_documents.push_back(Item(doc, image));
|
||||
}
|
||||
|
||||
int DocExporter::addDocumentSamples(
|
||||
Doc* doc,
|
||||
const doc::Tag* thisTag,
|
||||
@ -715,7 +754,7 @@ int DocExporter::addDocumentSamples(
|
||||
const doc::SelectedLayers* selLayers,
|
||||
const doc::SelectedFrames* selFrames)
|
||||
{
|
||||
DX_TRACE("DocExporter::addDocumentSamples");
|
||||
DX_TRACE("DX: addDocumentSamples");
|
||||
|
||||
std::vector<const Tag*> tags;
|
||||
|
||||
@ -804,6 +843,36 @@ int DocExporter::addDocumentSamples(
|
||||
return std::max(1, items);
|
||||
}
|
||||
|
||||
int DocExporter::addTilesetsSamples(
|
||||
Doc* doc,
|
||||
const doc::SelectedLayers* selLayers)
|
||||
{
|
||||
LayerList layers;
|
||||
if (selLayers)
|
||||
layers = selLayers->toAllLayersList();
|
||||
else
|
||||
layers = doc->sprite()->allVisibleLayers();
|
||||
|
||||
std::set<doc::ObjectId> alreadyExported;
|
||||
int items = 0;
|
||||
for (auto& layer : layers) {
|
||||
if (layer->isTilemap()) {
|
||||
Tileset* ts = dynamic_cast<LayerTilemap*>(layer)->tileset();
|
||||
|
||||
if (alreadyExported.find(ts->id()) == alreadyExported.end()) {
|
||||
for (const ImageRef& image : *ts) {
|
||||
addImage(doc, image);
|
||||
++items;
|
||||
}
|
||||
alreadyExported.insert(ts->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DX_TRACE("DX: addTilesetsSamples items=", items);
|
||||
return items;
|
||||
}
|
||||
|
||||
void DocExporter::captureSamples(Samples& samples,
|
||||
base::task_token& token)
|
||||
{
|
||||
@ -834,24 +903,34 @@ void DocExporter::captureSamples(Samples& samples,
|
||||
(tag != nullptr)); // Has tag
|
||||
}
|
||||
|
||||
gfx::Rect spriteBounds = sprite->bounds();
|
||||
if (m_trimSprite) {
|
||||
if (m_cache.spriteId == sprite->id() &&
|
||||
m_cache.spriteVer == sprite->version() &&
|
||||
m_cache.trimmedByGrid == m_trimByGrid) {
|
||||
spriteBounds = m_cache.trimmedBounds;
|
||||
}
|
||||
else {
|
||||
spriteBounds = get_trimmed_bounds(sprite, m_trimByGrid);
|
||||
if (spriteBounds.isEmpty())
|
||||
spriteBounds = gfx::Rect(0, 0, 1, 1);
|
||||
gfx::Rect spriteBounds;
|
||||
|
||||
// Cache trimmed bounds so we don't have to recalculate them
|
||||
// in the next iteration/preview.
|
||||
m_cache.spriteId = sprite->id();
|
||||
m_cache.spriteVer = sprite->version();
|
||||
m_cache.trimmedByGrid = m_trimByGrid;
|
||||
m_cache.trimmedBounds = spriteBounds;
|
||||
// This item is only one image (e.g. a tileset tile)
|
||||
if (item.isOneImageOnly()) {
|
||||
ASSERT(item.image);
|
||||
spriteBounds = item.image->bounds();
|
||||
}
|
||||
// This item comes from the sprite canvas
|
||||
else {
|
||||
spriteBounds = sprite->bounds();
|
||||
if (m_trimSprite) {
|
||||
if (m_cache.spriteId == sprite->id() &&
|
||||
m_cache.spriteVer == sprite->version() &&
|
||||
m_cache.trimmedByGrid == m_trimByGrid) {
|
||||
spriteBounds = m_cache.trimmedBounds;
|
||||
}
|
||||
else {
|
||||
spriteBounds = get_trimmed_bounds(sprite, m_trimByGrid);
|
||||
if (spriteBounds.isEmpty())
|
||||
spriteBounds = gfx::Rect(0, 0, 1, 1);
|
||||
|
||||
// Cache trimmed bounds so we don't have to recalculate them
|
||||
// in the next iteration/preview.
|
||||
m_cache.spriteId = sprite->id();
|
||||
m_cache.spriteVer = sprite->version();
|
||||
m_cache.trimmedByGrid = m_trimByGrid;
|
||||
m_cache.trimmedBounds = spriteBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -878,8 +957,9 @@ void DocExporter::captureSamples(Samples& samples,
|
||||
std::string filename = filename_formatter(format, fnInfo);
|
||||
|
||||
Sample sample(
|
||||
doc, sprite, item.selLayers.get(), frame, innerTag,
|
||||
filename, m_innerPadding, m_extrude);
|
||||
doc, sprite, item.image, item.selLayers.get(),
|
||||
frame, innerTag, filename,
|
||||
m_innerPadding, m_extrude);
|
||||
Cel* cel = nullptr;
|
||||
Cel* link = nullptr;
|
||||
bool done = false;
|
||||
@ -892,7 +972,8 @@ void DocExporter::captureSamples(Samples& samples,
|
||||
|
||||
// Re-use linked samples
|
||||
bool alreadyTrimmed = false;
|
||||
if (link && m_mergeDuplicates) {
|
||||
if (link && m_mergeDuplicates &&
|
||||
!item.isOneImageOnly()) {
|
||||
for (const Sample& other : samples) {
|
||||
if (token.canceled())
|
||||
return;
|
||||
@ -915,7 +996,8 @@ void DocExporter::captureSamples(Samples& samples,
|
||||
ASSERT(done || (!done && tag));
|
||||
}
|
||||
|
||||
if (!done && (m_ignoreEmptyCels || m_trimCels)) {
|
||||
if (!done && (m_ignoreEmptyCels || m_trimCels) &&
|
||||
!item.isOneImageOnly()) {
|
||||
// Ignore empty cels
|
||||
if (layer && layer->isImage() && !cel && m_ignoreEmptyCels)
|
||||
continue;
|
||||
@ -1306,6 +1388,9 @@ void DocExporter::createDataFile(const Samples& samples,
|
||||
|
||||
bool firstTag = true;
|
||||
for (auto& item : m_documents) {
|
||||
if (item.isOneImageOnly())
|
||||
continue;
|
||||
|
||||
Doc* doc = item.doc;
|
||||
Sprite* sprite = doc->sprite();
|
||||
|
||||
@ -1337,6 +1422,9 @@ void DocExporter::createDataFile(const Samples& samples,
|
||||
if (m_listLayers) {
|
||||
LayerList metaLayers;
|
||||
for (auto& item : m_documents) {
|
||||
if (item.isOneImageOnly())
|
||||
continue;
|
||||
|
||||
Doc* doc = item.doc;
|
||||
Sprite* sprite = doc->sprite();
|
||||
Layer* root = sprite->root();
|
||||
@ -1432,6 +1520,9 @@ void DocExporter::createDataFile(const Samples& samples,
|
||||
|
||||
bool firstSlice = true;
|
||||
for (auto& item : m_documents) {
|
||||
if (item.isOneImageOnly())
|
||||
continue;
|
||||
|
||||
Doc* doc = item.doc;
|
||||
Sprite* sprite = doc->sprite();
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "base/disable_copying.h"
|
||||
#include "base/task.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/image_buffer.h"
|
||||
#include "doc/object_id.h"
|
||||
#include "doc/object_version.h"
|
||||
@ -82,6 +83,10 @@ namespace app {
|
||||
const doc::SelectedLayers* selLayers,
|
||||
const doc::SelectedFrames* selFrames);
|
||||
|
||||
void addImage(
|
||||
Doc* doc,
|
||||
const doc::ImageRef& image);
|
||||
|
||||
int addDocumentSamples(
|
||||
Doc* doc,
|
||||
const doc::Tag* tag,
|
||||
@ -90,6 +95,10 @@ namespace app {
|
||||
const doc::SelectedLayers* selLayers,
|
||||
const doc::SelectedFrames* selFrames);
|
||||
|
||||
int addTilesetsSamples(
|
||||
Doc* doc,
|
||||
const doc::SelectedLayers* selLayers);
|
||||
|
||||
Doc* exportSheet(Context* ctx, base::task_token& token);
|
||||
gfx::Size calculateSheetSize();
|
||||
|
||||
@ -121,11 +130,14 @@ namespace app {
|
||||
const doc::Tag* tag = nullptr;
|
||||
std::unique_ptr<doc::SelectedLayers> selLayers;
|
||||
std::unique_ptr<doc::SelectedFrames> selFrames;
|
||||
doc::ImageRef image;
|
||||
|
||||
Item(Doc* doc,
|
||||
const doc::Tag* tag,
|
||||
const doc::SelectedLayers* selLayers,
|
||||
const doc::SelectedFrames* selFrames);
|
||||
Item(Doc* doc,
|
||||
const doc::ImageRef& image);
|
||||
Item(Item&& other);
|
||||
~Item();
|
||||
|
||||
@ -135,6 +147,8 @@ namespace app {
|
||||
|
||||
int frames() const;
|
||||
doc::SelectedFrames getSelectedFrames() const;
|
||||
|
||||
bool isOneImageOnly() const { return image != nullptr; }
|
||||
};
|
||||
typedef std::vector<Item> Items;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user