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:
David Capello 2016-09-16 15:19:47 -03:00
parent f4b5340dfb
commit a1ce0c5c73
6 changed files with 140 additions and 60 deletions

View File

@ -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;

View File

@ -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

View File

@ -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";

View File

@ -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());

View File

@ -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

View File

@ -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();