mirror of https://github.com/aseprite/aseprite.git
Add support to specify multiple layers in CLI to export (fix #1174)
Now we can use --layer multiple times to modify the --save-as and --sheet behavior to render only the filtered/selected layers. This can be combined with --split-layers too.
This commit is contained in:
parent
f4b5340dfb
commit
a1ce0c5c73
|
|
@ -12,6 +12,7 @@
|
|||
#include "gfx/rect.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ namespace app {
|
|||
std::string filename;
|
||||
std::string filenameFormat;
|
||||
std::string frameTag;
|
||||
std::string importLayer;
|
||||
std::vector<std::string> importLayers;
|
||||
doc::frame_t fromFrame, toFrame;
|
||||
bool splitLayers;
|
||||
bool splitTags;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,53 @@
|
|||
|
||||
namespace app {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string get_layer_path(Layer* layer)
|
||||
{
|
||||
std::string path;
|
||||
for (; layer != layer->sprite()->root(); layer=layer->parent()) {
|
||||
if (!path.empty())
|
||||
path.insert(0, "/");
|
||||
path.insert(0, layer->name());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
bool match_path(const std::string& filter, const std::string& layer_path)
|
||||
{
|
||||
if (filter == layer_path)
|
||||
return true;
|
||||
|
||||
std::vector<std::string> a, b;
|
||||
base::split_string(filter, a, "/");
|
||||
base::split_string(layer_path, b, "/");
|
||||
|
||||
for (int i=0; i<a.size() && i<b.size(); ++i) {
|
||||
if (a[i] != b[i] && a[i] != "*")
|
||||
return false;
|
||||
}
|
||||
|
||||
return (a.size() <= b.size());
|
||||
}
|
||||
|
||||
void filter_layers(const LayerList& layers,
|
||||
const std::vector<std::string>& filters,
|
||||
SelectedLayers& filteredLayers)
|
||||
{
|
||||
for (const auto& filter : filters) {
|
||||
bool filterWithGroup = (filter.find('/') != std::string::npos);
|
||||
|
||||
for (Layer* layer : layers) {
|
||||
if (layer->name() == filter ||
|
||||
(filterWithGroup && match_path(filter, get_layer_path(layer))))
|
||||
filteredLayers.insert(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
CliProcessor::CliProcessor(CliDelegate* delegate,
|
||||
const AppOptions& options)
|
||||
: m_delegate(delegate)
|
||||
|
|
@ -127,7 +174,7 @@ void CliProcessor::process()
|
|||
}
|
||||
// --layer <layer-name>
|
||||
else if (opt == &m_options.layer()) {
|
||||
cof.importLayer = value.value();
|
||||
cof.importLayers.push_back(value.value());
|
||||
}
|
||||
// --all-layers
|
||||
else if (opt == &m_options.allLayers()) {
|
||||
|
|
@ -371,25 +418,35 @@ bool CliProcessor::openFile(CliOpenFile& cof)
|
|||
}
|
||||
}
|
||||
|
||||
if (!cof.importLayer.empty()) {
|
||||
Layer* foundLayer = nullptr;
|
||||
for (Layer* layer : doc->sprite()->allLayers()) {
|
||||
if (layer->name() == cof.importLayer) {
|
||||
foundLayer = layer;
|
||||
break;
|
||||
if (!cof.importLayers.empty()) {
|
||||
SelectedLayers filteredLayers;
|
||||
filter_layers(doc->sprite()->allLayers(), cof.importLayers, filteredLayers);
|
||||
|
||||
if (cof.splitLayers) {
|
||||
for (Layer* layer : filteredLayers) {
|
||||
SelectedLayers oneLayer;
|
||||
oneLayer.insert(layer);
|
||||
|
||||
m_exporter->addDocument(doc, frameTag, &oneLayer,
|
||||
(!selFrames.empty() ? &selFrames: nullptr));
|
||||
}
|
||||
}
|
||||
if (foundLayer)
|
||||
m_exporter->addDocument(doc, foundLayer, frameTag,
|
||||
else {
|
||||
m_exporter->addDocument(doc, frameTag, &filteredLayers,
|
||||
(!selFrames.empty() ? &selFrames: nullptr));
|
||||
}
|
||||
}
|
||||
else if (cof.splitLayers) {
|
||||
for (auto layer : doc->sprite()->allVisibleLayers())
|
||||
m_exporter->addDocument(doc, layer, frameTag,
|
||||
for (auto layer : doc->sprite()->allVisibleLayers()) {
|
||||
SelectedLayers oneLayer;
|
||||
oneLayer.insert(layer);
|
||||
|
||||
m_exporter->addDocument(doc, frameTag, &oneLayer,
|
||||
(!selFrames.empty() ? &selFrames: nullptr));
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_exporter->addDocument(doc, nullptr, frameTag,
|
||||
m_exporter->addDocument(doc, frameTag, nullptr,
|
||||
(!selFrames.empty() ? &selFrames: nullptr));
|
||||
}
|
||||
}
|
||||
|
|
@ -441,13 +498,9 @@ void CliProcessor::saveFile(const CliOpenFile& cof)
|
|||
layers.push_back(layer);
|
||||
}
|
||||
else {
|
||||
// Show only one layer
|
||||
if (!cof.importLayer.empty()) {
|
||||
for (Layer* layer : allLayers) {
|
||||
if (layer->name() == cof.importLayer)
|
||||
filteredLayers.insert(layer);
|
||||
}
|
||||
}
|
||||
// Filter layers
|
||||
if (!cof.importLayers.empty())
|
||||
filter_layers(allLayers, cof.importLayers, filteredLayers);
|
||||
|
||||
// All visible layers
|
||||
layers.push_back(nullptr);
|
||||
|
|
@ -522,7 +575,7 @@ void CliProcessor::saveFile(const CliOpenFile& cof)
|
|||
else if (layer->parent() != layer->sprite()->root())
|
||||
fnInfo.groupName(layer->parent()->name());
|
||||
|
||||
itemCof.importLayer = layer->name();
|
||||
itemCof.importLayers.push_back(layer->name());
|
||||
}
|
||||
if (frameTag) {
|
||||
fnInfo
|
||||
|
|
|
|||
|
|
@ -69,8 +69,12 @@ void PreviewCliDelegate::afterOpenFile(const CliOpenFile& cof)
|
|||
if (cof.allLayers)
|
||||
std::cout << " - Make all layers visible\n";
|
||||
|
||||
if (!cof.importLayer.empty())
|
||||
std::cout << " - Make layer '" << cof.importLayer << "' visible only (hide all other layers)\n";
|
||||
if (!cof.importLayers.empty()) {
|
||||
std::cout << " - Filter layers:";
|
||||
for (const auto& filter : cof.importLayers)
|
||||
std::cout << ' ' << filter;
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::saveFile(const CliOpenFile& cof)
|
||||
|
|
@ -97,8 +101,12 @@ void PreviewCliDelegate::saveFile(const CliOpenFile& cof)
|
|||
<< cof.document->sprite()->width() << "x"
|
||||
<< cof.document->sprite()->height() << "\n";
|
||||
|
||||
if (!cof.importLayer.empty())
|
||||
std::cout << " - Layer: '" << cof.importLayer << "'\n";
|
||||
if (!cof.importLayers.empty()) {
|
||||
std::cout << " - Filter layers:";
|
||||
for (const auto& filter : cof.importLayers)
|
||||
std::cout << ' ' << filter;
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
if (cof.hasFrameTag()) {
|
||||
std::cout << " - Frame tag: '" << cof.frameTag << "'\n";
|
||||
|
|
|
|||
|
|
@ -778,8 +778,8 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
|
|||
|
||||
// If the user choose to render selected layers only, we can
|
||||
// temporaly make them visible and hide the other ones.
|
||||
Layer* layer = nullptr;
|
||||
RestoreVisibleLayers layersVisibility;
|
||||
SelectedLayers selLayers;
|
||||
if (layerName == kSelectedLayers) {
|
||||
// TODO the range of selected frames should be in doc::Site.
|
||||
auto range = App::instance()->timeline()->range();
|
||||
|
|
@ -790,10 +790,9 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
|
|||
}
|
||||
else {
|
||||
// TODO add a getLayerByName
|
||||
LayerList layers = sprite->allLayers();
|
||||
for (Layer* l : layers) {
|
||||
if (l->name() == layerName) {
|
||||
layer = l;
|
||||
for (Layer* layer : sprite->allLayers()) {
|
||||
if (layer->name() == layerName) {
|
||||
selLayers.insert(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -850,7 +849,8 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
|
|||
exporter.setListLayers(true);
|
||||
if (listFrameTags)
|
||||
exporter.setListFrameTags(true);
|
||||
exporter.addDocument(document, layer, frameTag,
|
||||
exporter.addDocument(document, frameTag,
|
||||
(!selLayers.empty() ? &selLayers: nullptr),
|
||||
(!selFrames.empty() ? &selFrames: nullptr));
|
||||
|
||||
base::UniquePtr<Document> newDocument(exporter.exportSheet());
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "app/document.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/filename_formatter.h"
|
||||
#include "app/restore_visible_layers.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/fstream_path.h"
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/selected_frames.h"
|
||||
#include "doc/selected_layers.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/packing_rects.h"
|
||||
#include "gfx/size.h"
|
||||
|
|
@ -108,28 +110,29 @@ private:
|
|||
typedef base::SharedPtr<SampleBounds> SampleBoundsPtr;
|
||||
|
||||
DocumentExporter::Item::Item(Document* doc,
|
||||
doc::Layer* layer,
|
||||
doc::FrameTag* frameTag,
|
||||
doc::SelectedLayers* selLayers,
|
||||
doc::SelectedFrames* selFrames)
|
||||
: doc(doc)
|
||||
, layer(layer)
|
||||
, frameTag(frameTag)
|
||||
, selFrames(selFrames ? new doc::SelectedFrames(*selFrames):
|
||||
nullptr)
|
||||
, selLayers(selLayers ? new doc::SelectedLayers(*selLayers): nullptr)
|
||||
, selFrames(selFrames ? new doc::SelectedFrames(*selFrames): nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DocumentExporter::Item::Item(Item&& other)
|
||||
: doc(other.doc)
|
||||
, layer(other.layer)
|
||||
, frameTag(other.frameTag)
|
||||
, selLayers(other.selLayers)
|
||||
, selFrames(other.selFrames)
|
||||
{
|
||||
other.selLayers = nullptr;
|
||||
other.selFrames = nullptr;
|
||||
}
|
||||
|
||||
DocumentExporter::Item::~Item()
|
||||
{
|
||||
delete selLayers;
|
||||
delete selFrames;
|
||||
}
|
||||
|
||||
|
|
@ -173,11 +176,11 @@ doc::SelectedFrames DocumentExporter::Item::getSelectedFrames() const
|
|||
|
||||
class DocumentExporter::Sample {
|
||||
public:
|
||||
Sample(Document* document, Sprite* sprite, Layer* layer,
|
||||
frame_t frame, const std::string& filename, int innerPadding) :
|
||||
Sample(Document* document, Sprite* sprite, SelectedLayers* selLayers,
|
||||
frame_t frame, const std::string& filename, int innerPadding) :
|
||||
m_document(document),
|
||||
m_sprite(sprite),
|
||||
m_layer(layer),
|
||||
m_selLayers(selLayers),
|
||||
m_frame(frame),
|
||||
m_filename(filename),
|
||||
m_innerPadding(innerPadding),
|
||||
|
|
@ -187,7 +190,11 @@ public:
|
|||
|
||||
Document* document() const { return m_document; }
|
||||
Sprite* sprite() const { return m_sprite; }
|
||||
Layer* layer() const { return m_layer; }
|
||||
Layer* layer() const {
|
||||
return (m_selLayers && m_selLayers->size() == 1 ? *m_selLayers->begin():
|
||||
nullptr);
|
||||
}
|
||||
SelectedLayers* selectedLayers() const { return m_selLayers; }
|
||||
frame_t frame() const { return m_frame; }
|
||||
std::string filename() const { return m_filename; }
|
||||
const gfx::Size& originalSize() const { return m_bounds->originalSize(); }
|
||||
|
|
@ -220,7 +227,7 @@ public:
|
|||
private:
|
||||
Document* m_document;
|
||||
Sprite* m_sprite;
|
||||
Layer* m_layer;
|
||||
SelectedLayers* m_selLayers;
|
||||
frame_t m_frame;
|
||||
std::string m_filename;
|
||||
int m_borderPadding;
|
||||
|
|
@ -466,7 +473,8 @@ void DocumentExporter::captureSamples(Samples& samples)
|
|||
for (auto& item : m_documents) {
|
||||
Document* doc = item.doc;
|
||||
Sprite* sprite = doc->sprite();
|
||||
Layer* layer = item.layer;
|
||||
Layer* layer = (item.selLayers && item.selLayers->size() == 1 ?
|
||||
*item.selLayers->begin(): nullptr);
|
||||
FrameTag* frameTag = item.frameTag;
|
||||
int frames = item.frames();
|
||||
|
||||
|
|
@ -494,16 +502,16 @@ void DocumentExporter::captureSamples(Samples& samples)
|
|||
|
||||
std::string filename = filename_formatter(format, fnInfo);
|
||||
|
||||
Sample sample(doc, sprite, layer, frame, filename, m_innerPadding);
|
||||
Sample sample(doc, sprite, item.selLayers, frame, filename, m_innerPadding);
|
||||
Cel* cel = nullptr;
|
||||
Cel* link = nullptr;
|
||||
bool done = false;
|
||||
|
||||
if (layer && layer->isImage())
|
||||
if (layer && layer->isImage()) {
|
||||
cel = layer->cel(frame);
|
||||
|
||||
if (cel)
|
||||
link = cel->link();
|
||||
if (cel)
|
||||
link = cel->link();
|
||||
}
|
||||
|
||||
// Re-use linked samples
|
||||
if (link) {
|
||||
|
|
@ -822,13 +830,23 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
|
|||
for (auto& item : m_documents) {
|
||||
Document* doc = item.doc;
|
||||
Sprite* sprite = doc->sprite();
|
||||
LayerList layers;
|
||||
|
||||
for (Layer* layer : sprite->allVisibleLayers()) {
|
||||
if (item.selLayers)
|
||||
layers = item.selLayers->toLayerList();
|
||||
else
|
||||
layers = sprite->allVisibleLayers();
|
||||
|
||||
for (Layer* layer : layers) {
|
||||
if (firstLayer)
|
||||
firstLayer = false;
|
||||
else
|
||||
os << ",";
|
||||
os << "\n { \"name\": \"" << escape_for_json(layer->name()) << "\"";
|
||||
|
||||
if (layer->parent() != layer->sprite()->root())
|
||||
os << ", \"group\": \"" << escape_for_json(layer->parent()->name()) << "\"";
|
||||
|
||||
if (LayerImage* layerImg = dynamic_cast<LayerImage*>(layer)) {
|
||||
os << ", \"opacity\": " << layerImg->opacity()
|
||||
<< ", \"blendMode\": \"" << blend_mode_to_string(layerImg->blendMode()) << "\"";
|
||||
|
|
@ -877,15 +895,15 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
|
|||
|
||||
void DocumentExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int y) const
|
||||
{
|
||||
render::Render render;
|
||||
gfx::Clip clip(x, y, sample.trimmedBounds());
|
||||
|
||||
if (sample.layer()) {
|
||||
render.renderLayer(dst, sample.layer(), sample.frame(), clip);
|
||||
}
|
||||
else {
|
||||
render.renderSprite(dst, sample.sprite(), sample.frame(), clip);
|
||||
}
|
||||
RestoreVisibleLayers layersVisibility;
|
||||
if (sample.selectedLayers())
|
||||
layersVisibility.showSelectedLayers(sample.sprite(),
|
||||
*sample.selectedLayers());
|
||||
|
||||
render::Render render;
|
||||
render.renderSprite(dst, sample.sprite(), sample.frame(), clip);
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
namespace doc {
|
||||
class FrameTag;
|
||||
class Image;
|
||||
class Layer;
|
||||
class SelectedLayers;
|
||||
class SelectedFrames;
|
||||
}
|
||||
|
||||
|
|
@ -72,10 +72,10 @@ namespace app {
|
|||
void setListLayers(bool value) { m_listLayers = value; }
|
||||
|
||||
void addDocument(Document* document,
|
||||
doc::Layer* layer,
|
||||
doc::FrameTag* tag,
|
||||
doc::SelectedLayers* selLayers,
|
||||
doc::SelectedFrames* selFrames) {
|
||||
m_documents.push_back(Item(document, layer, tag, selFrames));
|
||||
m_documents.push_back(Item(document, tag, selLayers, selFrames));
|
||||
}
|
||||
|
||||
Document* exportSheet();
|
||||
|
|
@ -99,13 +99,13 @@ namespace app {
|
|||
class Item {
|
||||
public:
|
||||
Document* doc;
|
||||
doc::Layer* layer;
|
||||
doc::FrameTag* frameTag;
|
||||
doc::SelectedLayers* selLayers;
|
||||
doc::SelectedFrames* selFrames;
|
||||
|
||||
Item(Document* doc,
|
||||
doc::Layer* layer,
|
||||
doc::FrameTag* frameTag,
|
||||
doc::SelectedLayers* selLayers,
|
||||
doc::SelectedFrames* selFrames);
|
||||
Item(Item&& other);
|
||||
~Item();
|
||||
|
|
|
|||
Loading…
Reference in New Issue