Add --slice and --split-slice CLI params (#721)

This commit is contained in:
David Capello 2017-04-11 18:45:51 -03:00
parent 5888db011c
commit 2c0d0d3682
13 changed files with 173 additions and 68 deletions

View File

@ -44,6 +44,7 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_sheetPack(m_po.add("sheet-pack").description("Same as --sheet-type packed"))
, m_splitLayers(m_po.add("split-layers").description("Save each visible layer of sprites\nas separated images in the sheet\n"))
, m_splitTags(m_po.add("split-tags").description("Save each tag as a separated file"))
, m_splitSlices(m_po.add("split-slices").description("Save each slice as a separated file"))
, m_layer(m_po.add("layer").alias("import-layer").requiresValue("<name>").description("Include just the given layer in the sheet\nor save as operation"))
, m_allLayers(m_po.add("all-layers").description("Make all layers visible\nBy default hidden layers will be ignored"))
, m_ignoreLayer(m_po.add("ignore-layer").requiresValue("<name>").description("Exclude the given layer in the sheet\nor save as operation"))
@ -55,6 +56,7 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_innerPadding(m_po.add("inner-padding").requiresValue("<value>").description("Add padding inside each frame"))
, m_trim(m_po.add("trim").description("Trim all images before exporting"))
, m_crop(m_po.add("crop").requiresValue("x,y,width,height").description("Crop all the images to the given rectangle"))
, m_slice(m_po.add("slice").requiresValue("<name>").description("Crop the sprite to the given slice area"))
, m_filenameFormat(m_po.add("filename-format").requiresValue("<fmt>").description("Special format to generate filenames"))
#ifdef ENABLE_SCRIPTING
, m_script(m_po.add("script").requiresValue("<filename>").description("Execute a specific script"))

View File

@ -58,6 +58,7 @@ public:
const Option& sheetPack() const { return m_sheetPack; }
const Option& splitLayers() const { return m_splitLayers; }
const Option& splitTags() const { return m_splitTags; }
const Option& splitSlices() const { return m_splitSlices; }
const Option& layer() const { return m_layer; }
const Option& allLayers() const { return m_allLayers; }
const Option& ignoreLayer() const { return m_ignoreLayer; }
@ -69,6 +70,7 @@ public:
const Option& innerPadding() const { return m_innerPadding; }
const Option& trim() const { return m_trim; }
const Option& crop() const { return m_crop; }
const Option& slice() const { return m_slice; }
const Option& filenameFormat() const { return m_filenameFormat; }
#ifdef ENABLE_SCRIPTING
const Option& script() const { return m_script; }
@ -108,6 +110,7 @@ private:
Option& m_sheetPack;
Option& m_splitLayers;
Option& m_splitTags;
Option& m_splitSlices;
Option& m_layer;
Option& m_allLayers;
Option& m_ignoreLayer;
@ -119,6 +122,7 @@ private:
Option& m_innerPadding;
Option& m_trim;
Option& m_crop;
Option& m_slice;
Option& m_filenameFormat;
#ifdef ENABLE_SCRIPTING
Option& m_script;

View File

@ -25,6 +25,7 @@ CliOpenFile::CliOpenFile()
toFrame = -1;
splitLayers = false;
splitTags = false;
splitSlices = false;
allLayers = false;
listLayers = false;
listTags = false;
@ -38,10 +39,16 @@ CliOpenFile::CliOpenFile()
FileOpROI CliOpenFile::roi() const
{
ASSERT(document);
SelectedFrames selFrames;
if (hasFrameRange())
selFrames.insert(fromFrame, toFrame);
return FileOpROI(document, frameTag, selFrames, true);
return FileOpROI(document,
slice,
frameTag,
selFrames,
true);
}
} // namespace app

View File

@ -24,11 +24,13 @@ namespace app {
std::string filename;
std::string filenameFormat;
std::string frameTag;
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;
@ -44,6 +46,10 @@ namespace app {
return (!frameTag.empty());
}
bool hasSlice() const {
return (!slice.empty());
}
bool hasFrameRange() const {
return (fromFrame >= 0 && toFrame >= 0);
}

View File

@ -31,6 +31,7 @@
#include "doc/layer.h"
#include "doc/selected_frames.h"
#include "doc/selected_layers.h"
#include "doc/slice.h"
namespace app {
@ -201,6 +202,10 @@ void CliProcessor::process()
else if (opt == &m_options.splitTags()) {
cof.splitTags = true;
}
// --split-slice
else if (opt == &m_options.splitSlices()) {
cof.splitSlices = true;
}
// --layer <layer-name>
else if (opt == &m_options.layer()) {
cof.includeLayers.push_back(value.value());
@ -270,6 +275,10 @@ void CliProcessor::process()
cof.crop.w = base::convert_to<int>(parts[2]);
cof.crop.h = base::convert_to<int>(parts[3]);
}
// --slice <slice>
else if (opt == &m_options.slice()) {
cof.slice = value.value();
}
// --filename-format
else if (opt == &m_options.filenameFormat()) {
cof.filenameFormat = value.value();
@ -281,15 +290,18 @@ void CliProcessor::process()
if (lastDoc) {
std::string fn = value.value();
// Automatic --split-layer or --split-tags in case the
// output filename already contains {layer} or {tag}
// template elements.
// Automatic --split-layer, --split-tags, --split-slices
// in case the output filename already contains {layer},
// {tag}, or {slice} template elements.
bool hasLayerTemplate = (is_layer_in_filename_format(fn) ||
is_group_in_filename_format(fn));
bool hasTagTemplate = is_tag_in_filename_format(fn);
if (hasLayerTemplate || hasTagTemplate) {
bool hasSliceTemplate = is_slice_in_filename_format(fn);
if (hasLayerTemplate || hasTagTemplate || hasSliceTemplate) {
cof.splitLayers = (cof.splitLayers || hasLayerTemplate);
cof.splitTags = (cof.splitTags || hasTagTemplate);
cof.splitSlices = (cof.splitSlices || hasSliceTemplate);
cof.filenameFormat =
get_default_filename_format(
fn,
@ -510,7 +522,6 @@ void CliProcessor::saveFile(const CliOpenFile& cof)
ctx->setActiveDocument(cof.document);
Command* trimCommand = CommandsModule::instance()->getCommandByName(CommandId::AutocropSprite);
Command* cropCommand = CommandsModule::instance()->getCommandByName(CommandId::CropSprite);
Command* undoCommand = CommandsModule::instance()->getCommandByName(CommandId::Undo);
app::Document* doc = cof.document;
bool clearUndo = false;
@ -521,7 +532,9 @@ void CliProcessor::saveFile(const CliOpenFile& cof)
cropParams.set("y", base::convert_to<std::string>(cof.crop.y).c_str());
cropParams.set("width", base::convert_to<std::string>(cof.crop.w).c_str());
cropParams.set("height", base::convert_to<std::string>(cof.crop.h).c_str());
ctx->executeCommand(cropCommand, cropParams);
ctx->executeCommand(
CommandsModule::instance()->getCommandByName(CommandId::CropSprite),
cropParams);
}
std::string fn = cof.filename;
@ -575,70 +588,91 @@ void CliProcessor::saveFile(const CliOpenFile& cof)
frameTags.push_back(nullptr);
}
std::vector<doc::Slice*> slices;
if (cof.hasSlice()) {
slices.push_back(
doc->sprite()->slices().getByName(cof.slice));
}
else {
doc::Slices& origSlices = cof.document->sprite()->slices();
if (cof.splitSlices && !origSlices.empty()) {
for (doc::Slice* slice : origSlices)
slices.push_back(slice);
}
else
slices.push_back(nullptr);
}
bool layerInFormat = is_layer_in_filename_format(fn);
bool groupInFormat = is_group_in_filename_format(fn);
for (doc::FrameTag* frameTag : frameTags) {
// For each layer, hide other ones and save the sprite.
for (doc::Layer* layer : layers) {
RestoreVisibleLayers layersVisibility;
for (doc::Slice* slice : slices) {
for (doc::FrameTag* frameTag : frameTags) {
// For each layer, hide other ones and save the sprite.
for (doc::Layer* layer : layers) {
RestoreVisibleLayers layersVisibility;
if (cof.splitLayers) {
ASSERT(layer);
if (cof.splitLayers) {
ASSERT(layer);
// If the user doesn't want all layers and this one is hidden.
if (!layer->isVisible())
continue; // Just ignore this layer.
// If the user doesn't want all layers and this one is hidden.
if (!layer->isVisible())
continue; // Just ignore this layer.
// Make this layer ("show") the only one visible.
layersVisibility.showLayer(layer);
}
else if (!filteredLayers.empty())
layersVisibility.showSelectedLayers(doc->sprite(), filteredLayers);
if (layer) {
if ((layerInFormat && layer->isGroup()) ||
(!layerInFormat && groupInFormat && !layer->isGroup())) {
continue;
// Make this layer ("show") the only one visible.
layersVisibility.showLayer(layer);
}
}
else if (!filteredLayers.empty())
layersVisibility.showSelectedLayers(doc->sprite(), filteredLayers);
// TODO --trim --save-as --split-layers doesn't make too much
// sense as we lost the trim rectangle information (e.g. we
// don't have sheet .json) Also, we should trim each frame
// individually (a process that can be done only in
// FileOp::operate()).
if (cof.trim)
ctx->executeCommand(trimCommand);
if (layer) {
if ((layerInFormat && layer->isGroup()) ||
(!layerInFormat && groupInFormat && !layer->isGroup())) {
continue;
}
}
CliOpenFile itemCof = cof;
FilenameInfo fnInfo;
fnInfo.filename(fn);
if (layer) {
fnInfo.layerName(layer->name());
// TODO --trim --save-as --split-layers doesn't make too much
// sense as we lost the trim rectangle information (e.g. we
// don't have sheet .json) Also, we should trim each frame
// individually (a process that can be done only in
// FileOp::operate()).
if (cof.trim)
ctx->executeCommand(trimCommand);
if (layer->isGroup())
fnInfo.groupName(layer->name());
else if (layer->parent() != layer->sprite()->root())
fnInfo.groupName(layer->parent()->name());
CliOpenFile itemCof = cof;
FilenameInfo fnInfo;
fnInfo.filename(fn);
if (layer) {
fnInfo.layerName(layer->name());
itemCof.includeLayers.push_back(layer->name());
}
if (frameTag) {
fnInfo
.innerTagName(frameTag->name())
.outerTagName(frameTag->name());
itemCof.frameTag = frameTag->name();
}
itemCof.filename = filename_formatter(filenameFormat, fnInfo);
itemCof.filenameFormat = filename_formatter(filenameFormat, fnInfo, false);
if (layer->isGroup())
fnInfo.groupName(layer->name());
else if (layer->parent() != layer->sprite()->root())
fnInfo.groupName(layer->parent()->name());
// Call delegate
m_delegate->saveFile(itemCof);
itemCof.includeLayers.push_back(layer->name());
}
if (frameTag) {
fnInfo
.innerTagName(frameTag->name())
.outerTagName(frameTag->name());
itemCof.frameTag = frameTag->name();
}
if (slice) {
fnInfo.sliceName(slice->name());
itemCof.slice = slice->name();
}
itemCof.filename = filename_formatter(filenameFormat, fnInfo);
itemCof.filenameFormat = filename_formatter(filenameFormat, fnInfo, false);
if (cof.trim) {
ctx->executeCommand(undoCommand);
clearUndo = true;
// Call delegate
m_delegate->saveFile(itemCof);
if (cof.trim) {
ctx->executeCommand(undoCommand);
clearUndo = true;
}
}
}
}

View File

@ -88,6 +88,9 @@ void DefaultCliDelegate::saveFile(const CliOpenFile& cof)
params.set("from-frame", base::convert_to<std::string>(cof.fromFrame).c_str());
params.set("to-frame", base::convert_to<std::string>(cof.toFrame).c_str());
}
if (cof.hasSlice()) {
params.set("slice", cof.slice.c_str());
}
ctx->executeCommand(saveAsCommand, params);
}

View File

@ -108,6 +108,10 @@ void PreviewCliDelegate::saveFile(const CliOpenFile& cof)
std::cout << " - Frame tag: '" << cof.frameTag << "'\n";
}
if (cof.hasSlice()) {
std::cout << " - Slice: '" << cof.slice << "'\n";
}
if (cof.hasFrameRange()) {
const auto& selFrames = cof.roi().selectedFrames();
if (!selFrames.empty()) {

View File

@ -150,6 +150,7 @@ void SaveFileBaseCommand::onLoadParams(const Params& params)
m_filename = params.get("filename");
m_filenameFormat = params.get("filename-format");
m_frameTag = params.get("frame-tag");
m_slice = params.get("slice");
if (params.has_param("from-frame") ||
params.has_param("to-frame")) {
@ -300,7 +301,7 @@ void SaveFileBaseCommand::saveDocumentInBackground(const Context* context,
base::UniquePtr<FileOp> fop(
FileOp::createSaveDocumentOperation(
context,
FileOpROI(document, m_frameTag,
FileOpROI(document, m_slice, m_frameTag,
m_selFrames, m_adjustFramesByFrameTag),
document->filename(),
m_filenameFormat));

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -39,6 +39,7 @@ namespace app {
std::string m_filenameFormat;
std::string m_selectedFilename;
std::string m_frameTag;
std::string m_slice;
doc::SelectedFrames m_selFrames;
bool m_adjustFramesByFrameTag;
};

View File

@ -45,7 +45,7 @@ using namespace base;
namespace {
void updateXmlPartFromSliceKey(const SliceKey* key, TiXmlElement* xmlPart)
void updateXmlPartFromSliceKey(const doc::SliceKey* key, TiXmlElement* xmlPart)
{
xmlPart->SetAttribute("x", key->bounds().x);
xmlPart->SetAttribute("y", key->bounds().y);
@ -145,8 +145,8 @@ int save_document(Context* context, doc::Document* document)
UniquePtr<FileOp> fop(
FileOp::createSaveDocumentOperation(
context,
FileOpROI(static_cast<app::Document*>(document), "",
SelectedFrames(), false),
FileOpROI(static_cast<app::Document*>(document),
"", "", SelectedFrames(), false),
document->filename(), ""));
if (!fop)
return -1;
@ -175,19 +175,25 @@ bool is_static_image_format(const std::string& filename)
FileOpROI::FileOpROI()
: m_document(nullptr)
, m_slice(nullptr)
, m_frameTag(nullptr)
{
}
FileOpROI::FileOpROI(const app::Document* doc,
const std::string& sliceName,
const std::string& frameTagName,
const doc::SelectedFrames& selFrames,
const bool adjustByFrameTag)
: m_document(doc)
, m_slice(nullptr)
, m_frameTag(nullptr)
, m_selFrames(selFrames)
{
if (doc) {
if (!sliceName.empty())
m_slice = doc->sprite()->slices().getByName(sliceName);
m_frameTag = doc->sprite()->frameTags().getByName(frameTagName);
if (m_frameTag) {
if (m_selFrames.empty())
@ -529,6 +535,7 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
FilenameInfo fnInfo;
fnInfo
.filename(fn)
.sliceName(fop->m_roi.slice() ? fop->m_roi.slice()->name(): "")
.innerTagName(innerTag ? innerTag->name(): "")
.outerTagName(outerTag ? outerTag->name(): "")
.frame(outputFrame)
@ -584,6 +591,8 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
//
// After this function you must to mark the FileOp as "done" calling
// FileOp::done() function.
//
// TODO refactor this code
void FileOp::operate(IFileOpProgress* progress)
{
ASSERT(!isDone());
@ -604,7 +613,7 @@ void FileOp::operate(IFileOpProgress* progress)
frame_t frame(0);
Image* old_image = nullptr;
// TODO set_palette for each frame???
// TODO setPalette for each frame???
auto add_image = [&]() {
m_seq.last_cel->data()->setImage(m_seq.image);
m_seq.layer->addCel(m_seq.last_cel);
@ -730,8 +739,8 @@ void FileOp::operate(IFileOpProgress* progress)
// Create a temporary bitmap
m_seq.image.reset(Image::create(sprite->pixelFormat(),
sprite->width(),
sprite->height()));
sprite->width(),
sprite->height()));
m_seq.progress_offset = 0.0f;
m_seq.progress_fraction = 1.0f / (double)sprite->totalFrames();
@ -741,7 +750,23 @@ void FileOp::operate(IFileOpProgress* progress)
frame_t outputFrame = 0;
for (frame_t frame : m_roi.selectedFrames()) {
// Draw the "frame" in "m_seq.image"
render.renderSprite(m_seq.image.get(), sprite, frame);
if (m_roi.slice()) {
const SliceKey* key = m_roi.slice()->getByFrame(frame);
if (!key || key->isEmpty())
continue; // Skip frame because there is no slice key
m_seq.image.reset(
Image::create(sprite->pixelFormat(),
key->bounds().w,
key->bounds().h));
render.renderSprite(
m_seq.image.get(), sprite, frame,
gfx::Clip(gfx::Point(0, 0), key->bounds()));
}
else {
render.renderSprite(m_seq.image.get(), sprite, frame);
}
// Setup the palette.
sprite->palette(frame)->copyColorsTo(m_seq.palette);

View File

@ -38,6 +38,7 @@ namespace doc {
class Layer;
class LayerImage;
class Palette;
class Slice;
class Sprite;
}
@ -65,11 +66,13 @@ namespace app {
public:
FileOpROI();
FileOpROI(const app::Document* doc,
const std::string& sliceName,
const std::string& frameTagName,
const doc::SelectedFrames& selFrames,
const bool adjustByFrameTag);
const app::Document* document() const { return m_document; }
doc::Slice* slice() const { return m_slice; }
doc::FrameTag* frameTag() const { return m_frameTag; }
doc::frame_t fromFrame() const { return m_selFrames.firstFrame(); }
doc::frame_t toFrame() const { return m_selFrames.lastFrame(); }
@ -81,6 +84,7 @@ namespace app {
private:
const app::Document* m_document;
doc::Slice* m_slice;
doc::FrameTag* m_frameTag;
doc::SelectedFrames m_selFrames;
};

View File

@ -88,6 +88,11 @@ bool is_group_in_filename_format(const std::string& format)
return (format.find("{group}") != std::string::npos);
}
bool is_slice_in_filename_format(const std::string& format)
{
return (format.find("{slice}") != std::string::npos);
}
std::string filename_formatter(
const std::string& format,
FilenameInfo& info,
@ -106,6 +111,7 @@ std::string filename_formatter(
base::replace_string(output, "{extension}", base::get_file_extension(filename));
base::replace_string(output, "{layer}", info.layerName());
base::replace_string(output, "{group}", info.groupName());
base::replace_string(output, "{slice}", info.sliceName());
if (replaceFrame) {
base::replace_string(output, "{tag}", info.innerTagName());

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -21,6 +21,7 @@ namespace app {
const std::string& groupName() const { return m_groupName; }
const std::string& innerTagName() const { return m_innerTagName; }
const std::string& outerTagName() const { return m_outerTagName; }
const std::string& sliceName() const { return m_sliceName; }
int frame() const { return m_frame; }
int tagFrame() const { return m_tagFrame; }
@ -49,6 +50,11 @@ namespace app {
return *this;
}
FilenameInfo& sliceName(const std::string& value) {
m_sliceName = value;
return *this;
}
FilenameInfo& frame(int value) {
m_frame = value;
return *this;
@ -65,6 +71,7 @@ namespace app {
std::string m_groupName;
std::string m_innerTagName;
std::string m_outerTagName;
std::string m_sliceName;
int m_frame;
int m_tagFrame;
};
@ -78,6 +85,7 @@ namespace app {
bool is_tag_in_filename_format(const std::string& format);
bool is_layer_in_filename_format(const std::string& format);
bool is_group_in_filename_format(const std::string& format);
bool is_slice_in_filename_format(const std::string& format);
// If "replaceFrame" is false, this function doesn't replace all the
// information that depends on the current frame ({frame},