mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-24 12:41:08 +00:00
Merge branch 'refactor-cli' into beta
This commit is contained in:
commit
f18b1dfdb7
@ -172,6 +172,7 @@ if(ENABLE_TESTS)
|
||||
find_tests(render render-lib)
|
||||
find_tests(css css-lib)
|
||||
find_tests(ui ui-lib)
|
||||
find_tests(app/cli app-lib)
|
||||
find_tests(app/file app-lib)
|
||||
find_tests(app app-lib)
|
||||
find_tests(. app-lib)
|
||||
|
@ -92,9 +92,13 @@ add_library(app-lib
|
||||
app.cpp
|
||||
app_brushes.cpp
|
||||
app_menus.cpp
|
||||
app_options.cpp
|
||||
app_render.cpp
|
||||
check_update.cpp
|
||||
cli/app_options.cpp
|
||||
cli/cli_open_file.cpp
|
||||
cli/cli_processor.cpp
|
||||
cli/default_cli_delegate.cpp
|
||||
cli/preview_cli_delegate.cpp
|
||||
cmd.cpp
|
||||
cmd/add_cel.cpp
|
||||
cmd/add_frame.cpp
|
||||
|
465
src/app/app.cpp
465
src/app/app.cpp
@ -11,21 +11,18 @@
|
||||
|
||||
#include "app/app.h"
|
||||
|
||||
#include "app/app_options.h"
|
||||
#include "app/check_update.h"
|
||||
#include "app/cli/app_options.h"
|
||||
#include "app/cli/cli_processor.h"
|
||||
#include "app/cli/default_cli_delegate.h"
|
||||
#include "app/cli/preview_cli_delegate.h"
|
||||
#include "app/color_utils.h"
|
||||
#include "app/commands/cmd_save_file.h"
|
||||
#include "app/commands/cmd_sprite_size.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/console.h"
|
||||
#include "app/crash/data_recovery.h"
|
||||
#include "app/document_exporter.h"
|
||||
#include "app/document_undo.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/file/file_formats_manager.h"
|
||||
#include "app/file_system.h"
|
||||
#include "app/filename_formatter.h"
|
||||
#include "app/gui_xml.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/log.h"
|
||||
@ -54,21 +51,12 @@
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/clipboard.h"
|
||||
#include "app/webserver.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/exception.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/split_string.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/document_observer.h"
|
||||
#include "doc/frame_tag.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layers_range.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/site.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/render.h"
|
||||
#include "script/engine_delegate.h"
|
||||
#include "she/display.h"
|
||||
#include "she/error.h"
|
||||
@ -134,13 +122,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
class StdoutEngineDelegate : public script::EngineDelegate {
|
||||
public:
|
||||
void onConsolePrint(const char* text) override {
|
||||
printf("%s\n", text);
|
||||
}
|
||||
};
|
||||
|
||||
App* App::m_instance = NULL;
|
||||
|
||||
App::App()
|
||||
@ -149,7 +130,6 @@ App::App()
|
||||
, m_legacy(NULL)
|
||||
, m_isGui(false)
|
||||
, m_isShell(false)
|
||||
, m_exporter(NULL)
|
||||
{
|
||||
ASSERT(m_instance == NULL);
|
||||
m_instance = this;
|
||||
@ -157,7 +137,7 @@ App::App()
|
||||
|
||||
void App::initialize(const AppOptions& options)
|
||||
{
|
||||
m_isGui = options.startUI();
|
||||
m_isGui = options.startUI() && !options.previewCLI();
|
||||
m_isShell = options.startShell();
|
||||
if (m_isGui)
|
||||
m_uiSystem.reset(new ui::UISystem);
|
||||
@ -182,16 +162,10 @@ void App::initialize(const AppOptions& options)
|
||||
m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0);
|
||||
m_brushes.reset(new AppBrushes);
|
||||
|
||||
if (options.hasExporterParams())
|
||||
m_exporter.reset(new DocumentExporter);
|
||||
|
||||
// Data recovery is enabled only in GUI mode
|
||||
if (isGui() && preferences().general.dataRecovery())
|
||||
m_modules->createDataRecovery();
|
||||
|
||||
// Register well-known image file types.
|
||||
FileFormatsManager::instance()->registerAllFormats();
|
||||
|
||||
if (isPortable())
|
||||
LOG("Running in portable mode\n");
|
||||
|
||||
@ -200,7 +174,6 @@ void App::initialize(const AppOptions& options)
|
||||
load_default_palette(options.paletteFileName());
|
||||
|
||||
// Initialize GUI interface
|
||||
UIContext* ctx = UIContext::instance();
|
||||
if (isGui()) {
|
||||
LOG("GUI mode\n");
|
||||
|
||||
@ -232,427 +205,15 @@ void App::initialize(const AppOptions& options)
|
||||
|
||||
// Procress options
|
||||
LOG("Processing options...\n");
|
||||
|
||||
bool ignoreEmpty = false;
|
||||
bool trim = false;
|
||||
Params cropParams;
|
||||
SpriteSheetType sheetType = SpriteSheetType::None;
|
||||
|
||||
// Open file specified in the command line
|
||||
if (!options.values().empty()) {
|
||||
Console console;
|
||||
bool splitLayers = false;
|
||||
bool splitLayersSaveAs = false;
|
||||
bool allLayers = false;
|
||||
bool listLayers = false;
|
||||
bool listTags = false;
|
||||
std::string importLayer;
|
||||
std::string importLayerSaveAs;
|
||||
std::string filenameFormat;
|
||||
std::string frameTagName;
|
||||
std::string frameRange;
|
||||
|
||||
for (const auto& value : options.values()) {
|
||||
const AppOptions::Option* opt = value.option();
|
||||
|
||||
// Special options/commands
|
||||
if (opt) {
|
||||
// --data <file.json>
|
||||
if (opt == &options.data()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setDataFilename(value.value());
|
||||
}
|
||||
// --format <format>
|
||||
else if (opt == &options.format()) {
|
||||
if (m_exporter) {
|
||||
DocumentExporter::DataFormat format = DocumentExporter::DefaultDataFormat;
|
||||
|
||||
if (value.value() == "json-hash")
|
||||
format = DocumentExporter::JsonHashDataFormat;
|
||||
else if (value.value() == "json-array")
|
||||
format = DocumentExporter::JsonArrayDataFormat;
|
||||
|
||||
m_exporter->setDataFormat(format);
|
||||
}
|
||||
}
|
||||
// --sheet <file.png>
|
||||
else if (opt == &options.sheet()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setTextureFilename(value.value());
|
||||
}
|
||||
// --sheet-width <width>
|
||||
else if (opt == &options.sheetWidth()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setTextureWidth(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --sheet-height <height>
|
||||
else if (opt == &options.sheetHeight()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setTextureHeight(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --sheet-pack
|
||||
else if (opt == &options.sheetType()) {
|
||||
if (value.value() == "horizontal")
|
||||
sheetType = SpriteSheetType::Horizontal;
|
||||
else if (value.value() == "vertical")
|
||||
sheetType = SpriteSheetType::Vertical;
|
||||
else if (value.value() == "rows")
|
||||
sheetType = SpriteSheetType::Rows;
|
||||
else if (value.value() == "columns")
|
||||
sheetType = SpriteSheetType::Columns;
|
||||
else if (value.value() == "packed")
|
||||
sheetType = SpriteSheetType::Packed;
|
||||
}
|
||||
// --sheet-pack
|
||||
else if (opt == &options.sheetPack()) {
|
||||
sheetType = SpriteSheetType::Packed;
|
||||
}
|
||||
// --split-layers
|
||||
else if (opt == &options.splitLayers()) {
|
||||
splitLayers = true;
|
||||
splitLayersSaveAs = true;
|
||||
}
|
||||
// --layer <layer-name>
|
||||
else if (opt == &options.layer()) {
|
||||
importLayer = value.value();
|
||||
importLayerSaveAs = value.value();
|
||||
}
|
||||
// --all-layers
|
||||
else if (opt == &options.allLayers()) {
|
||||
allLayers = true;
|
||||
}
|
||||
// --frame-tag <tag-name>
|
||||
else if (opt == &options.frameTag()) {
|
||||
frameTagName = value.value();
|
||||
}
|
||||
// --frame-range from,to
|
||||
else if (opt == &options.frameRange()) {
|
||||
frameRange = value.value();
|
||||
}
|
||||
// --ignore-empty
|
||||
else if (opt == &options.ignoreEmpty()) {
|
||||
ignoreEmpty = true;
|
||||
}
|
||||
// --border-padding
|
||||
else if (opt == &options.borderPadding()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setBorderPadding(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --shape-padding
|
||||
else if (opt == &options.shapePadding()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setShapePadding(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --inner-padding
|
||||
else if (opt == &options.innerPadding()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setInnerPadding(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --trim
|
||||
else if (opt == &options.trim()) {
|
||||
trim = true;
|
||||
}
|
||||
// --crop x,y,width,height
|
||||
else if (opt == &options.crop()) {
|
||||
std::vector<std::string> parts;
|
||||
base::split_string(value.value(), parts, ",");
|
||||
if (parts.size() < 4)
|
||||
throw std::runtime_error("--crop needs four parameters separated by comma (,)\n"
|
||||
"Usage: --crop x,y,width,height\n"
|
||||
"E.g. --crop 0,0,32,32");
|
||||
|
||||
cropParams.set("x", parts[0].c_str());
|
||||
cropParams.set("y", parts[1].c_str());
|
||||
cropParams.set("width", parts[2].c_str());
|
||||
cropParams.set("height", parts[3].c_str());
|
||||
}
|
||||
// --filename-format
|
||||
else if (opt == &options.filenameFormat()) {
|
||||
filenameFormat = value.value();
|
||||
}
|
||||
// --save-as <filename>
|
||||
else if (opt == &options.saveAs()) {
|
||||
Document* doc = NULL;
|
||||
if (!ctx->documents().empty())
|
||||
doc = dynamic_cast<Document*>(ctx->documents().lastAdded());
|
||||
|
||||
if (!doc) {
|
||||
console.printf("A document is needed before --save-as argument\n");
|
||||
}
|
||||
else {
|
||||
ctx->setActiveDocument(doc);
|
||||
|
||||
std::string format = filenameFormat;
|
||||
|
||||
Command* saveAsCommand = CommandsModule::instance()->getCommandByName(CommandId::SaveFileCopyAs);
|
||||
Command* trimCommand = CommandsModule::instance()->getCommandByName(CommandId::AutocropSprite);
|
||||
Command* cropCommand = CommandsModule::instance()->getCommandByName(CommandId::CropSprite);
|
||||
Command* undoCommand = CommandsModule::instance()->getCommandByName(CommandId::Undo);
|
||||
|
||||
// --save-as with --split-layers
|
||||
if (splitLayersSaveAs) {
|
||||
std::string fn, fmt;
|
||||
if (format.empty()) {
|
||||
if (doc->sprite()->totalFrames() > frame_t(1))
|
||||
format = "{path}/{title} ({layer}) {frame}.{extension}";
|
||||
{
|
||||
base::UniquePtr<CliDelegate> delegate;
|
||||
if (options.previewCLI())
|
||||
delegate.reset(new PreviewCliDelegate);
|
||||
else
|
||||
format = "{path}/{title} ({layer}).{extension}";
|
||||
}
|
||||
delegate.reset(new DefaultCliDelegate);
|
||||
|
||||
// Store in "visibility" the original "visible" state of every layer.
|
||||
std::vector<bool> visibility(doc->sprite()->countLayers());
|
||||
int i = 0;
|
||||
for (Layer* layer : doc->sprite()->layers())
|
||||
visibility[i++] = layer->isVisible();
|
||||
|
||||
// For each layer, hide other ones and save the sprite.
|
||||
i = 0;
|
||||
for (Layer* show : doc->sprite()->layers()) {
|
||||
// If the user doesn't want all layers and this one is hidden.
|
||||
if (!visibility[i++])
|
||||
continue; // Just ignore this layer.
|
||||
|
||||
// Make this layer ("show") the only one visible.
|
||||
for (Layer* hide : doc->sprite()->layers())
|
||||
hide->setVisible(hide == show);
|
||||
|
||||
FilenameInfo fnInfo;
|
||||
fnInfo
|
||||
.filename(value.value())
|
||||
.layerName(show->name());
|
||||
|
||||
fn = filename_formatter(format, fnInfo);
|
||||
fmt = filename_formatter(format, fnInfo, false);
|
||||
|
||||
if (!cropParams.empty())
|
||||
ctx->executeCommand(cropCommand, cropParams);
|
||||
|
||||
// TODO --trim command with --save-as 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 fop_operate()).
|
||||
if (trim)
|
||||
ctx->executeCommand(trimCommand);
|
||||
|
||||
Params params;
|
||||
params.set("filename", fn.c_str());
|
||||
params.set("filename-format", fmt.c_str());
|
||||
ctx->executeCommand(saveAsCommand, params);
|
||||
|
||||
if (trim) { // Undo trim command
|
||||
ctx->executeCommand(undoCommand);
|
||||
|
||||
// Just in case allow non-linear history is enabled
|
||||
// we clear redo information
|
||||
doc->undoHistory()->clearRedo();
|
||||
}
|
||||
}
|
||||
|
||||
// Restore layer visibility
|
||||
i = 0;
|
||||
for (Layer* layer : doc->sprite()->layers())
|
||||
layer->setVisible(visibility[i++]);
|
||||
}
|
||||
else {
|
||||
// Show only one layer
|
||||
if (!importLayerSaveAs.empty()) {
|
||||
for (Layer* layer : doc->sprite()->layers())
|
||||
layer->setVisible(layer->name() == importLayerSaveAs);
|
||||
}
|
||||
|
||||
if (!cropParams.empty())
|
||||
ctx->executeCommand(cropCommand, cropParams);
|
||||
|
||||
if (trim)
|
||||
ctx->executeCommand(trimCommand);
|
||||
|
||||
Params params;
|
||||
params.set("filename", value.value().c_str());
|
||||
params.set("filename-format", format.c_str());
|
||||
ctx->executeCommand(saveAsCommand, params);
|
||||
|
||||
if (trim) { // Undo trim command
|
||||
ctx->executeCommand(undoCommand);
|
||||
|
||||
// Just in case allow non-linear history is enabled
|
||||
// we clear redo information
|
||||
doc->undoHistory()->clearRedo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// --scale <factor>
|
||||
else if (opt == &options.scale()) {
|
||||
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
|
||||
double scale = strtod(value.value().c_str(), NULL);
|
||||
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
|
||||
|
||||
// Scale all sprites
|
||||
for (auto doc : ctx->documents()) {
|
||||
ctx->setActiveDocument(static_cast<app::Document*>(doc));
|
||||
ctx->executeCommand(command);
|
||||
}
|
||||
}
|
||||
// --shrink-to <width,height>
|
||||
else if (opt == &options.shrinkTo()) {
|
||||
std::vector<std::string> dimensions;
|
||||
base::split_string(value.value(), dimensions, ",");
|
||||
if (dimensions.size() < 2)
|
||||
throw std::runtime_error("--shrink-to needs two parameters separated by comma (,)\n"
|
||||
"Usage: --shrink-to width,height\n"
|
||||
"E.g. --shrink-to 128,64");
|
||||
|
||||
double maxWidth = base::convert_to<double>(dimensions[0]);
|
||||
double maxHeight = base::convert_to<double>(dimensions[1]);
|
||||
double scaleWidth, scaleHeight, scale;
|
||||
|
||||
// Shrink all sprites if needed
|
||||
for (auto doc : ctx->documents()) {
|
||||
ctx->setActiveDocument(static_cast<app::Document*>(doc));
|
||||
scaleWidth = (doc->width() > maxWidth ? maxWidth / doc->width() : 1.0);
|
||||
scaleHeight = (doc->height() > maxHeight ? maxHeight / doc->height() : 1.0);
|
||||
if (scaleWidth < 1.0 || scaleHeight < 1.0) {
|
||||
scale = MIN(scaleWidth, scaleHeight);
|
||||
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
|
||||
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
|
||||
ctx->executeCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
// --script <filename>
|
||||
else if (opt == &options.script()) {
|
||||
std::string script = value.value();
|
||||
|
||||
StdoutEngineDelegate delegate;
|
||||
AppScripting engine(&delegate);
|
||||
engine.evalFile(script);
|
||||
}
|
||||
// --list-layers
|
||||
else if (opt == &options.listLayers()) {
|
||||
listLayers = true;
|
||||
if (m_exporter)
|
||||
m_exporter->setListLayers(true);
|
||||
}
|
||||
// --list-tags
|
||||
else if (opt == &options.listTags()) {
|
||||
listTags = true;
|
||||
if (m_exporter)
|
||||
m_exporter->setListFrameTags(true);
|
||||
}
|
||||
}
|
||||
// File names aren't associated to any option
|
||||
else {
|
||||
const std::string& filename = base::normalize_path(value.value());
|
||||
|
||||
app::Document* oldDoc = ctx->activeDocument();
|
||||
|
||||
Command* openCommand = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
|
||||
Params params;
|
||||
params.set("filename", filename.c_str());
|
||||
ctx->executeCommand(openCommand, params);
|
||||
|
||||
app::Document* doc = ctx->activeDocument();
|
||||
|
||||
// If the active document is equal to the previous one, it
|
||||
// means that we couldn't open this specific document.
|
||||
if (doc == oldDoc)
|
||||
doc = nullptr;
|
||||
|
||||
// List layers and/or tags
|
||||
if (doc) {
|
||||
// Show all layers
|
||||
if (allLayers) {
|
||||
for (Layer* layer : doc->sprite()->layers())
|
||||
layer->setVisible(true);
|
||||
}
|
||||
|
||||
if (listLayers) {
|
||||
listLayers = false;
|
||||
for (Layer* layer : doc->sprite()->layers()) {
|
||||
if (layer->isVisible())
|
||||
std::cout << layer->name() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (listTags) {
|
||||
listTags = false;
|
||||
for (FrameTag* tag : doc->sprite()->frameTags())
|
||||
std::cout << tag->name() << "\n";
|
||||
}
|
||||
if (m_exporter) {
|
||||
FrameTag* frameTag = nullptr;
|
||||
if (!frameTagName.empty()) {
|
||||
frameTag = doc->sprite()->frameTags().getByName(frameTagName);
|
||||
}
|
||||
else if (!frameRange.empty()) {
|
||||
std::vector<std::string> splitRange;
|
||||
base::split_string(frameRange, splitRange, ",");
|
||||
if (splitRange.size() < 2)
|
||||
throw std::runtime_error("--frame-range needs two parameters separated by comma (,)\n"
|
||||
"Usage: --frame-range from,to\n"
|
||||
"E.g. --frame-range 0,99");
|
||||
|
||||
frameTag = new FrameTag(base::convert_to<frame_t>(splitRange[0]),
|
||||
base::convert_to<frame_t>(splitRange[1]));
|
||||
}
|
||||
|
||||
if (!importLayer.empty()) {
|
||||
Layer* foundLayer = NULL;
|
||||
for (Layer* layer : doc->sprite()->layers()) {
|
||||
if (layer->name() == importLayer) {
|
||||
foundLayer = layer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundLayer)
|
||||
m_exporter->addDocument(doc, foundLayer, frameTag);
|
||||
}
|
||||
else if (splitLayers) {
|
||||
for (auto layer : doc->sprite()->layers()) {
|
||||
if (layer->isVisible())
|
||||
m_exporter->addDocument(doc, layer, frameTag);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_exporter->addDocument(doc, nullptr, frameTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!importLayer.empty())
|
||||
importLayer.clear();
|
||||
|
||||
if (splitLayers)
|
||||
splitLayers = false;
|
||||
if (listLayers)
|
||||
listLayers = false;
|
||||
if (listTags)
|
||||
listTags = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_exporter && !filenameFormat.empty())
|
||||
m_exporter->setFilenameFormat(filenameFormat);
|
||||
}
|
||||
|
||||
// Export
|
||||
if (m_exporter) {
|
||||
LOG("Exporting sheet...\n");
|
||||
|
||||
if (sheetType != SpriteSheetType::None)
|
||||
m_exporter->setSpriteSheetType(sheetType);
|
||||
|
||||
if (ignoreEmpty)
|
||||
m_exporter->setIgnoreEmptyCels(true);
|
||||
|
||||
if (trim)
|
||||
m_exporter->setTrimCels(true);
|
||||
|
||||
base::UniquePtr<Document> spriteSheet(m_exporter->exportSheet());
|
||||
m_exporter.reset(NULL);
|
||||
|
||||
LOG("Export sprite sheet: Done\n");
|
||||
CliProcessor cli(delegate.get(), options);
|
||||
cli.process();
|
||||
}
|
||||
|
||||
she::instance()->finishLaunching();
|
||||
@ -689,7 +250,7 @@ void App::run()
|
||||
|
||||
// Start shell to execute scripts.
|
||||
if (m_isShell) {
|
||||
StdoutEngineDelegate delegate;
|
||||
script::StdoutEngineDelegate delegate;
|
||||
AppScripting engine(&delegate);
|
||||
engine.printLastResult();
|
||||
Shell shell;
|
||||
|
@ -30,7 +30,6 @@ namespace app {
|
||||
class AppOptions;
|
||||
class ContextBar;
|
||||
class Document;
|
||||
class DocumentExporter;
|
||||
class INotificationDelegate;
|
||||
class InputChain;
|
||||
class LegacyModules;
|
||||
@ -107,7 +106,6 @@ namespace app {
|
||||
bool m_isShell;
|
||||
base::UniquePtr<MainWindow> m_mainWindow;
|
||||
FileList m_files;
|
||||
base::UniquePtr<DocumentExporter> m_exporter;
|
||||
base::UniquePtr<AppBrushes> m_brushes;
|
||||
};
|
||||
|
||||
|
@ -9,11 +9,10 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app_options.h"
|
||||
#include "app/cli/app_options.h"
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
namespace app {
|
||||
@ -24,10 +23,14 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
: m_exeName(base::get_file_name(argv[0]))
|
||||
, m_startUI(true)
|
||||
, m_startShell(false)
|
||||
, m_previewCLI(false)
|
||||
, m_showHelp(false)
|
||||
, m_showVersion(false)
|
||||
, m_verboseLevel(kNoVerbose)
|
||||
, m_palette(m_po.add("palette").requiresValue("<filename>").description("Use a specific palette by default"))
|
||||
, m_shell(m_po.add("shell").description("Start an interactive console to execute scripts"))
|
||||
, m_batch(m_po.add("batch").mnemonic('b').description("Do not start the UI"))
|
||||
, m_preview(m_po.add("preview").mnemonic('p').description("Do not execute actions, just print what will be\ndone"))
|
||||
, m_saveAs(m_po.add("save-as").requiresValue("<filename>").description("Save the last given document with other format"))
|
||||
, m_scale(m_po.add("scale").requiresValue("<factor>").description("Resize all previous opened documents"))
|
||||
, m_shrinkTo(m_po.add("shrink-to").requiresValue("width,height").description("Shrink each sprite if it is\nlarger than width or height"))
|
||||
@ -38,7 +41,8 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
, m_sheetHeight(m_po.add("sheet-height").requiresValue("<pixels>").description("Sprite sheet height"))
|
||||
, m_sheetType(m_po.add("sheet-type").requiresValue("<type>").description("Algorithm to create the sprite sheet:\n horizontal\n vertical\n rows\n columns\n packed"))
|
||||
, m_sheetPack(m_po.add("sheet-pack").description("Same as --sheet-type packed"))
|
||||
, m_splitLayers(m_po.add("split-layers").description("Import each layer of the next given sprite as\na separated image in the sheet"))
|
||||
, 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_layer(m_po.add("layer").alias("import-layer").requiresValue("<name>").description("Include just the given layer in the sheet"))
|
||||
, m_allLayers(m_po.add("all-layers").description("Make all layers visible\nBy default hidden layers will be ignored"))
|
||||
, m_frameTag(m_po.add("frame-tag").requiresValue("<name>").description("Include tagged frames in the sheet"))
|
||||
@ -67,18 +71,16 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
m_verboseLevel = kVerbose;
|
||||
|
||||
m_paletteFileName = m_po.value_of(m_palette);
|
||||
|
||||
m_startShell = m_po.enabled(m_shell);
|
||||
m_previewCLI = m_po.enabled(m_preview);
|
||||
m_showHelp = m_po.enabled(m_help);
|
||||
m_showVersion = m_po.enabled(m_version);
|
||||
|
||||
if (m_po.enabled(m_help)) {
|
||||
showHelp();
|
||||
m_startUI = false;
|
||||
}
|
||||
else if (m_po.enabled(m_version)) {
|
||||
showVersion();
|
||||
m_startUI = false;
|
||||
}
|
||||
|
||||
if (m_po.enabled(m_shell) || m_po.enabled(m_batch)) {
|
||||
if (m_startShell ||
|
||||
m_showHelp ||
|
||||
m_showVersion ||
|
||||
m_po.enabled(m_batch)) {
|
||||
m_startUI = false;
|
||||
}
|
||||
}
|
||||
@ -96,21 +98,4 @@ bool AppOptions::hasExporterParams() const
|
||||
m_po.enabled(m_sheet);
|
||||
}
|
||||
|
||||
void AppOptions::showHelp()
|
||||
{
|
||||
std::cout
|
||||
<< PACKAGE << " v" << VERSION << " | A pixel art program\n" << COPYRIGHT
|
||||
<< "\n\nUsage:\n"
|
||||
<< " " << m_exeName << " [OPTIONS] [FILES]...\n\n"
|
||||
<< "Options:\n"
|
||||
<< m_po
|
||||
<< "\nFind more information in " << PACKAGE
|
||||
<< " web site: " << WEBSITE << "\n\n";
|
||||
}
|
||||
|
||||
void AppOptions::showVersion()
|
||||
{
|
||||
std::cout << PACKAGE << ' ' << VERSION << '\n';
|
||||
}
|
||||
|
||||
}
|
@ -5,8 +5,8 @@
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_APP_OPTIONS_H_INCLUDED
|
||||
#define APP_APP_OPTIONS_H_INCLUDED
|
||||
#ifndef APP_CLI_APP_OPTIONS_H_INCLUDED
|
||||
#define APP_CLI_APP_OPTIONS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
@ -31,8 +31,14 @@ public:
|
||||
|
||||
AppOptions(int argc, const char* argv[]);
|
||||
|
||||
const std::string& exeName() const { return m_exeName; }
|
||||
const base::ProgramOptions& programOptions() const { return m_po; }
|
||||
|
||||
bool startUI() const { return m_startUI; }
|
||||
bool startShell() const { return m_startShell; }
|
||||
bool previewCLI() const { return m_previewCLI; }
|
||||
bool showHelp() const { return m_showHelp; }
|
||||
bool showVersion() const { return m_showVersion; }
|
||||
VerboseLevel verboseLevel() const { return m_verboseLevel; }
|
||||
|
||||
const std::string& paletteFileName() const { return m_paletteFileName; }
|
||||
@ -53,6 +59,7 @@ public:
|
||||
const Option& sheetType() const { return m_sheetType; }
|
||||
const Option& sheetPack() const { return m_sheetPack; }
|
||||
const Option& splitLayers() const { return m_splitLayers; }
|
||||
const Option& splitTags() const { return m_splitTags; }
|
||||
const Option& layer() const { return m_layer; }
|
||||
const Option& allLayers() const { return m_allLayers; }
|
||||
const Option& frameTag() const { return m_frameTag; }
|
||||
@ -71,19 +78,20 @@ public:
|
||||
bool hasExporterParams() const;
|
||||
|
||||
private:
|
||||
void showHelp();
|
||||
void showVersion();
|
||||
|
||||
std::string m_exeName;
|
||||
base::ProgramOptions m_po;
|
||||
bool m_startUI;
|
||||
bool m_startShell;
|
||||
bool m_previewCLI;
|
||||
bool m_showHelp;
|
||||
bool m_showVersion;
|
||||
VerboseLevel m_verboseLevel;
|
||||
std::string m_paletteFileName;
|
||||
|
||||
Option& m_palette;
|
||||
Option& m_shell;
|
||||
Option& m_batch;
|
||||
Option& m_preview;
|
||||
Option& m_saveAs;
|
||||
Option& m_scale;
|
||||
Option& m_shrinkTo;
|
||||
@ -95,6 +103,7 @@ private:
|
||||
Option& m_sheetType;
|
||||
Option& m_sheetPack;
|
||||
Option& m_splitLayers;
|
||||
Option& m_splitTags;
|
||||
Option& m_layer;
|
||||
Option& m_allLayers;
|
||||
Option& m_frameTag;
|
37
src/app/cli/cli_delegate.h
Normal file
37
src/app/cli/cli_delegate.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_CLI_CLI_DELEGATE_H_INCLUDED
|
||||
#define APP_CLI_CLI_DELEGATE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
|
||||
class AppOptions;
|
||||
class DocumentExporter;
|
||||
struct CliOpenFile;
|
||||
|
||||
class CliDelegate {
|
||||
public:
|
||||
virtual ~CliDelegate() { }
|
||||
virtual void showHelp(const AppOptions& options) { }
|
||||
virtual void showVersion() { }
|
||||
virtual void uiMode() { }
|
||||
virtual void shellMode() { }
|
||||
virtual void batchMode() { }
|
||||
virtual void beforeOpenFile(const CliOpenFile& cof) { }
|
||||
virtual void afterOpenFile(const CliOpenFile& cof) { }
|
||||
virtual void saveFile(const CliOpenFile& cof) { }
|
||||
virtual void exportFiles(DocumentExporter& exporter) { }
|
||||
virtual void execScript(const std::string& filename) { }
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
43
src/app/cli/cli_open_file.cpp
Normal file
43
src/app/cli/cli_open_file.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cli/cli_open_file.h"
|
||||
|
||||
#include "app/document.h"
|
||||
#include "app/file/file.h"
|
||||
#include "doc/frame_tag.h"
|
||||
#include "doc/frame_tags.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
CliOpenFile::CliOpenFile()
|
||||
{
|
||||
document = nullptr;
|
||||
fromFrame = -1;
|
||||
toFrame = -1;
|
||||
splitLayers = false;
|
||||
splitTags = false;
|
||||
allLayers = false;
|
||||
listLayers = false;
|
||||
listTags = false;
|
||||
ignoreEmpty = false;
|
||||
trim = false;
|
||||
crop = gfx::Rect();
|
||||
}
|
||||
|
||||
FileOpROI CliOpenFile::roi() const
|
||||
{
|
||||
ASSERT(document);
|
||||
return FileOpROI(document, frameTag, fromFrame, toFrame);
|
||||
}
|
||||
|
||||
} // namespace app
|
53
src/app/cli/cli_open_file.h
Normal file
53
src/app/cli/cli_open_file.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_CLI_CLI_OPEN_FILE_H_INCLUDED
|
||||
#define APP_CLI_CLI_OPEN_FILE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/frame.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
|
||||
class Document;
|
||||
class FileOpROI;
|
||||
|
||||
struct CliOpenFile {
|
||||
app::Document* document;
|
||||
std::string filename;
|
||||
std::string filenameFormat;
|
||||
std::string frameTag;
|
||||
std::string importLayer;
|
||||
doc::frame_t fromFrame, toFrame;
|
||||
bool splitLayers;
|
||||
bool splitTags;
|
||||
bool allLayers;
|
||||
bool listLayers;
|
||||
bool listTags;
|
||||
bool ignoreEmpty;
|
||||
bool trim;
|
||||
gfx::Rect crop;
|
||||
|
||||
CliOpenFile();
|
||||
|
||||
bool hasFrameTag() const {
|
||||
return (!frameTag.empty());
|
||||
}
|
||||
|
||||
bool hasFrameRange() const {
|
||||
return (fromFrame >= 0 && toFrame >= 0);
|
||||
}
|
||||
|
||||
FileOpROI roi() const;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
540
src/app/cli/cli_processor.cpp
Normal file
540
src/app/cli/cli_processor.cpp
Normal file
@ -0,0 +1,540 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cli/cli_processor.h"
|
||||
|
||||
#include "app/cli/app_options.h"
|
||||
#include "app/cli/cli_delegate.h"
|
||||
#include "app/commands/cmd_sprite_size.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/console.h"
|
||||
#include "app/document.h"
|
||||
#include "app/document_exporter.h"
|
||||
#include "app/document_undo.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/filename_formatter.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/path.h"
|
||||
#include "base/split_string.h"
|
||||
#include "doc/frame_tag.h"
|
||||
#include "doc/frame_tags.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layers_range.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
CliProcessor::CliProcessor(CliDelegate* delegate,
|
||||
const AppOptions& options)
|
||||
: m_delegate(delegate)
|
||||
, m_options(options)
|
||||
, m_exporter(nullptr)
|
||||
{
|
||||
if (options.hasExporterParams())
|
||||
m_exporter.reset(new DocumentExporter);
|
||||
}
|
||||
|
||||
void CliProcessor::process()
|
||||
{
|
||||
// --help
|
||||
if (m_options.showHelp()) {
|
||||
m_delegate->showHelp(m_options);
|
||||
}
|
||||
// --version
|
||||
else if (m_options.showVersion()) {
|
||||
m_delegate->showVersion();
|
||||
}
|
||||
// Process other options and file names
|
||||
else if (!m_options.values().empty()) {
|
||||
Console console;
|
||||
UIContext* ctx = UIContext::instance();
|
||||
CliOpenFile cof;
|
||||
SpriteSheetType sheetType = SpriteSheetType::None;
|
||||
app::Document* lastDoc = nullptr;
|
||||
|
||||
for (const auto& value : m_options.values()) {
|
||||
const AppOptions::Option* opt = value.option();
|
||||
|
||||
// Special options/commands
|
||||
if (opt) {
|
||||
// --data <file.json>
|
||||
if (opt == &m_options.data()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setDataFilename(value.value());
|
||||
}
|
||||
// --format <format>
|
||||
else if (opt == &m_options.format()) {
|
||||
if (m_exporter) {
|
||||
DocumentExporter::DataFormat format = DocumentExporter::DefaultDataFormat;
|
||||
|
||||
if (value.value() == "json-hash")
|
||||
format = DocumentExporter::JsonHashDataFormat;
|
||||
else if (value.value() == "json-array")
|
||||
format = DocumentExporter::JsonArrayDataFormat;
|
||||
|
||||
m_exporter->setDataFormat(format);
|
||||
}
|
||||
}
|
||||
// --sheet <file.png>
|
||||
else if (opt == &m_options.sheet()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setTextureFilename(value.value());
|
||||
}
|
||||
// --sheet-width <width>
|
||||
else if (opt == &m_options.sheetWidth()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setTextureWidth(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --sheet-height <height>
|
||||
else if (opt == &m_options.sheetHeight()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setTextureHeight(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --sheet-pack
|
||||
else if (opt == &m_options.sheetType()) {
|
||||
if (value.value() == "horizontal")
|
||||
sheetType = SpriteSheetType::Horizontal;
|
||||
else if (value.value() == "vertical")
|
||||
sheetType = SpriteSheetType::Vertical;
|
||||
else if (value.value() == "rows")
|
||||
sheetType = SpriteSheetType::Rows;
|
||||
else if (value.value() == "columns")
|
||||
sheetType = SpriteSheetType::Columns;
|
||||
else if (value.value() == "packed")
|
||||
sheetType = SpriteSheetType::Packed;
|
||||
}
|
||||
// --sheet-pack
|
||||
else if (opt == &m_options.sheetPack()) {
|
||||
sheetType = SpriteSheetType::Packed;
|
||||
}
|
||||
// --split-layers
|
||||
else if (opt == &m_options.splitLayers()) {
|
||||
cof.splitLayers = true;
|
||||
}
|
||||
// --split-tags
|
||||
else if (opt == &m_options.splitTags()) {
|
||||
cof.splitTags = true;
|
||||
}
|
||||
// --layer <layer-name>
|
||||
else if (opt == &m_options.layer()) {
|
||||
cof.importLayer = value.value();
|
||||
}
|
||||
// --all-layers
|
||||
else if (opt == &m_options.allLayers()) {
|
||||
cof.allLayers = true;
|
||||
}
|
||||
// --frame-tag <tag-name>
|
||||
else if (opt == &m_options.frameTag()) {
|
||||
cof.frameTag = value.value();
|
||||
}
|
||||
// --frame-range from,to
|
||||
else if (opt == &m_options.frameRange()) {
|
||||
std::vector<std::string> splitRange;
|
||||
base::split_string(value.value(), splitRange, ",");
|
||||
if (splitRange.size() < 2)
|
||||
throw std::runtime_error("--frame-range needs two parameters separated by comma (,)\n"
|
||||
"Usage: --frame-range from,to\n"
|
||||
"E.g. --frame-range 0,99");
|
||||
|
||||
cof.fromFrame = base::convert_to<frame_t>(splitRange[0]);
|
||||
cof.toFrame = base::convert_to<frame_t>(splitRange[1]);
|
||||
}
|
||||
// --ignore-empty
|
||||
else if (opt == &m_options.ignoreEmpty()) {
|
||||
cof.ignoreEmpty = true;
|
||||
if (m_exporter)
|
||||
m_exporter->setIgnoreEmptyCels(true);
|
||||
}
|
||||
// --border-padding
|
||||
else if (opt == &m_options.borderPadding()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setBorderPadding(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --shape-padding
|
||||
else if (opt == &m_options.shapePadding()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setShapePadding(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --inner-padding
|
||||
else if (opt == &m_options.innerPadding()) {
|
||||
if (m_exporter)
|
||||
m_exporter->setInnerPadding(strtol(value.value().c_str(), NULL, 0));
|
||||
}
|
||||
// --trim
|
||||
else if (opt == &m_options.trim()) {
|
||||
cof.trim = true;
|
||||
if (m_exporter)
|
||||
m_exporter->setTrimCels(true);
|
||||
}
|
||||
// --crop x,y,width,height
|
||||
else if (opt == &m_options.crop()) {
|
||||
std::vector<std::string> parts;
|
||||
base::split_string(value.value(), parts, ",");
|
||||
if (parts.size() < 4)
|
||||
throw std::runtime_error("--crop needs four parameters separated by comma (,)\n"
|
||||
"Usage: --crop x,y,width,height\n"
|
||||
"E.g. --crop 0,0,32,32");
|
||||
|
||||
cof.crop.x = base::convert_to<int>(parts[0]);
|
||||
cof.crop.y = base::convert_to<int>(parts[1]);
|
||||
cof.crop.w = base::convert_to<int>(parts[2]);
|
||||
cof.crop.h = base::convert_to<int>(parts[3]);
|
||||
}
|
||||
// --filename-format
|
||||
else if (opt == &m_options.filenameFormat()) {
|
||||
cof.filenameFormat = value.value();
|
||||
if (m_exporter)
|
||||
m_exporter->setFilenameFormat(cof.filenameFormat);
|
||||
}
|
||||
// --save-as <filename>
|
||||
else if (opt == &m_options.saveAs()) {
|
||||
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.
|
||||
bool hasLayerTemplate = (fn.find("{layer}") != std::string::npos);
|
||||
bool hasTagTemplate = (fn.find("{tag}") != std::string::npos);
|
||||
if (hasLayerTemplate || hasTagTemplate) {
|
||||
cof.splitLayers = (cof.splitLayers || hasLayerTemplate);
|
||||
cof.splitTags = (cof.splitTags || hasTagTemplate);
|
||||
cof.filenameFormat =
|
||||
get_default_filename_format(
|
||||
fn,
|
||||
true, // With path
|
||||
(lastDoc->sprite()->totalFrames() > 1), // Has frames
|
||||
false, // Has layer
|
||||
false); // Has frame tag
|
||||
}
|
||||
|
||||
cof.document = lastDoc;
|
||||
cof.filename = fn;
|
||||
saveFile(cof);
|
||||
}
|
||||
else
|
||||
console.printf("A document is needed before --save-as argument\n");
|
||||
}
|
||||
// --scale <factor>
|
||||
else if (opt == &m_options.scale()) {
|
||||
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
|
||||
double scale = strtod(value.value().c_str(), NULL);
|
||||
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
|
||||
|
||||
// Scale all sprites
|
||||
for (auto doc : ctx->documents()) {
|
||||
ctx->setActiveDocument(static_cast<app::Document*>(doc));
|
||||
ctx->executeCommand(command);
|
||||
}
|
||||
}
|
||||
// --shrink-to <width,height>
|
||||
else if (opt == &m_options.shrinkTo()) {
|
||||
std::vector<std::string> dimensions;
|
||||
base::split_string(value.value(), dimensions, ",");
|
||||
if (dimensions.size() < 2)
|
||||
throw std::runtime_error("--shrink-to needs two parameters separated by comma (,)\n"
|
||||
"Usage: --shrink-to width,height\n"
|
||||
"E.g. --shrink-to 128,64");
|
||||
|
||||
double maxWidth = base::convert_to<double>(dimensions[0]);
|
||||
double maxHeight = base::convert_to<double>(dimensions[1]);
|
||||
double scaleWidth, scaleHeight, scale;
|
||||
|
||||
// Shrink all sprites if needed
|
||||
for (auto doc : ctx->documents()) {
|
||||
ctx->setActiveDocument(static_cast<app::Document*>(doc));
|
||||
scaleWidth = (doc->width() > maxWidth ? maxWidth / doc->width() : 1.0);
|
||||
scaleHeight = (doc->height() > maxHeight ? maxHeight / doc->height() : 1.0);
|
||||
if (scaleWidth < 1.0 || scaleHeight < 1.0) {
|
||||
scale = MIN(scaleWidth, scaleHeight);
|
||||
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
|
||||
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
|
||||
ctx->executeCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
// --script <filename>
|
||||
else if (opt == &m_options.script()) {
|
||||
std::string filename = value.value();
|
||||
m_delegate->execScript(filename);
|
||||
}
|
||||
// --list-layers
|
||||
else if (opt == &m_options.listLayers()) {
|
||||
cof.listLayers = true;
|
||||
if (m_exporter)
|
||||
m_exporter->setListLayers(true);
|
||||
}
|
||||
// --list-tags
|
||||
else if (opt == &m_options.listTags()) {
|
||||
cof.listTags = true;
|
||||
if (m_exporter)
|
||||
m_exporter->setListFrameTags(true);
|
||||
}
|
||||
}
|
||||
// File names aren't associated to any option
|
||||
else {
|
||||
cof.document = nullptr;
|
||||
cof.filename = base::normalize_path(value.value());
|
||||
if (openFile(cof))
|
||||
lastDoc = cof.document;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_exporter) {
|
||||
if (sheetType != SpriteSheetType::None)
|
||||
m_exporter->setSpriteSheetType(sheetType);
|
||||
|
||||
m_delegate->exportFiles(*m_exporter.get());
|
||||
m_exporter.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Running mode
|
||||
if (m_options.startUI()) {
|
||||
m_delegate->uiMode();
|
||||
}
|
||||
else if (m_options.startShell()) {
|
||||
m_delegate->shellMode();
|
||||
}
|
||||
else {
|
||||
m_delegate->batchMode();
|
||||
}
|
||||
}
|
||||
|
||||
bool CliProcessor::openFile(CliOpenFile& cof)
|
||||
{
|
||||
m_delegate->beforeOpenFile(cof);
|
||||
|
||||
Context* ctx = UIContext::instance();
|
||||
app::Document* oldDoc = ctx->activeDocument();
|
||||
Command* openCommand = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
|
||||
Params params;
|
||||
params.set("filename", cof.filename.c_str());
|
||||
ctx->executeCommand(openCommand, params);
|
||||
|
||||
app::Document* doc = ctx->activeDocument();
|
||||
// If the active document is equal to the previous one, it
|
||||
// means that we couldn't open this specific document.
|
||||
if (doc == oldDoc)
|
||||
doc = nullptr;
|
||||
|
||||
cof.document = doc;
|
||||
|
||||
if (doc) {
|
||||
// Show all layers
|
||||
if (cof.allLayers) {
|
||||
for (doc::Layer* layer : doc->sprite()->layers())
|
||||
layer->setVisible(true);
|
||||
}
|
||||
|
||||
// Add document to exporter
|
||||
if (m_exporter) {
|
||||
FrameTag* frameTag = nullptr;
|
||||
bool isTemporalTag = false;
|
||||
|
||||
if (cof.hasFrameTag()) {
|
||||
frameTag = doc->sprite()->frameTags().getByName(cof.frameTag);
|
||||
}
|
||||
if (cof.hasFrameRange()) {
|
||||
// --frame-range with --frame-tag
|
||||
if (frameTag) {
|
||||
frameTag = new FrameTag(
|
||||
frameTag->fromFrame()+MID(0, cof.fromFrame, frameTag->frames()-1),
|
||||
frameTag->fromFrame()+MID(0, cof.toFrame, frameTag->frames()-1));
|
||||
}
|
||||
// --frame-range without --frame-tag
|
||||
else {
|
||||
frameTag = new FrameTag(cof.fromFrame, cof.toFrame);
|
||||
}
|
||||
isTemporalTag = true;
|
||||
}
|
||||
|
||||
if (!cof.importLayer.empty()) {
|
||||
Layer* foundLayer = nullptr;
|
||||
for (Layer* layer : doc->sprite()->layers()) {
|
||||
if (layer->name() == cof.importLayer) {
|
||||
foundLayer = layer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundLayer)
|
||||
m_exporter->addDocument(doc, foundLayer, frameTag, isTemporalTag);
|
||||
}
|
||||
else if (cof.splitLayers) {
|
||||
for (auto layer : doc->sprite()->layers()) {
|
||||
if (layer->isVisible())
|
||||
m_exporter->addDocument(doc, layer, frameTag, isTemporalTag);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_exporter->addDocument(doc, nullptr, frameTag, isTemporalTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_delegate->afterOpenFile(cof);
|
||||
|
||||
return (doc ? true: false);
|
||||
}
|
||||
|
||||
void CliProcessor::saveFile(const CliOpenFile& cof)
|
||||
{
|
||||
UIContext* ctx = UIContext::instance();
|
||||
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;
|
||||
|
||||
if (!cof.crop.isEmpty()) {
|
||||
Params cropParams;
|
||||
cropParams.set("x", base::convert_to<std::string>(cof.crop.x).c_str());
|
||||
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);
|
||||
}
|
||||
|
||||
// Store in "visibility" the original "visible" state of every layer.
|
||||
std::vector<bool> visibility(doc->sprite()->countLayers());
|
||||
int i = 0;
|
||||
for (doc::Layer* layer : doc->sprite()->layers())
|
||||
visibility[i++] = layer->isVisible();
|
||||
|
||||
std::string fn = cof.filename;
|
||||
std::string filenameFormat = cof.filenameFormat;
|
||||
if (filenameFormat.empty()) { // Default format
|
||||
bool hasFrames = (cof.roi().frames() > 1);
|
||||
filenameFormat = get_default_filename_format(
|
||||
fn,
|
||||
true, // With path
|
||||
hasFrames, // Has frames
|
||||
cof.splitLayers, // Has layer
|
||||
cof.splitTags); // Has frame tag
|
||||
}
|
||||
|
||||
std::vector<doc::Layer*> layers;
|
||||
// --save-as with --split-layers or --split-tags
|
||||
if (cof.splitLayers) {
|
||||
for (doc::Layer* layer : doc->sprite()->layers())
|
||||
layers.push_back(layer);
|
||||
}
|
||||
else {
|
||||
// Show only one layer
|
||||
if (!cof.importLayer.empty()) {
|
||||
for (Layer* layer : doc->sprite()->layers()) {
|
||||
if (layer->name() == cof.importLayer) {
|
||||
layer->setVisible(true);
|
||||
layers.push_back(layer);
|
||||
}
|
||||
else
|
||||
layer->setVisible(false);
|
||||
}
|
||||
}
|
||||
// All visible layers
|
||||
else
|
||||
layers.push_back(nullptr);
|
||||
}
|
||||
|
||||
std::vector<doc::FrameTag*> frameTags;
|
||||
if (cof.hasFrameTag()) {
|
||||
frameTags.push_back(
|
||||
doc->sprite()->frameTags().getByName(cof.frameTag));
|
||||
}
|
||||
else {
|
||||
doc::FrameTags& origFrameTags = cof.document->sprite()->frameTags();
|
||||
if (cof.splitTags && !origFrameTags.empty()) {
|
||||
for (doc::FrameTag* frameTag : origFrameTags) {
|
||||
// In case the tag is outside the given --frame-range
|
||||
if (cof.hasFrameRange()) {
|
||||
if (frameTag->toFrame() < cof.fromFrame ||
|
||||
frameTag->fromFrame() > cof.toFrame)
|
||||
continue;
|
||||
}
|
||||
frameTags.push_back(frameTag);
|
||||
}
|
||||
}
|
||||
else
|
||||
frameTags.push_back(nullptr);
|
||||
}
|
||||
|
||||
for (doc::FrameTag* frameTag : frameTags) {
|
||||
// For each layer, hide other ones and save the sprite.
|
||||
i = 0;
|
||||
for (doc::Layer* layer : layers) {
|
||||
if (cof.splitLayers) {
|
||||
ASSERT(layer);
|
||||
|
||||
// If the user doesn't want all layers and this one is hidden.
|
||||
if (!visibility[i++])
|
||||
continue; // Just ignore this layer.
|
||||
|
||||
// Make this layer ("show") the only one visible.
|
||||
for (doc::Layer* hide : doc->sprite()->layers())
|
||||
hide->setVisible(hide == layer);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
CliOpenFile itemCof = cof;
|
||||
FilenameInfo fnInfo;
|
||||
fnInfo.filename(fn);
|
||||
if (layer) {
|
||||
fnInfo.layerName(layer->name());
|
||||
itemCof.importLayer = 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);
|
||||
|
||||
// Call delegate
|
||||
m_delegate->saveFile(itemCof);
|
||||
|
||||
if (cof.trim) {
|
||||
ctx->executeCommand(undoCommand);
|
||||
clearUndo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore layer visibility
|
||||
i = 0;
|
||||
for (Layer* layer : doc->sprite()->layers())
|
||||
layer->setVisible(visibility[i++]);
|
||||
|
||||
// Undo crop
|
||||
if (!cof.crop.isEmpty()) {
|
||||
ctx->executeCommand(undoCommand);
|
||||
clearUndo = true;
|
||||
}
|
||||
|
||||
if (clearUndo) {
|
||||
// Just in case allow non-linear history is enabled
|
||||
// we clear redo information
|
||||
doc->undoHistory()->clearRedo();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace app
|
41
src/app/cli/cli_processor.h
Normal file
41
src/app/cli/cli_processor.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_APP_CLI_PROCESSOR_H_INCLUDED
|
||||
#define APP_APP_CLI_PROCESSOR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cli/cli_delegate.h"
|
||||
#include "app/cli/cli_open_file.h"
|
||||
#include "base/unique_ptr.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
|
||||
class AppOptions;
|
||||
class DocumentExporter;
|
||||
|
||||
class CliProcessor {
|
||||
public:
|
||||
CliProcessor(CliDelegate* delegate,
|
||||
const AppOptions& options);
|
||||
void process();
|
||||
|
||||
private:
|
||||
bool openFile(CliOpenFile& cof);
|
||||
void saveFile(const CliOpenFile& cof);
|
||||
|
||||
CliDelegate* m_delegate;
|
||||
const AppOptions& m_options;
|
||||
base::UniquePtr<DocumentExporter> m_exporter;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
87
src/app/cli/cli_tests.cpp
Normal file
87
src/app/cli/cli_tests.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#include "tests/test.h"
|
||||
|
||||
#include "app/cli/app_options.h"
|
||||
#include "app/cli/cli_processor.h"
|
||||
#include "app/document_exporter.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
using namespace app;
|
||||
|
||||
class CliTestDelegate : public CliDelegate {
|
||||
public:
|
||||
CliTestDelegate() {
|
||||
m_helpWasShown = false;
|
||||
m_versionWasShown = false;
|
||||
m_uiMode = false;
|
||||
m_shellMode = false;
|
||||
m_batchMode = false;
|
||||
}
|
||||
|
||||
void showHelp(const AppOptions& options) override { m_helpWasShown = true; }
|
||||
void showVersion() override { m_versionWasShown = true; }
|
||||
void uiMode() override { m_uiMode = true; }
|
||||
void shellMode() override { m_shellMode = true; }
|
||||
void batchMode() override { m_batchMode = true; }
|
||||
void beforeOpenFile(const CliOpenFile& cof) override { }
|
||||
void afterOpenFile(const CliOpenFile& cof) override { }
|
||||
void saveFile(const CliOpenFile& cof) override { }
|
||||
void exportFiles(DocumentExporter& exporter) override { }
|
||||
void execScript(const std::string& filename) override { }
|
||||
|
||||
bool helpWasShown() const { return m_helpWasShown; }
|
||||
bool versionWasShown() const { return m_versionWasShown; }
|
||||
|
||||
private:
|
||||
bool m_helpWasShown;
|
||||
bool m_versionWasShown;
|
||||
bool m_uiMode;
|
||||
bool m_shellMode;
|
||||
bool m_batchMode;
|
||||
};
|
||||
|
||||
AppOptions args(std::initializer_list<const char*> l) {
|
||||
int argc = l.size()+1;
|
||||
const char** argv = new const char*[argc];
|
||||
argv[0] = "aseprite.exe";
|
||||
auto it = l.begin();
|
||||
for (int i=1; i<argc; ++i, ++it) {
|
||||
argv[i] = *it;
|
||||
TRACE("argv[%d] = %s\n", i, argv[i]);
|
||||
}
|
||||
AppOptions opts(argc, argv);
|
||||
delete[] argv;
|
||||
return opts;
|
||||
}
|
||||
|
||||
TEST(Cli, None)
|
||||
{
|
||||
CliTestDelegate d;
|
||||
CliProcessor p(&d, args({ }));
|
||||
p.process();
|
||||
EXPECT_TRUE(!d.helpWasShown());
|
||||
EXPECT_TRUE(!d.versionWasShown());
|
||||
}
|
||||
|
||||
TEST(Cli, Help)
|
||||
{
|
||||
CliTestDelegate d;
|
||||
CliProcessor p(&d, args({ "--help" }));
|
||||
p.process();
|
||||
EXPECT_TRUE(d.helpWasShown());
|
||||
}
|
||||
|
||||
TEST(Cli, Version)
|
||||
{
|
||||
CliTestDelegate d;
|
||||
CliProcessor p(&d, args({ "--version" }));
|
||||
p.process();
|
||||
EXPECT_TRUE(d.versionWasShown());
|
||||
}
|
105
src/app/cli/default_cli_delegate.cpp
Normal file
105
src/app/cli/default_cli_delegate.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cli/default_cli_delegate.h"
|
||||
|
||||
#include "app/cli/app_options.h"
|
||||
#include "app/cli/cli_open_file.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/document.h"
|
||||
#include "app/document_exporter.h"
|
||||
#include "app/script/app_scripting.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/frame_tag.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layers_range.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "script/engine_delegate.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace app {
|
||||
|
||||
void DefaultCliDelegate::showHelp(const AppOptions& options)
|
||||
{
|
||||
std::cout
|
||||
<< PACKAGE << " v" << VERSION << " | A pixel art program\n" << COPYRIGHT
|
||||
<< "\n\nUsage:\n"
|
||||
<< " " << options.exeName() << " [OPTIONS] [FILES]...\n\n"
|
||||
<< "Options:\n"
|
||||
<< options.programOptions()
|
||||
<< "\nFind more information in " << PACKAGE
|
||||
<< " web site: " << WEBSITE << "\n\n";
|
||||
}
|
||||
|
||||
void DefaultCliDelegate::showVersion()
|
||||
{
|
||||
std::cout << PACKAGE << ' ' << VERSION << '\n';
|
||||
}
|
||||
|
||||
void DefaultCliDelegate::afterOpenFile(const CliOpenFile& cof)
|
||||
{
|
||||
if (!cof.document) // Do nothing
|
||||
return;
|
||||
|
||||
if (cof.listLayers) {
|
||||
for (doc::Layer* layer : cof.document->sprite()->layers()) {
|
||||
if (layer->isVisible())
|
||||
std::cout << layer->name() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (cof.listTags) {
|
||||
for (doc::FrameTag* tag : cof.document->sprite()->frameTags())
|
||||
std::cout << tag->name() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultCliDelegate::saveFile(const CliOpenFile& cof)
|
||||
{
|
||||
Context* ctx = UIContext::instance();
|
||||
Command* saveAsCommand = CommandsModule::instance()->getCommandByName(CommandId::SaveFileCopyAs);
|
||||
Params params;
|
||||
params.set("filename", cof.filename.c_str());
|
||||
params.set("filename-format", cof.filenameFormat.c_str());
|
||||
|
||||
if (cof.hasFrameTag()) {
|
||||
params.set("frame-tag", cof.frameTag.c_str());
|
||||
}
|
||||
if (cof.hasFrameRange()) {
|
||||
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());
|
||||
}
|
||||
|
||||
ctx->executeCommand(saveAsCommand, params);
|
||||
}
|
||||
|
||||
void DefaultCliDelegate::exportFiles(DocumentExporter& exporter)
|
||||
{
|
||||
LOG("Exporting sheet...\n");
|
||||
|
||||
base::UniquePtr<app::Document> spriteSheet(exporter.exportSheet());
|
||||
|
||||
// Sprite sheet isn't used, we just delete it.
|
||||
|
||||
LOG("Export sprite sheet: Done\n");
|
||||
}
|
||||
|
||||
void DefaultCliDelegate::execScript(const std::string& filename)
|
||||
{
|
||||
script::StdoutEngineDelegate delegate;
|
||||
AppScripting engine(&delegate);
|
||||
engine.evalFile(filename);
|
||||
}
|
||||
|
||||
} // namespace app
|
28
src/app/cli/default_cli_delegate.h
Normal file
28
src/app/cli/default_cli_delegate.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_CLI_DEFAULT_CLI_DELEGATE_H_INCLUDED
|
||||
#define APP_CLI_DEFAULT_CLI_DELEGATE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cli/cli_delegate.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class DefaultCliDelegate : public CliDelegate {
|
||||
public:
|
||||
void showHelp(const AppOptions& programOptions) override;
|
||||
void showVersion() override;
|
||||
void afterOpenFile(const CliOpenFile& cof) override;
|
||||
void saveFile(const CliOpenFile& cof) override;
|
||||
void exportFiles(DocumentExporter& exporter) override;
|
||||
void execScript(const std::string& filename) override;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
181
src/app/cli/preview_cli_delegate.cpp
Normal file
181
src/app/cli/preview_cli_delegate.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cli/preview_cli_delegate.h"
|
||||
|
||||
#include "app/cli/cli_open_file.h"
|
||||
#include "app/document.h"
|
||||
#include "app/document_exporter.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace app {
|
||||
|
||||
void PreviewCliDelegate::showHelp(const AppOptions& options)
|
||||
{
|
||||
std::cout << "- Show " PACKAGE " CLI usage\n";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::showVersion()
|
||||
{
|
||||
std::cout << "- Show " PACKAGE " version\n";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::uiMode()
|
||||
{
|
||||
std::cout << "- Run UI mode\n";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::shellMode()
|
||||
{
|
||||
std::cout << "- Run shell mode\n";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::batchMode()
|
||||
{
|
||||
std::cout << "- Exit\n";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::beforeOpenFile(const CliOpenFile& cof)
|
||||
{
|
||||
std::cout << "- Open file '" << cof.filename << "'\n";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::afterOpenFile(const CliOpenFile& cof)
|
||||
{
|
||||
if (!cof.document) {
|
||||
std::cout << " - WARNING: File not found or error loading file\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (cof.listLayers)
|
||||
std::cout << " - List layers\n";
|
||||
|
||||
if (cof.listTags)
|
||||
std::cout << " - List tags\n";
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::saveFile(const CliOpenFile& cof)
|
||||
{
|
||||
ASSERT(cof.document);
|
||||
ASSERT(cof.document->sprite());
|
||||
|
||||
std::cout << "- Save file '" << cof.filename << "'\n"
|
||||
<< " - Sprite: '" << cof.document->filename() << "'\n";
|
||||
|
||||
if (!cof.crop.isEmpty()) {
|
||||
std::cout << " - Crop: "
|
||||
<< cof.crop.x << ","
|
||||
<< cof.crop.y << " "
|
||||
<< cof.crop.w << "x"
|
||||
<< cof.crop.h << "\n";
|
||||
}
|
||||
|
||||
if (cof.trim) {
|
||||
std::cout << " - Trim\n";
|
||||
}
|
||||
|
||||
std::cout << " - Size: "
|
||||
<< cof.document->sprite()->width() << "x"
|
||||
<< cof.document->sprite()->height() << "\n";
|
||||
|
||||
if (!cof.importLayer.empty())
|
||||
std::cout << " - Layer: '" << cof.importLayer << "'\n";
|
||||
|
||||
if (cof.hasFrameTag()) {
|
||||
std::cout << " - Frame tag: '" << cof.frameTag << "'\n";
|
||||
}
|
||||
|
||||
if (cof.hasFrameRange()) {
|
||||
auto roi = cof.roi();
|
||||
std::cout << " - Frame range from "
|
||||
<< roi.fromFrame() << " to "
|
||||
<< roi.toFrame() << "\n";
|
||||
}
|
||||
|
||||
if (!cof.filenameFormat.empty())
|
||||
std::cout << " - Filename format: '" << cof.filenameFormat << "'\n";
|
||||
|
||||
base::UniquePtr<FileOp> fop(
|
||||
FileOp::createSaveDocumentOperation(
|
||||
UIContext::instance(),
|
||||
cof.roi(),
|
||||
cof.filename.c_str(),
|
||||
cof.filenameFormat.c_str()));
|
||||
|
||||
if (fop) {
|
||||
std::vector<std::string> files;
|
||||
fop->getFilenameList(files);
|
||||
for (const auto& file : files) {
|
||||
if (base::is_file(file))
|
||||
std::cout << " - Overwrite file: '" << file << "'\n";
|
||||
else
|
||||
std::cout << " - Output file: '" << file << "'\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
std::cout << " - No output\n";
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::exportFiles(DocumentExporter& exporter)
|
||||
{
|
||||
std::string type = "None";
|
||||
switch (exporter.spriteSheetType()) {
|
||||
case SpriteSheetType::Horizontal: type = "Horizontal"; break;
|
||||
case SpriteSheetType::Vertical: type = "Vertical"; break;
|
||||
case SpriteSheetType::Rows: type = "Rows"; break;
|
||||
case SpriteSheetType::Columns: type = "Columns"; break;
|
||||
case SpriteSheetType::Packed: type = "Packed"; break;
|
||||
}
|
||||
|
||||
gfx::Size size = exporter.calculateSheetSize();
|
||||
std::cout << "- Export sprite sheet:\n"
|
||||
<< " - Type: " << type << "\n"
|
||||
<< " - Size: " << size.w << "x" << size.h << "\n";
|
||||
|
||||
if (!exporter.textureFilename().empty()) {
|
||||
std::cout << " - Save texture file: '"
|
||||
<< exporter.textureFilename() << "'\n";
|
||||
}
|
||||
|
||||
if (!exporter.dataFilename().empty()) {
|
||||
std::string format = "Unknown";
|
||||
switch (exporter.dataFormat()) {
|
||||
case DocumentExporter::JsonHashDataFormat: format = "JSON Hash"; break;
|
||||
case DocumentExporter::JsonArrayDataFormat: format = "JSON Array"; break;
|
||||
}
|
||||
std::cout << " - Save data file: '" << exporter.dataFilename() << "'\n"
|
||||
<< " - Data format: " << format << "\n";
|
||||
|
||||
if (!exporter.filenameFormat().empty()) {
|
||||
std::cout << " - Filename format for JSON items: '"
|
||||
<< exporter.filenameFormat() << "'\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::execScript(const std::string& filename)
|
||||
{
|
||||
std::cout << "- Run script: '" << filename << "'\n";
|
||||
}
|
||||
|
||||
} // namespace app
|
32
src/app/cli/preview_cli_delegate.h
Normal file
32
src/app/cli/preview_cli_delegate.h
Normal file
@ -0,0 +1,32 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_CLI_PREVIEW_CLI_DELEGATE_H_INCLUDED
|
||||
#define APP_CLI_PREVIEW_CLI_DELEGATE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cli/cli_delegate.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class PreviewCliDelegate : public CliDelegate {
|
||||
public:
|
||||
void showHelp(const AppOptions& programOptions) override;
|
||||
void showVersion() override;
|
||||
void uiMode() override;
|
||||
void shellMode() override;
|
||||
void batchMode() override;
|
||||
void beforeOpenFile(const CliOpenFile& cof) override;
|
||||
void afterOpenFile(const CliOpenFile& cof) override;
|
||||
void saveFile(const CliOpenFile& cof) override;
|
||||
void exportFiles(DocumentExporter& exporter) override;
|
||||
void execScript(const std::string& filename) override;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -94,43 +94,6 @@ private:
|
||||
FileOp* m_fop;
|
||||
};
|
||||
|
||||
static void save_document_in_background(const Context* context,
|
||||
const Document* document, bool mark_as_saved,
|
||||
const std::string& fn_format)
|
||||
{
|
||||
base::UniquePtr<FileOp> fop(
|
||||
FileOp::createSaveDocumentOperation(
|
||||
context, document,
|
||||
document->filename().c_str(), fn_format.c_str()));
|
||||
if (!fop)
|
||||
return;
|
||||
|
||||
SaveFileJob job(fop);
|
||||
job.showProgressWindow();
|
||||
|
||||
if (fop->hasError()) {
|
||||
Console console;
|
||||
console.printf(fop->error().c_str());
|
||||
|
||||
// We don't know if the file was saved correctly or not. So mark
|
||||
// it as it should be saved again.
|
||||
const_cast<Document*>(document)->impossibleToBackToSavedState();
|
||||
}
|
||||
// If the job was cancelled, mark the document as modified.
|
||||
else if (fop->isStop()) {
|
||||
const_cast<Document*>(document)->impossibleToBackToSavedState();
|
||||
}
|
||||
else if (context->isUIAvailable()) {
|
||||
App::instance()->recentFiles()->addRecentFile(document->filename().c_str());
|
||||
if (mark_as_saved)
|
||||
const_cast<Document*>(document)->markAsSaved();
|
||||
|
||||
StatusBar::instance()
|
||||
->setStatusText(2000, "File %s, saved.",
|
||||
document->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
SaveFileBaseCommand::SaveFileBaseCommand(const char* short_name, const char* friendly_name, CommandFlags flags)
|
||||
@ -142,6 +105,14 @@ void SaveFileBaseCommand::onLoadParams(const Params& params)
|
||||
{
|
||||
m_filename = params.get("filename");
|
||||
m_filenameFormat = params.get("filename-format");
|
||||
m_frameTag = params.get("frame-tag");
|
||||
|
||||
m_fromFrame = m_toFrame = -1;
|
||||
if (params.has_param("from-frame") ||
|
||||
params.has_param("to-frame")) {
|
||||
m_fromFrame = params.get_as<doc::frame_t>("from-frame");
|
||||
m_toFrame = params.get_as<doc::frame_t>("to-frame");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if there is a current sprite to save.
|
||||
@ -222,9 +193,7 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
|
||||
}
|
||||
|
||||
// Save the document
|
||||
save_document_in_background(
|
||||
context, const_cast<Document*>(document),
|
||||
markAsSaved, m_filenameFormat);
|
||||
saveDocumentInBackground(context, const_cast<Document*>(document), markAsSaved);
|
||||
|
||||
// Undo resize
|
||||
if (undoResize) {
|
||||
@ -246,6 +215,45 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
|
||||
return true;
|
||||
}
|
||||
|
||||
void SaveFileBaseCommand::saveDocumentInBackground(const Context* context,
|
||||
const app::Document* document,
|
||||
bool markAsSaved) const
|
||||
{
|
||||
base::UniquePtr<FileOp> fop(
|
||||
FileOp::createSaveDocumentOperation(
|
||||
context,
|
||||
FileOpROI(document, m_frameTag, m_fromFrame, m_toFrame),
|
||||
document->filename().c_str(),
|
||||
m_filenameFormat.c_str()));
|
||||
if (!fop)
|
||||
return;
|
||||
|
||||
SaveFileJob job(fop);
|
||||
job.showProgressWindow();
|
||||
|
||||
if (fop->hasError()) {
|
||||
Console console;
|
||||
console.printf(fop->error().c_str());
|
||||
|
||||
// We don't know if the file was saved correctly or not. So mark
|
||||
// it as it should be saved again.
|
||||
const_cast<Document*>(document)->impossibleToBackToSavedState();
|
||||
}
|
||||
// If the job was cancelled, mark the document as modified.
|
||||
else if (fop->isStop()) {
|
||||
const_cast<Document*>(document)->impossibleToBackToSavedState();
|
||||
}
|
||||
else if (context->isUIAvailable()) {
|
||||
App::instance()->recentFiles()->addRecentFile(document->filename().c_str());
|
||||
if (markAsSaved)
|
||||
const_cast<Document*>(document)->markAsSaved();
|
||||
|
||||
StatusBar::instance()
|
||||
->setStatusText(2000, "File %s, saved.",
|
||||
document->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SaveFileCommand : public SaveFileBaseCommand {
|
||||
@ -274,9 +282,7 @@ void SaveFileCommand::onExecute(Context* context)
|
||||
ContextWriter writer(context);
|
||||
Document* documentWriter = writer.document();
|
||||
|
||||
save_document_in_background(
|
||||
context, documentWriter, true,
|
||||
m_filenameFormat.c_str());
|
||||
saveDocumentInBackground(context, documentWriter, true);
|
||||
}
|
||||
// If the document isn't associated to a file, we must to show the
|
||||
// save-as dialog to the user to select for first time the file-name
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -10,10 +10,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/commands/command.h"
|
||||
#include "doc/frame.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
class Document;
|
||||
class FileSelectorDelegate;
|
||||
|
||||
class SaveFileBaseCommand : public Command {
|
||||
@ -30,10 +32,15 @@ namespace app {
|
||||
|
||||
bool saveAsDialog(Context* context, const char* dlgTitle,
|
||||
FileSelectorDelegate* delegate = nullptr);
|
||||
void saveDocumentInBackground(const Context* context,
|
||||
const app::Document* document,
|
||||
bool markAsSaved) const;
|
||||
|
||||
std::string m_filename;
|
||||
std::string m_filenameFormat;
|
||||
std::string m_selectedFilename;
|
||||
std::string m_frameTag;
|
||||
doc::frame_t m_fromFrame, m_toFrame;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -341,12 +341,9 @@ public:
|
||||
|
||||
DocumentExporter::DocumentExporter()
|
||||
: m_dataFormat(DefaultDataFormat)
|
||||
, m_textureFormat(DefaultTextureFormat)
|
||||
, m_textureWidth(0)
|
||||
, m_textureHeight(0)
|
||||
, m_sheetType(SpriteSheetType::None)
|
||||
, m_scale(1.0)
|
||||
, m_scaleMode(DefaultScaleMode)
|
||||
, m_ignoreEmptyCels(false)
|
||||
, m_borderPadding(0)
|
||||
, m_shapePadding(0)
|
||||
@ -357,6 +354,14 @@ DocumentExporter::DocumentExporter()
|
||||
{
|
||||
}
|
||||
|
||||
DocumentExporter::~DocumentExporter()
|
||||
{
|
||||
for (auto& item : m_documents) {
|
||||
if (item.temporalTag)
|
||||
delete item.frameTag;
|
||||
}
|
||||
}
|
||||
|
||||
Document* DocumentExporter::exportSheet()
|
||||
{
|
||||
// We output the metadata to std::cout if the user didn't specify a file.
|
||||
@ -384,22 +389,7 @@ Document* DocumentExporter::exportSheet()
|
||||
}
|
||||
|
||||
// 2) Layout those samples in a texture field.
|
||||
switch (m_sheetType) {
|
||||
case SpriteSheetType::Packed: {
|
||||
BestFitLayoutSamples layout;
|
||||
layout.layoutSamples(
|
||||
samples, m_borderPadding, m_shapePadding,
|
||||
m_textureWidth, m_textureHeight);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
SimpleLayoutSamples layout(m_sheetType);
|
||||
layout.layoutSamples(
|
||||
samples, m_borderPadding, m_shapePadding,
|
||||
m_textureWidth, m_textureHeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
layoutSamples(samples);
|
||||
|
||||
// 3) Create and render the texture.
|
||||
base::UniquePtr<Document> textureDocument(
|
||||
@ -426,7 +416,15 @@ Document* DocumentExporter::exportSheet()
|
||||
return textureDocument.release();
|
||||
}
|
||||
|
||||
void DocumentExporter::captureSamples(Samples& samples)
|
||||
gfx::Size DocumentExporter::calculateSheetSize()
|
||||
{
|
||||
Samples samples;
|
||||
captureSamples(samples);
|
||||
layoutSamples(samples);
|
||||
return calculateSheetSize(samples);
|
||||
}
|
||||
|
||||
void DocumentExporter::captureSamples(Samples& samples) const
|
||||
{
|
||||
for (auto& item : m_documents) {
|
||||
Document* doc = item.doc;
|
||||
@ -434,21 +432,14 @@ void DocumentExporter::captureSamples(Samples& samples)
|
||||
Layer* layer = item.layer;
|
||||
FrameTag* frameTag = item.frameTag;
|
||||
int frames = item.frames();
|
||||
bool hasFrames = (frames > 1);
|
||||
bool hasLayer = (layer != nullptr);
|
||||
bool hasFrameTag = (frameTag && !item.temporalTag);
|
||||
|
||||
std::string format = m_filenameFormat;
|
||||
if (format.empty()) {
|
||||
if (hasFrames || hasLayer | hasFrameTag) {
|
||||
format = "{title}";
|
||||
if (hasLayer ) format += " ({layer})";
|
||||
if (hasFrameTag) format += " #{tag}";
|
||||
if (hasFrames ) format += " {frame}";
|
||||
format += ".{extension}";
|
||||
}
|
||||
else
|
||||
format = "{name}";
|
||||
format = get_default_filename_format_for_sheet(
|
||||
doc->filename(),
|
||||
(frames > 1), // Has frames
|
||||
(layer != nullptr), // Has layer
|
||||
(frameTag && !item.temporalTag)); // Has frame tag
|
||||
}
|
||||
|
||||
frame_t frameFirst = item.fromFrame();
|
||||
@ -543,35 +534,32 @@ void DocumentExporter::captureSamples(Samples& samples)
|
||||
}
|
||||
}
|
||||
|
||||
Document* DocumentExporter::createEmptyTexture(const Samples& samples)
|
||||
void DocumentExporter::layoutSamples(Samples& samples)
|
||||
{
|
||||
switch (m_sheetType) {
|
||||
case SpriteSheetType::Packed: {
|
||||
BestFitLayoutSamples layout;
|
||||
layout.layoutSamples(
|
||||
samples, m_borderPadding, m_shapePadding,
|
||||
m_textureWidth, m_textureHeight);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
SimpleLayoutSamples layout(m_sheetType);
|
||||
layout.layoutSamples(
|
||||
samples, m_borderPadding, m_shapePadding,
|
||||
m_textureWidth, m_textureHeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Size DocumentExporter::calculateSheetSize(const Samples& samples) const
|
||||
{
|
||||
Palette* palette = NULL;
|
||||
PixelFormat pixelFormat = IMAGE_INDEXED;
|
||||
gfx::Rect fullTextureBounds(0, 0, m_textureWidth, m_textureHeight);
|
||||
int maxColors = 256;
|
||||
|
||||
for (Samples::const_iterator
|
||||
it = samples.begin(),
|
||||
end = samples.end(); it != end; ++it) {
|
||||
// We try to render an indexed image. But if we find a sprite with
|
||||
// two or more palettes, or two of the sprites have different
|
||||
// palettes, we've to use RGB format.
|
||||
if (pixelFormat == IMAGE_INDEXED) {
|
||||
if (it->sprite()->pixelFormat() != IMAGE_INDEXED) {
|
||||
pixelFormat = IMAGE_RGB;
|
||||
}
|
||||
else if (it->sprite()->getPalettes().size() > 1) {
|
||||
pixelFormat = IMAGE_RGB;
|
||||
}
|
||||
else if (palette != NULL
|
||||
&& palette->countDiff(it->sprite()->palette(frame_t(0)), NULL, NULL) > 0) {
|
||||
pixelFormat = IMAGE_RGB;
|
||||
}
|
||||
else
|
||||
palette = it->sprite()->palette(frame_t(0));
|
||||
}
|
||||
|
||||
gfx::Rect sampleBounds = it->inTextureBounds();
|
||||
for (const auto& sample : samples) {
|
||||
gfx::Rect sampleBounds = sample.inTextureBounds();
|
||||
|
||||
// If the user specified a fixed sprite sheet size, we add the
|
||||
// border padding in the sample size to do an union between
|
||||
@ -589,11 +577,41 @@ Document* DocumentExporter::createEmptyTexture(const Samples& samples)
|
||||
if (m_textureWidth == 0) fullTextureBounds.w += m_borderPadding;
|
||||
if (m_textureHeight == 0) fullTextureBounds.h += m_borderPadding;
|
||||
|
||||
return gfx::Size(fullTextureBounds.x+fullTextureBounds.w,
|
||||
fullTextureBounds.y+fullTextureBounds.h);
|
||||
}
|
||||
|
||||
Document* DocumentExporter::createEmptyTexture(const Samples& samples) const
|
||||
{
|
||||
PixelFormat pixelFormat = IMAGE_INDEXED;
|
||||
Palette* palette = nullptr;
|
||||
int maxColors = 256;
|
||||
|
||||
for (const auto& sample : samples) {
|
||||
// We try to render an indexed image. But if we find a sprite with
|
||||
// two or more palettes, or two of the sprites have different
|
||||
// palettes, we've to use RGB format.
|
||||
if (pixelFormat == IMAGE_INDEXED) {
|
||||
if (sample.sprite()->pixelFormat() != IMAGE_INDEXED) {
|
||||
pixelFormat = IMAGE_RGB;
|
||||
}
|
||||
else if (sample.sprite()->getPalettes().size() > 1) {
|
||||
pixelFormat = IMAGE_RGB;
|
||||
}
|
||||
else if (palette != NULL
|
||||
&& palette->countDiff(sample.sprite()->palette(frame_t(0)), NULL, NULL) > 0) {
|
||||
pixelFormat = IMAGE_RGB;
|
||||
}
|
||||
else
|
||||
palette = sample.sprite()->palette(frame_t(0));
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Size textureSize = calculateSheetSize(samples);
|
||||
|
||||
base::UniquePtr<Sprite> sprite(
|
||||
Sprite::createBasicSprite(
|
||||
pixelFormat,
|
||||
fullTextureBounds.x+fullTextureBounds.w,
|
||||
fullTextureBounds.y+fullTextureBounds.h, maxColors));
|
||||
pixelFormat, textureSize.w, textureSize.h, maxColors));
|
||||
|
||||
if (palette != NULL)
|
||||
sprite->setPalette(palette, false);
|
||||
@ -604,7 +622,7 @@ Document* DocumentExporter::createEmptyTexture(const Samples& samples)
|
||||
return document.release();
|
||||
}
|
||||
|
||||
void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage)
|
||||
void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage) const
|
||||
{
|
||||
textureImage->clear(0);
|
||||
|
||||
@ -627,7 +645,7 @@ void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os, Image* textureImage)
|
||||
void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os, Image* textureImage) const
|
||||
{
|
||||
std::string frames_begin;
|
||||
std::string frames_end;
|
||||
@ -703,7 +721,7 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
|
||||
<< " \"size\": { "
|
||||
<< "\"w\": " << textureImage->width() << ", "
|
||||
<< "\"h\": " << textureImage->height() << " },\n"
|
||||
<< " \"scale\": \"" << m_scale << "\"";
|
||||
<< " \"scale\": \"1\"";
|
||||
|
||||
// meta.frameTags
|
||||
if (m_listFrameTags) {
|
||||
@ -795,7 +813,7 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
|
||||
<< "}\n";
|
||||
}
|
||||
|
||||
void DocumentExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int y)
|
||||
void DocumentExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int y) const
|
||||
{
|
||||
render::Render render;
|
||||
gfx::Clip clip(x, y, sample.trimmedBounds());
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -35,26 +35,30 @@ namespace app {
|
||||
DefaultDataFormat = JsonHashDataFormat
|
||||
};
|
||||
|
||||
enum TextureFormat {
|
||||
JsonTextureFormat,
|
||||
DefaultTextureFormat = JsonTextureFormat
|
||||
};
|
||||
|
||||
enum ScaleMode {
|
||||
DefaultScaleMode
|
||||
};
|
||||
|
||||
DocumentExporter();
|
||||
~DocumentExporter();
|
||||
|
||||
DataFormat dataFormat() const { return m_dataFormat; }
|
||||
const std::string& dataFilename() { return m_dataFilename; }
|
||||
const std::string& textureFilename() { return m_textureFilename; }
|
||||
int textureWidth() const { return m_textureWidth; }
|
||||
int textureHeight() const { return m_textureHeight; }
|
||||
SpriteSheetType spriteSheetType() { return m_sheetType; }
|
||||
bool ignoreEmptyCels() { return m_ignoreEmptyCels; }
|
||||
int borderPadding() const { return m_borderPadding; }
|
||||
int shapePadding() const { return m_shapePadding; }
|
||||
int innerPadding() const { return m_innerPadding; }
|
||||
bool trimCels() const { return m_trimCels; }
|
||||
const std::string& filenameFormat() const { return m_filenameFormat; }
|
||||
bool listFrameTags() const { return m_listFrameTags; }
|
||||
bool listLayers() const { return m_listLayers; }
|
||||
|
||||
void setDataFormat(DataFormat format) { m_dataFormat = format; }
|
||||
void setDataFilename(const std::string& filename) { m_dataFilename = filename; }
|
||||
void setTextureFormat(TextureFormat format) { m_textureFormat = format; }
|
||||
void setTextureFilename(const std::string& filename) { m_textureFilename = filename; }
|
||||
void setTextureWidth(int width) { m_textureWidth = width; }
|
||||
void setTextureHeight(int height) { m_textureHeight = height; }
|
||||
void setSpriteSheetType(SpriteSheetType type) { m_sheetType = type; }
|
||||
void setScale(double scale) { m_scale = scale; }
|
||||
void setScaleMode(ScaleMode mode) { m_scaleMode = mode; }
|
||||
void setIgnoreEmptyCels(bool ignore) { m_ignoreEmptyCels = ignore; }
|
||||
void setBorderPadding(int padding) { m_borderPadding = padding; }
|
||||
void setShapePadding(int padding) { m_shapePadding = padding; }
|
||||
@ -72,6 +76,7 @@ namespace app {
|
||||
}
|
||||
|
||||
Document* exportSheet();
|
||||
gfx::Size calculateSheetSize();
|
||||
|
||||
private:
|
||||
class Sample;
|
||||
@ -80,11 +85,13 @@ namespace app {
|
||||
class SimpleLayoutSamples;
|
||||
class BestFitLayoutSamples;
|
||||
|
||||
void captureSamples(Samples& samples);
|
||||
Document* createEmptyTexture(const Samples& samples);
|
||||
void renderTexture(const Samples& samples, doc::Image* textureImage);
|
||||
void createDataFile(const Samples& samples, std::ostream& os, doc::Image* textureImage);
|
||||
void renderSample(const Sample& sample, doc::Image* dst, int x, int y);
|
||||
void captureSamples(Samples& samples) const;
|
||||
void layoutSamples(Samples& samples);
|
||||
gfx::Size calculateSheetSize(const Samples& samples) const;
|
||||
Document* createEmptyTexture(const Samples& samples) const;
|
||||
void renderTexture(const Samples& samples, doc::Image* textureImage) const;
|
||||
void createDataFile(const Samples& samples, std::ostream& os, doc::Image* textureImage) const;
|
||||
void renderSample(const Sample& sample, doc::Image* dst, int x, int y) const;
|
||||
|
||||
class Item {
|
||||
public:
|
||||
@ -109,13 +116,10 @@ namespace app {
|
||||
|
||||
DataFormat m_dataFormat;
|
||||
std::string m_dataFilename;
|
||||
TextureFormat m_textureFormat;
|
||||
std::string m_textureFilename;
|
||||
int m_textureWidth;
|
||||
int m_textureHeight;
|
||||
SpriteSheetType m_sheetType;
|
||||
double m_scale;
|
||||
ScaleMode m_scaleMode;
|
||||
bool m_ignoreEmptyCels;
|
||||
int m_borderPadding;
|
||||
int m_shapePadding;
|
||||
|
@ -88,7 +88,8 @@ struct ASE_Chunk {
|
||||
};
|
||||
|
||||
static bool ase_file_read_header(FILE* f, ASE_Header* header);
|
||||
static void ase_file_prepare_header(FILE* f, ASE_Header* header, const Sprite* sprite);
|
||||
static void ase_file_prepare_header(FILE* f, ASE_Header* header, const Sprite* sprite,
|
||||
const frame_t firstFrame, const frame_t totalFrames);
|
||||
static void ase_file_write_header(FILE* f, ASE_Header* header);
|
||||
static void ase_file_write_header_filesize(FILE* f, ASE_Header* header);
|
||||
|
||||
@ -97,7 +98,10 @@ static void ase_file_prepare_frame_header(FILE* f, ASE_FrameHeader* frame_header
|
||||
static void ase_file_write_frame_header(FILE* f, ASE_FrameHeader* frame_header);
|
||||
|
||||
static void ase_file_write_layers(FILE* f, ASE_FrameHeader* frame_header, const Layer* layer);
|
||||
static void ase_file_write_cels(FILE* f, ASE_FrameHeader* frame_header, const Sprite* sprite, const Layer* layer, frame_t frame);
|
||||
static void ase_file_write_cels(FILE* f, ASE_FrameHeader* frame_header,
|
||||
const Sprite* sprite, const Layer* layer,
|
||||
const frame_t frame,
|
||||
const frame_t firstFrame);
|
||||
|
||||
static void ase_file_read_padding(FILE* f, int bytes);
|
||||
static void ase_file_write_padding(FILE* f, int bytes);
|
||||
@ -115,13 +119,17 @@ static void ase_file_write_palette_chunk(FILE* f, ASE_FrameHeader* frame_header,
|
||||
static Layer* ase_file_read_layer_chunk(FILE* f, ASE_Header* header, Sprite* sprite, Layer** previous_layer, int* current_level);
|
||||
static void ase_file_write_layer_chunk(FILE* f, ASE_FrameHeader* frame_header, const Layer* layer);
|
||||
static Cel* ase_file_read_cel_chunk(FILE* f, Sprite* sprite, frame_t frame, PixelFormat pixelFormat, FileOp* fop, ASE_Header* header, size_t chunk_end);
|
||||
static void ase_file_write_cel_chunk(FILE* f, ASE_FrameHeader* frame_header, const Cel* cel, const LayerImage* layer, const Sprite* sprite);
|
||||
static void ase_file_write_cel_chunk(FILE* f, ASE_FrameHeader* frame_header,
|
||||
const Cel* cel, const LayerImage* layer,
|
||||
const Sprite* sprite,
|
||||
const frame_t firstFrame);
|
||||
static Mask* ase_file_read_mask_chunk(FILE* f);
|
||||
#if 0
|
||||
static void ase_file_write_mask_chunk(FILE* f, ASE_FrameHeader* frame_header, Mask* mask);
|
||||
#endif
|
||||
static void ase_file_read_frame_tags_chunk(FILE* f, FrameTags* frameTags);
|
||||
static void ase_file_write_frame_tags_chunk(FILE* f, ASE_FrameHeader* frame_header, const FrameTags* frameTags);
|
||||
static void ase_file_write_frame_tags_chunk(FILE* f, ASE_FrameHeader* frame_header, const FrameTags* frameTags,
|
||||
const frame_t fromFrame, const frame_t toFrame);
|
||||
static void ase_file_read_user_data_chunk(FILE* f, UserData* userData);
|
||||
static void ase_file_write_user_data_chunk(FILE* f, ASE_FrameHeader* frame_header, const UserData* userData);
|
||||
|
||||
@ -342,7 +350,9 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
|
||||
// Write the header
|
||||
ASE_Header header;
|
||||
ase_file_prepare_header(f, &header, sprite);
|
||||
ase_file_prepare_header(f, &header, sprite,
|
||||
fop->roi().fromFrame(),
|
||||
fop->roi().frames());
|
||||
ase_file_write_header(f, &header);
|
||||
|
||||
bool require_new_palette_chunk = false;
|
||||
@ -354,7 +364,8 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
}
|
||||
|
||||
// Write frames
|
||||
for (frame_t frame(0); frame<sprite->totalFrames(); ++frame) {
|
||||
for (frame_t frame=fop->roi().fromFrame();
|
||||
frame <= fop->roi().toFrame(); ++frame) {
|
||||
// Prepare the frame header
|
||||
ASE_FrameHeader frame_header;
|
||||
ase_file_prepare_frame_header(f, &frame_header);
|
||||
@ -365,7 +376,9 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
// is the first frame or did the palette change?
|
||||
Palette* pal = sprite->palette(frame);
|
||||
int palFrom = 0, palTo = pal->size()-1;
|
||||
if ((frame == 0 ||
|
||||
if (// First frame or..
|
||||
(frame == fop->roi().fromFrame() ||
|
||||
// This palette is different from the previous frame palette
|
||||
sprite->palette(frame-1)->countDiff(pal, &palFrom, &palTo) > 0)) {
|
||||
// Write new palette chunk
|
||||
if (require_new_palette_chunk) {
|
||||
@ -378,7 +391,7 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
}
|
||||
|
||||
// Write extra chunks in the first frame
|
||||
if (frame == 0) {
|
||||
if (frame == fop->roi().fromFrame()) {
|
||||
LayerIterator it = sprite->folder()->getLayerBegin();
|
||||
LayerIterator end = sprite->folder()->getLayerEnd();
|
||||
|
||||
@ -388,18 +401,22 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
|
||||
// Writer frame tags
|
||||
if (sprite->frameTags().size() > 0)
|
||||
ase_file_write_frame_tags_chunk(f, &frame_header, &sprite->frameTags());
|
||||
ase_file_write_frame_tags_chunk(f, &frame_header, &sprite->frameTags(),
|
||||
fop->roi().fromFrame(),
|
||||
fop->roi().toFrame());
|
||||
}
|
||||
|
||||
// Write cel chunks
|
||||
ase_file_write_cels(f, &frame_header, sprite, sprite->folder(), frame);
|
||||
ase_file_write_cels(f, &frame_header,
|
||||
sprite, sprite->folder(),
|
||||
frame, fop->roi().fromFrame());
|
||||
|
||||
// Write the frame header
|
||||
ase_file_write_frame_header(f, &frame_header);
|
||||
|
||||
// Progress
|
||||
if (sprite->totalFrames() > 1)
|
||||
fop->setProgress(float(frame+1) / float(sprite->totalFrames()));
|
||||
if (fop->roi().frames() > 1)
|
||||
fop->setProgress(float(frame+1) / float(fop->roi().frames()));
|
||||
|
||||
if (fop->isStop())
|
||||
break;
|
||||
@ -457,27 +474,28 @@ static bool ase_file_read_header(FILE* f, ASE_Header* header)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ase_file_prepare_header(FILE* f, ASE_Header* header, const Sprite* sprite)
|
||||
static void ase_file_prepare_header(FILE* f, ASE_Header* header, const Sprite* sprite,
|
||||
const frame_t firstFrame, const frame_t totalFrames)
|
||||
{
|
||||
header->pos = ftell(f);
|
||||
|
||||
header->size = 0;
|
||||
header->magic = ASE_FILE_MAGIC;
|
||||
header->frames = sprite->totalFrames();
|
||||
header->frames = totalFrames;
|
||||
header->width = sprite->width();
|
||||
header->height = sprite->height();
|
||||
header->depth = (sprite->pixelFormat() == IMAGE_RGB ? 32:
|
||||
sprite->pixelFormat() == IMAGE_GRAYSCALE ? 16:
|
||||
sprite->pixelFormat() == IMAGE_INDEXED ? 8: 0);
|
||||
header->flags = ASE_FILE_FLAG_LAYER_WITH_OPACITY;
|
||||
header->speed = sprite->frameDuration(frame_t(0));
|
||||
header->speed = sprite->frameDuration(firstFrame);
|
||||
header->next = 0;
|
||||
header->frit = 0;
|
||||
header->transparent_index = sprite->transparentColor();
|
||||
header->ignore[0] = 0;
|
||||
header->ignore[1] = 0;
|
||||
header->ignore[2] = 0;
|
||||
header->ncolors = sprite->palette(frame_t(0))->size();
|
||||
header->ncolors = sprite->palette(firstFrame)->size();
|
||||
header->pixel_width = sprite->pixelRatio().w;
|
||||
header->pixel_height = sprite->pixelRatio().h;
|
||||
}
|
||||
@ -571,7 +589,10 @@ static void ase_file_write_layers(FILE* f, ASE_FrameHeader* frame_header, const
|
||||
}
|
||||
}
|
||||
|
||||
static void ase_file_write_cels(FILE* f, ASE_FrameHeader* frame_header, const Sprite* sprite, const Layer* layer, frame_t frame)
|
||||
static void ase_file_write_cels(FILE* f, ASE_FrameHeader* frame_header,
|
||||
const Sprite* sprite, const Layer* layer,
|
||||
const frame_t frame,
|
||||
const frame_t firstFrame)
|
||||
{
|
||||
if (layer->isImage()) {
|
||||
const Cel* cel = layer->cel(frame);
|
||||
@ -579,7 +600,9 @@ static void ase_file_write_cels(FILE* f, ASE_FrameHeader* frame_header, const Sp
|
||||
/* fop->setError("New cel in frame %d, in layer %d\n", */
|
||||
/* frame, sprite_layer2index(sprite, layer)); */
|
||||
|
||||
ase_file_write_cel_chunk(f, frame_header, cel, static_cast<const LayerImage*>(layer), sprite);
|
||||
ase_file_write_cel_chunk(f, frame_header, cel,
|
||||
static_cast<const LayerImage*>(layer),
|
||||
sprite, firstFrame);
|
||||
|
||||
if (!cel->link() &&
|
||||
!cel->data()->userData().isEmpty()) {
|
||||
@ -594,7 +617,7 @@ static void ase_file_write_cels(FILE* f, ASE_FrameHeader* frame_header, const Sp
|
||||
end = static_cast<const LayerFolder*>(layer)->getLayerEnd();
|
||||
|
||||
for (; it != end; ++it)
|
||||
ase_file_write_cels(f, frame_header, sprite, *it, frame);
|
||||
ase_file_write_cels(f, frame_header, sprite, *it, frame, firstFrame);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1269,12 +1292,28 @@ static Cel* ase_file_read_cel_chunk(FILE* f, Sprite* sprite, frame_t frame,
|
||||
}
|
||||
|
||||
static void ase_file_write_cel_chunk(FILE* f, ASE_FrameHeader* frame_header,
|
||||
const Cel* cel, const LayerImage* layer, const Sprite* sprite)
|
||||
const Cel* cel, const LayerImage* layer,
|
||||
const Sprite* sprite,
|
||||
const frame_t firstFrame)
|
||||
{
|
||||
ChunkWriter chunk(f, frame_header, ASE_FILE_CHUNK_CEL);
|
||||
|
||||
int layer_index = sprite->layerToIndex(layer);
|
||||
const Cel* link = cel->link();
|
||||
|
||||
// In case the original link is outside the ROI, we've to find the
|
||||
// first linked cel that is inside the ROI.
|
||||
if (link && link->frame() < firstFrame) {
|
||||
link = nullptr;
|
||||
for (frame_t i=firstFrame; i<=cel->frame(); ++i) {
|
||||
link = layer->cel(i);
|
||||
if (link && link->image()->id() == cel->image()->id())
|
||||
break;
|
||||
}
|
||||
if (link == cel)
|
||||
link = nullptr;
|
||||
}
|
||||
|
||||
int cel_type = (link ? ASE_FILE_LINK_CEL: ASE_FILE_COMPRESSED_CEL);
|
||||
|
||||
fputw(layer_index, f);
|
||||
@ -1320,7 +1359,7 @@ static void ase_file_write_cel_chunk(FILE* f, ASE_FrameHeader* frame_header,
|
||||
|
||||
case ASE_FILE_LINK_CEL:
|
||||
// Linked cel to another frame
|
||||
fputw(link->frame(), f);
|
||||
fputw(link->frame()-firstFrame, f);
|
||||
break;
|
||||
|
||||
case ASE_FILE_COMPRESSED_CEL: {
|
||||
@ -1449,18 +1488,34 @@ static void ase_file_read_frame_tags_chunk(FILE* f, FrameTags* frameTags)
|
||||
}
|
||||
}
|
||||
|
||||
static void ase_file_write_frame_tags_chunk(FILE* f, ASE_FrameHeader* frame_header, const FrameTags* frameTags)
|
||||
static void ase_file_write_frame_tags_chunk(FILE* f, ASE_FrameHeader* frame_header, const FrameTags* frameTags,
|
||||
const frame_t fromFrame, const frame_t toFrame)
|
||||
{
|
||||
ChunkWriter chunk(f, frame_header, ASE_FILE_CHUNK_FRAME_TAGS);
|
||||
|
||||
fputw(frameTags->size(), f);
|
||||
int tags = 0;
|
||||
for (const FrameTag* tag : *frameTags) {
|
||||
// Skip tags that are outside the given ROI
|
||||
if (tag->fromFrame() > toFrame ||
|
||||
tag->toFrame() < fromFrame)
|
||||
continue;
|
||||
++tags;
|
||||
}
|
||||
|
||||
fputw(tags, f);
|
||||
fputl(0, f); // 8 reserved bytes
|
||||
fputl(0, f);
|
||||
|
||||
for (const FrameTag* tag : *frameTags) {
|
||||
fputw(tag->fromFrame(), f);
|
||||
fputw(tag->toFrame(), f);
|
||||
if (tag->fromFrame() > toFrame ||
|
||||
tag->toFrame() < fromFrame)
|
||||
continue;
|
||||
|
||||
frame_t from = MID(0, tag->fromFrame()-fromFrame, toFrame-fromFrame);
|
||||
frame_t to = MID(from, tag->toFrame()-fromFrame, toFrame-fromFrame);
|
||||
|
||||
fputw(from, f);
|
||||
fputw(to, f);
|
||||
fputc((int)tag->aniDir(), f);
|
||||
|
||||
fputl(0, f); // 8 reserved bytes
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -693,7 +693,7 @@ bool BmpFormat::onLoad(FileOp *fop)
|
||||
}
|
||||
|
||||
// Setup the file-data.
|
||||
if (!fop->sequenceGetFormatOptions()) {
|
||||
if (!fop->formatOptions()) {
|
||||
base::SharedPtr<BmpOptions> bmp_options(new BmpOptions());
|
||||
|
||||
bmp_options->format = format;
|
||||
@ -703,7 +703,7 @@ bool BmpFormat::onLoad(FileOp *fop)
|
||||
bmp_options->green_mask = gmask;
|
||||
bmp_options->blue_mask = bmask;
|
||||
|
||||
fop->sequenceSetFormatOptions(bmp_options);
|
||||
fop->setFormatOptions(bmp_options);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -103,7 +103,7 @@ int save_document(Context* context, doc::Document* document)
|
||||
UniquePtr<FileOp> fop(
|
||||
FileOp::createSaveDocumentOperation(
|
||||
context,
|
||||
static_cast<app::Document*>(document),
|
||||
FileOpROI(static_cast<app::Document*>(document), "", -1, -1),
|
||||
document->filename().c_str(), ""));
|
||||
if (!fop)
|
||||
return -1;
|
||||
@ -120,6 +120,66 @@ int save_document(Context* context, doc::Document* document)
|
||||
return (!fop->hasError() ? 0: -1);
|
||||
}
|
||||
|
||||
bool is_static_image_format(const std::string& filename)
|
||||
{
|
||||
std::string extension =
|
||||
base::string_to_lower(
|
||||
base::get_file_extension(filename));
|
||||
|
||||
// Get the format through the extension of the filename
|
||||
FileFormat* format = FileFormatsManager::instance()
|
||||
->getFileFormatByExtension(extension.c_str());
|
||||
|
||||
return (format && format->support(FILE_SUPPORT_SEQUENCES));
|
||||
}
|
||||
|
||||
FileOpROI::FileOpROI()
|
||||
: m_document(nullptr)
|
||||
, m_frameTag(nullptr)
|
||||
, m_fromFrame(-1)
|
||||
, m_toFrame(-1)
|
||||
{
|
||||
}
|
||||
|
||||
FileOpROI::FileOpROI(const app::Document* doc,
|
||||
const std::string& frameTagName,
|
||||
const doc::frame_t fromFrame,
|
||||
const doc::frame_t toFrame)
|
||||
: m_document(doc)
|
||||
, m_frameTag(nullptr)
|
||||
, m_fromFrame(fromFrame)
|
||||
, m_toFrame(toFrame)
|
||||
{
|
||||
if (doc) {
|
||||
if (fromFrame >= 0)
|
||||
m_fromFrame = MID(0, fromFrame, doc->sprite()->lastFrame());
|
||||
else
|
||||
m_fromFrame = 0;
|
||||
|
||||
if (toFrame >= 0)
|
||||
m_toFrame = MID(m_fromFrame, toFrame, doc->sprite()->lastFrame());
|
||||
else
|
||||
m_toFrame = doc->sprite()->lastFrame();
|
||||
|
||||
if (!frameTagName.empty()) {
|
||||
doc::FrameTag* tag = doc->sprite()->frameTags().getByName(frameTagName);
|
||||
if (tag) {
|
||||
m_frameTag = tag;
|
||||
|
||||
if (fromFrame >= 0)
|
||||
m_fromFrame = tag->fromFrame() + MID(0, fromFrame, tag->frames()-1);
|
||||
else
|
||||
m_fromFrame = tag->fromFrame();
|
||||
|
||||
if (toFrame >= 0)
|
||||
m_toFrame = tag->fromFrame() + MID(fromFrame, toFrame, tag->frames()-1);
|
||||
else
|
||||
m_toFrame = tag->toFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
FileOp* FileOp::createLoadDocumentOperation(Context* context, const char* filename, int flags)
|
||||
{
|
||||
@ -163,8 +223,8 @@ FileOp* FileOp::createLoadDocumentOperation(Context* context, const char* filena
|
||||
int c, width, start_from;
|
||||
char buf[512];
|
||||
|
||||
/* first of all, we must generate the list of files to load in the
|
||||
sequence... */
|
||||
// First of all, we must generate the list of files to load in the
|
||||
// sequence...
|
||||
|
||||
// Check is this could be a sequence
|
||||
start_from = split_filename(filename, left, right, width);
|
||||
@ -218,15 +278,16 @@ done:;
|
||||
|
||||
// static
|
||||
FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
const Document* document,
|
||||
const FileOpROI& roi,
|
||||
const char* filename,
|
||||
const char* fn_format_arg)
|
||||
const char* filenameFormatArg)
|
||||
{
|
||||
base::UniquePtr<FileOp> fop(
|
||||
new FileOp(FileOpSave, const_cast<Context*>(context)));
|
||||
|
||||
// Document to save
|
||||
fop->m_document = const_cast<Document*>(document);
|
||||
fop->m_document = const_cast<Document*>(roi.document());
|
||||
fop->m_roi = roi;
|
||||
|
||||
// Get the extension of the filename (in lower case)
|
||||
std::string extension = base::string_to_lower(base::get_file_extension(filename));
|
||||
@ -247,7 +308,7 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
std::string warnings;
|
||||
bool fatal = false;
|
||||
|
||||
/* check image type support */
|
||||
// Check image type support
|
||||
switch (fop->m_document->sprite()->pixelFormat()) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
@ -284,7 +345,7 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
}
|
||||
|
||||
// Frames support
|
||||
if (fop->m_document->sprite()->totalFrames() > 1) {
|
||||
if (fop->m_roi.frames() > 1) {
|
||||
if (!fop->m_format->support(FILE_SUPPORT_FRAMES) &&
|
||||
!fop->m_format->support(FILE_SUPPORT_SEQUENCES)) {
|
||||
warnings += "<<- Frames";
|
||||
@ -381,68 +442,33 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
fop->prepareForSequence();
|
||||
|
||||
std::string fn = filename;
|
||||
std::string fn_format = fn_format_arg;
|
||||
bool default_format = false;
|
||||
|
||||
std::string fn_format = filenameFormatArg;
|
||||
if (fn_format.empty()) {
|
||||
if (fop->m_document->sprite()->totalFrames() == 1)
|
||||
fn_format = "{fullname}";
|
||||
else {
|
||||
fn_format = "{path}/{title}{frame}.{extension}";
|
||||
default_format = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Save one frame
|
||||
if (fop->m_document->sprite()->totalFrames() == 1) {
|
||||
FilenameInfo fnInfo;
|
||||
fnInfo.filename(fn);
|
||||
|
||||
fn = filename_formatter(fn_format, fnInfo);
|
||||
fop->m_seq.filename_list.push_back(fn);
|
||||
}
|
||||
// Save multiple frames
|
||||
else {
|
||||
int width = 0;
|
||||
int start_from = 0;
|
||||
|
||||
if (default_format) {
|
||||
std::string left, right;
|
||||
start_from = split_filename(fn.c_str(), left, right, width);
|
||||
if (start_from < 0) {
|
||||
start_from = 1;
|
||||
width = 1;
|
||||
}
|
||||
else {
|
||||
fn = left;
|
||||
fn += right;
|
||||
}
|
||||
fn_format = get_default_filename_format(
|
||||
fn,
|
||||
true, // With path
|
||||
(fop->m_roi.frames() > 1), // Has frames
|
||||
false, // Doesn't have layers
|
||||
false); // Doesn't have tags
|
||||
}
|
||||
|
||||
Sprite* spr = fop->m_document->sprite();
|
||||
std::vector<char> buf(32);
|
||||
std::sprintf(&buf[0], "{frame%0*d}", width, 0);
|
||||
if (default_format)
|
||||
fn_format = set_frame_format(fn_format, &buf[0]);
|
||||
else if (spr->totalFrames() > 1)
|
||||
fn_format = add_frame_format(fn_format, &buf[0]);
|
||||
|
||||
for (frame_t frame(0); frame<spr->totalFrames(); ++frame) {
|
||||
FrameTag* innerTag = spr->frameTags().innerTag(frame);
|
||||
FrameTag* outerTag = spr->frameTags().outerTag(frame);
|
||||
for (frame_t frame = fop->m_roi.fromFrame();
|
||||
frame <= fop->m_roi.toFrame(); ++frame) {
|
||||
FrameTag* innerTag = (fop->m_roi.frameTag() ? fop->m_roi.frameTag(): spr->frameTags().innerTag(frame));
|
||||
FrameTag* outerTag = (fop->m_roi.frameTag() ? fop->m_roi.frameTag(): spr->frameTags().outerTag(frame));
|
||||
FilenameInfo fnInfo;
|
||||
fnInfo
|
||||
.filename(fn)
|
||||
.innerTagName(innerTag ? innerTag->name(): "")
|
||||
.outerTagName(outerTag ? outerTag->name(): "")
|
||||
.frame(start_from+frame)
|
||||
.frame(frame - fop->m_roi.fromFrame())
|
||||
.tagFrame(innerTag ? frame - innerTag->fromFrame():
|
||||
start_from+frame);
|
||||
frame);
|
||||
|
||||
std::string frame_fn =
|
||||
filename_formatter(fn_format, fnInfo);
|
||||
|
||||
fop->m_seq.filename_list.push_back(frame_fn);
|
||||
fop->m_seq.filename_list.push_back(
|
||||
filename_formatter(fn_format, fnInfo));
|
||||
}
|
||||
|
||||
if (context && context->isUIAvailable() &&
|
||||
@ -457,21 +483,20 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
fop->m_filename = filename;
|
||||
|
||||
// Configure output format?
|
||||
if (fop->m_format->support(FILE_SUPPORT_GET_FORMAT_OPTIONS)) {
|
||||
base::SharedPtr<FormatOptions> format_options =
|
||||
base::SharedPtr<FormatOptions> opts =
|
||||
fop->m_format->getFormatOptions(fop);
|
||||
|
||||
// Does the user cancelled the operation?
|
||||
if (!format_options)
|
||||
if (!opts)
|
||||
return nullptr;
|
||||
|
||||
fop->m_seq.format_options = format_options;
|
||||
fop->m_document->setFormatOptions(format_options);
|
||||
fop->m_formatOptions = opts;
|
||||
fop->m_document->setFormatOptions(opts);
|
||||
}
|
||||
|
||||
return fop.release();
|
||||
@ -598,7 +623,7 @@ void FileOp::operate(IFileOpProgress* progress)
|
||||
|
||||
// Sets special options from the specific format (e.g. BMP
|
||||
// file can contain the number of bits per pixel).
|
||||
m_document->setFormatOptions(m_seq.format_options);
|
||||
m_document->setFormatOptions(m_formatOptions);
|
||||
}
|
||||
}
|
||||
// Direct load from one file.
|
||||
@ -630,7 +655,8 @@ void FileOp::operate(IFileOpProgress* progress)
|
||||
|
||||
// For each frame in the sprite.
|
||||
render::Render render;
|
||||
for (frame_t frame(0); frame < sprite->totalFrames(); ++frame) {
|
||||
for (frame_t frame = m_roi.fromFrame();
|
||||
frame <= m_roi.toFrame(); ++frame) {
|
||||
// Draw the "frame" in "m_seq.image"
|
||||
render.renderSprite(m_seq.image.get(), sprite, frame);
|
||||
|
||||
@ -638,7 +664,7 @@ void FileOp::operate(IFileOpProgress* progress)
|
||||
sprite->palette(frame)->copyColorsTo(m_seq.palette);
|
||||
|
||||
// Setup the filename to be used.
|
||||
m_filename = m_seq.filename_list[frame];
|
||||
m_filename = m_seq.filename_list[frame - m_roi.fromFrame()];
|
||||
|
||||
// Call the "save" procedure... did it fail?
|
||||
if (!m_format->save(this)) {
|
||||
@ -745,15 +771,15 @@ void FileOp::postLoad()
|
||||
m_document->markAsSaved();
|
||||
}
|
||||
|
||||
base::SharedPtr<FormatOptions> FileOp::sequenceGetFormatOptions() const
|
||||
base::SharedPtr<FormatOptions> FileOp::formatOptions() const
|
||||
{
|
||||
return m_seq.format_options;
|
||||
return m_formatOptions;
|
||||
}
|
||||
|
||||
void FileOp::sequenceSetFormatOptions(const base::SharedPtr<FormatOptions>& format_options)
|
||||
void FileOp::setFormatOptions(const base::SharedPtr<FormatOptions>& opts)
|
||||
{
|
||||
ASSERT(!m_seq.format_options);
|
||||
m_seq.format_options = format_options;
|
||||
ASSERT(!m_formatOptions);
|
||||
m_formatOptions = opts;
|
||||
}
|
||||
|
||||
void FileOp::sequenceSetNColors(int ncolors)
|
||||
@ -878,6 +904,16 @@ void FileOp::setProgress(double progress)
|
||||
m_progressInterface->ackFileOpProgress(progress);
|
||||
}
|
||||
|
||||
void FileOp::getFilenameList(std::vector<std::string>& output) const
|
||||
{
|
||||
if (isSequence()) {
|
||||
output = m_seq.filename_list;
|
||||
}
|
||||
else {
|
||||
output.push_back(m_filename);
|
||||
}
|
||||
}
|
||||
|
||||
double FileOp::progress() const
|
||||
{
|
||||
double progress;
|
||||
@ -933,7 +969,7 @@ FileOp::FileOp(FileOpType type, Context* context)
|
||||
void FileOp::prepareForSequence()
|
||||
{
|
||||
m_seq.palette = new Palette(frame_t(0), 256);
|
||||
m_seq.format_options.reset();
|
||||
m_formatOptions.reset();
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -15,10 +15,11 @@
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/pixel_format.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Flags for FileOp::createLoadDocumentOperation()
|
||||
#define FILE_LOAD_SEQUENCE_NONE 0x00000001
|
||||
#define FILE_LOAD_SEQUENCE_ASK 0x00000002
|
||||
#define FILE_LOAD_SEQUENCE_YES 0x00000004
|
||||
@ -26,6 +27,7 @@
|
||||
|
||||
namespace doc {
|
||||
class Document;
|
||||
class FrameTag;
|
||||
}
|
||||
|
||||
namespace doc {
|
||||
@ -51,18 +53,49 @@ namespace app {
|
||||
FileOpSave
|
||||
} FileOpType;
|
||||
|
||||
class IFileOpProgress
|
||||
{
|
||||
class IFileOpProgress {
|
||||
public:
|
||||
virtual ~IFileOpProgress() { }
|
||||
virtual void ackFileOpProgress(double progress) = 0;
|
||||
};
|
||||
|
||||
class FileOpROI { // Region of interest
|
||||
public:
|
||||
FileOpROI();
|
||||
FileOpROI(const app::Document* doc,
|
||||
const std::string& frameTagName,
|
||||
const doc::frame_t fromFrame,
|
||||
const doc::frame_t toFrame);
|
||||
|
||||
const app::Document* document() const { return m_document; }
|
||||
doc::FrameTag* frameTag() const { return m_frameTag; }
|
||||
doc::frame_t fromFrame() const { return m_fromFrame; }
|
||||
doc::frame_t toFrame() const { return m_toFrame; }
|
||||
|
||||
doc::frame_t frames() const {
|
||||
ASSERT(m_fromFrame >= 0);
|
||||
ASSERT(m_toFrame >= 0);
|
||||
return (m_toFrame - m_fromFrame + 1);
|
||||
}
|
||||
|
||||
private:
|
||||
const app::Document* m_document;
|
||||
doc::FrameTag* m_frameTag;
|
||||
doc::frame_t m_fromFrame;
|
||||
doc::frame_t m_toFrame;
|
||||
};
|
||||
|
||||
// Structure to load & save files.
|
||||
class FileOp {
|
||||
public:
|
||||
static FileOp* createLoadDocumentOperation(Context* context, const char* filename, int flags);
|
||||
static FileOp* createSaveDocumentOperation(const Context* context, const Document* document, const char* filename, const char* fn_format);
|
||||
static FileOp* createLoadDocumentOperation(Context* context,
|
||||
const char* filename,
|
||||
int flags);
|
||||
|
||||
static FileOp* createSaveDocumentOperation(const Context* context,
|
||||
const FileOpROI& roi,
|
||||
const char* filename,
|
||||
const char* filenameFormat);
|
||||
|
||||
~FileOp();
|
||||
|
||||
@ -78,6 +111,8 @@ namespace app {
|
||||
return doc;
|
||||
}
|
||||
|
||||
const FileOpROI& roi() const { return m_roi; }
|
||||
|
||||
void createDocument(Sprite* spr);
|
||||
void operate(IFileOpProgress* progress = nullptr);
|
||||
|
||||
@ -89,10 +124,12 @@ namespace app {
|
||||
// Does extra post-load processing which may require user intervention.
|
||||
void postLoad();
|
||||
|
||||
// Special options specific to the file format.
|
||||
base::SharedPtr<FormatOptions> formatOptions() const;
|
||||
void setFormatOptions(const base::SharedPtr<FormatOptions>& opts);
|
||||
|
||||
// Helpers for file decoder/encoder (FileFormat) with
|
||||
// FILE_SUPPORT_SEQUENCES flag.
|
||||
base::SharedPtr<FormatOptions> sequenceGetFormatOptions() const;
|
||||
void sequenceSetFormatOptions(const base::SharedPtr<FormatOptions>& formatOptions);
|
||||
void sequenceSetNColors(int ncolors);
|
||||
int sequenceGetNColors() const;
|
||||
void sequenceSetColor(int index, int r, int g, int b);
|
||||
@ -115,6 +152,8 @@ namespace app {
|
||||
double progress() const;
|
||||
void setProgress(double progress);
|
||||
|
||||
void getFilenameList(std::vector<std::string>& output) const;
|
||||
|
||||
private:
|
||||
FileOp(); // Undefined
|
||||
FileOp(FileOpType type, Context* context);
|
||||
@ -126,6 +165,7 @@ namespace app {
|
||||
// releaseDocument() member function)
|
||||
Document* m_document; // Loaded document, or document to be saved.
|
||||
std::string m_filename; // File-name to load/save.
|
||||
FileOpROI m_roi;
|
||||
|
||||
// Shared fields between threads.
|
||||
mutable base::mutex m_mutex; // Mutex to access to the next two fields.
|
||||
@ -138,6 +178,8 @@ namespace app {
|
||||
// that support animation like
|
||||
// GIF/FLI/ASE).
|
||||
|
||||
base::SharedPtr<FormatOptions> m_formatOptions;
|
||||
|
||||
// Data for sequences.
|
||||
struct {
|
||||
std::vector<std::string> filename_list; // All file names to load/save.
|
||||
@ -151,22 +193,24 @@ namespace app {
|
||||
bool has_alpha;
|
||||
LayerImage* layer;
|
||||
Cel* last_cel;
|
||||
base::SharedPtr<FormatOptions> format_options;
|
||||
} m_seq;
|
||||
|
||||
void prepareForSequence();
|
||||
};
|
||||
|
||||
// Available extensions for each load/save operation.
|
||||
|
||||
std::string get_readable_extensions();
|
||||
std::string get_writable_extensions();
|
||||
|
||||
// High-level routines to load/save documents.
|
||||
|
||||
app::Document* load_document(Context* context, const char* filename);
|
||||
int save_document(Context* context, doc::Document* document);
|
||||
|
||||
// Returns true if the given filename contains a file extension that
|
||||
// can be used to save only static images (i.e. animations are saved
|
||||
// as sequence of files).
|
||||
bool is_static_image_format(const std::string& filename);
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -40,7 +40,7 @@ static FileFormatsManager* singleton = NULL;
|
||||
FileFormatsManager* FileFormatsManager::instance()
|
||||
{
|
||||
if (!singleton)
|
||||
singleton = new FileFormatsManager();
|
||||
singleton = new FileFormatsManager;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
@ -51,15 +51,7 @@ void FileFormatsManager::destroyInstance()
|
||||
singleton = NULL;
|
||||
}
|
||||
|
||||
FileFormatsManager::~FileFormatsManager()
|
||||
{
|
||||
FileFormatsList::iterator end = this->end();
|
||||
for (FileFormatsList::iterator it = begin(); it != end; ++it) {
|
||||
delete (*it); // delete the FileFormat
|
||||
}
|
||||
}
|
||||
|
||||
void FileFormatsManager::registerAllFormats()
|
||||
FileFormatsManager::FileFormatsManager()
|
||||
{
|
||||
// The first format is the default image format in FileSelector
|
||||
registerFormat(CreateAseFormat());
|
||||
@ -77,6 +69,14 @@ void FileFormatsManager::registerAllFormats()
|
||||
#endif
|
||||
}
|
||||
|
||||
FileFormatsManager::~FileFormatsManager()
|
||||
{
|
||||
FileFormatsList::iterator end = this->end();
|
||||
for (FileFormatsList::iterator it = begin(); it != end; ++it) {
|
||||
delete (*it); // delete the FileFormat
|
||||
}
|
||||
}
|
||||
|
||||
void FileFormatsManager::registerFormat(FileFormat* fileFormat)
|
||||
{
|
||||
m_formats.push_back(fileFormat);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -29,8 +29,6 @@ namespace app {
|
||||
|
||||
virtual ~FileFormatsManager();
|
||||
|
||||
void registerAllFormats();
|
||||
|
||||
// Iterators to access to the list of formats.
|
||||
FileFormatsList::iterator begin();
|
||||
FileFormatsList::iterator end();
|
||||
@ -38,7 +36,7 @@ namespace app {
|
||||
FileFormat* getFileFormatByExtension(const char* extension) const;
|
||||
|
||||
private:
|
||||
// Register one format.
|
||||
FileFormatsManager();
|
||||
void registerFormat(FileFormat* fileFormat);
|
||||
|
||||
FileFormatsList m_formats;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -12,6 +12,7 @@
|
||||
#include "app/document.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/file/file_formats_manager.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/doc.h"
|
||||
|
||||
#include <cstdio>
|
||||
@ -23,7 +24,6 @@ using namespace app;
|
||||
TEST(File, SeveralSizes)
|
||||
{
|
||||
// Register all possible image formats.
|
||||
FileFormatsManager::instance()->registerAllFormats();
|
||||
std::vector<char> fn(256);
|
||||
app::Context ctx;
|
||||
|
||||
@ -33,7 +33,7 @@ TEST(File, SeveralSizes)
|
||||
std::sprintf(&fn[0], "test.ase");
|
||||
|
||||
{
|
||||
doc::Document* doc = ctx.documents().add(w, h, doc::ColorMode::INDEXED, 256);
|
||||
base::UniquePtr<doc::Document> doc(ctx.documents().add(w, h, doc::ColorMode::INDEXED, 256));
|
||||
doc->setFilename(&fn[0]);
|
||||
|
||||
// Random pixels
|
||||
@ -50,19 +50,18 @@ TEST(File, SeveralSizes)
|
||||
}
|
||||
}
|
||||
|
||||
save_document(&ctx, doc);
|
||||
save_document(&ctx, doc.get());
|
||||
doc->close();
|
||||
delete doc;
|
||||
}
|
||||
|
||||
{
|
||||
app::Document* doc = load_document(&ctx, &fn[0]);
|
||||
base::UniquePtr<app::Document> doc(load_document(&ctx, &fn[0]));
|
||||
ASSERT_EQ(w, doc->sprite()->width());
|
||||
ASSERT_EQ(h, doc->sprite()->height());
|
||||
|
||||
// Same random pixels (see the seed)
|
||||
Layer* layer = doc->sprite()->folder()->getFirstLayer();
|
||||
ASSERT_TRUE(layer != NULL);
|
||||
ASSERT_TRUE(layer != nullptr);
|
||||
Image* image = layer->cel(frame_t(0))->image();
|
||||
std::srand(w*h);
|
||||
int c = std::rand()%256;
|
||||
@ -75,7 +74,6 @@ TEST(File, SeveralSizes)
|
||||
}
|
||||
|
||||
doc->close();
|
||||
delete doc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -156,11 +156,13 @@ bool FliFormat::onLoad(FileOp* fop)
|
||||
|
||||
#ifdef ENABLE_SAVE
|
||||
|
||||
static int get_time_precision(const Sprite *sprite)
|
||||
static int get_time_precision(const Sprite *sprite,
|
||||
const doc::frame_t fromFrame,
|
||||
const doc::frame_t toFrame)
|
||||
{
|
||||
// Check if all frames have the same duration
|
||||
bool constantFrameRate = true;
|
||||
for (frame_t c(1); c < sprite->totalFrames(); ++c) {
|
||||
for (frame_t c=fromFrame+1; c <= toFrame; ++c) {
|
||||
if (sprite->frameDuration(c-1) != sprite->frameDuration(c)) {
|
||||
constantFrameRate = false;
|
||||
break;
|
||||
@ -170,7 +172,7 @@ static int get_time_precision(const Sprite *sprite)
|
||||
return sprite->frameDuration(0);
|
||||
|
||||
int precision = 1000;
|
||||
for (frame_t c(0); c < sprite->totalFrames() && precision > 1; ++c) {
|
||||
for (frame_t c=fromFrame; c <= toFrame && precision > 1; ++c) {
|
||||
int len = sprite->frameDuration(c);
|
||||
while (len / precision == 0)
|
||||
precision /= 10;
|
||||
@ -192,7 +194,10 @@ bool FliFormat::onSave(FileOp* fop)
|
||||
header.frames = 0;
|
||||
header.width = sprite->width();
|
||||
header.height = sprite->height();
|
||||
header.speed = get_time_precision(sprite);
|
||||
header.speed = get_time_precision(
|
||||
sprite,
|
||||
fop->roi().fromFrame(),
|
||||
fop->roi().toFrame());
|
||||
encoder.writeHeader(header);
|
||||
|
||||
// Create the bitmaps
|
||||
@ -203,10 +208,9 @@ bool FliFormat::onSave(FileOp* fop)
|
||||
flic::Frame fliFrame;
|
||||
fliFrame.pixels = bmp->getPixelAddress(0, 0);
|
||||
fliFrame.rowstride = IndexedTraits::getRowStrideBytes(bmp->width());
|
||||
for (frame_t frame_it=0;
|
||||
frame_it <= sprite->totalFrames();
|
||||
++frame_it) {
|
||||
frame_t frame = (frame_it % sprite->totalFrames());
|
||||
frame_t nframes = fop->roi().frames();
|
||||
for (frame_t frame_it=0; frame_it <= nframes; ++frame_it) {
|
||||
frame_t frame = fop->roi().fromFrame() + (frame_it % nframes);
|
||||
const Palette* pal = sprite->palette(frame);
|
||||
int size = MIN(256, pal->size());
|
||||
|
||||
@ -222,7 +226,7 @@ bool FliFormat::onSave(FileOp* fop)
|
||||
|
||||
// How many times this frame should be written to get the same
|
||||
// time that it has in the sprite
|
||||
if (frame_it < sprite->totalFrames()) {
|
||||
if (frame_it < nframes) {
|
||||
int times = sprite->frameDuration(frame) / header.speed;
|
||||
times = MAX(1, times);
|
||||
for (int c=0; c<times; c++)
|
||||
@ -233,7 +237,7 @@ bool FliFormat::onSave(FileOp* fop)
|
||||
}
|
||||
|
||||
// Update progress
|
||||
fop->setProgress((float)(frame_it+1) / (float)(sprite->totalFrames()+1));
|
||||
fop->setProgress((float)(frame_it+1) / (float)(nframes+1));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -870,7 +870,7 @@ public:
|
||||
else
|
||||
m_clearColor = rgba(0, 0, 0, 0);
|
||||
|
||||
const base::SharedPtr<GifOptions> gifOptions = fop->sequenceGetFormatOptions();
|
||||
const base::SharedPtr<GifOptions> gifOptions = fop->formatOptions();
|
||||
m_interlaced = gifOptions->interlaced();
|
||||
m_loop = (gifOptions->loop() ? 0: -1);
|
||||
|
||||
@ -897,7 +897,7 @@ public:
|
||||
m_currentImage = m_images[1].get();
|
||||
m_nextImage = m_images[2].get();
|
||||
|
||||
int nframes = m_sprite->totalFrames();
|
||||
int nframes = totalFrames();
|
||||
for (int frameNum=0; frameNum<nframes; ++frameNum) {
|
||||
if (frameNum == 0)
|
||||
renderFrame(0, m_nextImage);
|
||||
@ -933,6 +933,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
doc::frame_t totalFrames() const {
|
||||
return m_fop->roi().frames();
|
||||
}
|
||||
|
||||
doc::frame_t fromFrame() const {
|
||||
return m_fop->roi().fromFrame();
|
||||
}
|
||||
|
||||
void writeHeader() {
|
||||
if (EGifPutScreenDesc(m_gifFile,
|
||||
m_spriteBounds.w,
|
||||
@ -983,7 +991,7 @@ private:
|
||||
// frame and maybe the transparency index).
|
||||
void writeExtension(int frameNum, int transparentIndex, DisposalMethod disposalMethod) {
|
||||
unsigned char extension_bytes[5];
|
||||
int frameDelay = m_sprite->frameDuration(frameNum) / 10;
|
||||
int frameDelay = m_sprite->frameDuration(fromFrame()+frameNum) / 10;
|
||||
|
||||
extension_bytes[0] = (((int(disposalMethod) & 7) << 2) |
|
||||
(transparentIndex >= 0 ? 1: 0));
|
||||
@ -1029,7 +1037,7 @@ private:
|
||||
prev = calculateFrameBounds(m_currentImage, m_previousImage);
|
||||
|
||||
if (!m_hasBackground &&
|
||||
frameNum+1 < m_sprite->totalFrames())
|
||||
frameNum+1 < totalFrames())
|
||||
next = calculateFrameBounds(m_currentImage, m_nextImage);
|
||||
|
||||
frameBounds = prev.createUnion(next);
|
||||
@ -1056,8 +1064,8 @@ private:
|
||||
void writeImage(int frameNum, const gfx::Rect& frameBounds, DisposalMethod disposal) {
|
||||
UniquePtr<Palette> framePaletteRef;
|
||||
UniquePtr<RgbMap> rgbmapRef;
|
||||
Palette* framePalette = m_sprite->palette(frameNum);
|
||||
RgbMap* rgbmap = m_sprite->rgbMap(frameNum);
|
||||
Palette* framePalette = m_sprite->palette(fromFrame()+frameNum);
|
||||
RgbMap* rgbmap = m_sprite->rgbMap(fromFrame()+frameNum);
|
||||
|
||||
// Create optimized palette for RGB/Grayscale images
|
||||
if (m_quantizeColormaps) {
|
||||
@ -1235,7 +1243,7 @@ private:
|
||||
render::Render render;
|
||||
render.setBgType(render::BgType::NONE);
|
||||
clear_image(dst, m_clearColor);
|
||||
render.renderSprite(dst, m_sprite, frameNum);
|
||||
render.renderSprite(dst, m_sprite, fromFrame()+frameNum);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -237,8 +237,7 @@ bool JpegFormat::onSave(FileOp* fop)
|
||||
const Image* image = fop->sequenceImage();
|
||||
JSAMPARRAY buffer;
|
||||
JDIMENSION buffer_height;
|
||||
const base::SharedPtr<JpegOptions> jpeg_options =
|
||||
fop->sequenceGetFormatOptions();
|
||||
const base::SharedPtr<JpegOptions> jpeg_options = fop->formatOptions();
|
||||
int c;
|
||||
|
||||
// Open the file for write in it.
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2015 Gabriel Rauter
|
||||
// Copyright (C) 2015 David Capello
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -154,7 +154,7 @@ bool WebPFormat::onLoad(FileOp* fop)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fop->sequenceGetFormatOptions()) {
|
||||
if (!fop->formatOptions()) {
|
||||
base::SharedPtr<WebPOptions> webPOptions(new WebPOptions());
|
||||
webPOptions->setLossless(std::min(config.input.format - 1, 1));
|
||||
fop->sequenceSetFormatOptions(webPOptions);
|
||||
@ -251,8 +251,7 @@ bool WebPFormat::onSave(FileOp* fop)
|
||||
return false;
|
||||
}
|
||||
|
||||
base::SharedPtr<WebPOptions> webp_options =
|
||||
fop->sequenceGetFormatOptions();
|
||||
base::SharedPtr<WebPOptions> webp_options = fop->formatOptions();
|
||||
|
||||
WebPConfig config;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -11,6 +11,8 @@
|
||||
|
||||
#include "app/filename_formatter.h"
|
||||
|
||||
#include "app/file/file.h"
|
||||
#include "app/file/split_filename.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/path.h"
|
||||
#include "base/replace_string.h"
|
||||
@ -48,10 +50,34 @@ static bool replace_frame(const char* frameKey, // E.g. = "{frame"
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_frame_info_from_filename_format(
|
||||
const std::string& format, int* frameBase, int* width)
|
||||
{
|
||||
const char* frameKey = "{frame";
|
||||
size_t i = format.find(frameKey);
|
||||
if (i != std::string::npos) {
|
||||
int keyLen = std::strlen(frameKey);
|
||||
|
||||
size_t j = format.find("}", i+keyLen);
|
||||
if (j != std::string::npos) {
|
||||
std::string frameStr = format.substr(i, j - i + 1);
|
||||
|
||||
if (frameBase)
|
||||
*frameBase = std::strtol(frameStr.c_str()+keyLen, NULL, 10);
|
||||
|
||||
if (width)
|
||||
*width = (int(j) - int(i+keyLen));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string filename_formatter(
|
||||
const std::string& format,
|
||||
FilenameInfo& info,
|
||||
bool replaceFrame)
|
||||
const bool replaceFrame)
|
||||
{
|
||||
const std::string& filename = info.filename();
|
||||
std::string path = base::get_file_path(filename);
|
||||
@ -77,42 +103,95 @@ std::string filename_formatter(
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string set_frame_format(
|
||||
const std::string& format,
|
||||
const std::string& newFrameFormat)
|
||||
std::string get_default_filename_format(
|
||||
std::string& filename,
|
||||
const bool withPath,
|
||||
const bool hasFrames,
|
||||
const bool hasLayer,
|
||||
const bool hasFrameTag)
|
||||
{
|
||||
std::string output = format;
|
||||
std::string format;
|
||||
|
||||
size_t i = output.find("{frame");
|
||||
if (i != std::string::npos) {
|
||||
size_t j = output.find("}", i+6);
|
||||
if (j != std::string::npos) {
|
||||
output.replace(i, j - i + 1, newFrameFormat);
|
||||
if (withPath)
|
||||
format += "{path}/";
|
||||
|
||||
format += "{title}";
|
||||
|
||||
if (hasLayer)
|
||||
format += " ({layer})";
|
||||
|
||||
if (hasFrameTag)
|
||||
format += " #{tag}";
|
||||
|
||||
if (hasFrames && is_static_image_format(filename) &&
|
||||
filename.find("{frame") == std::string::npos &&
|
||||
filename.find("{tagframe") == std::string::npos) {
|
||||
const bool autoFrameFromLastDigit =
|
||||
(!hasLayer &&
|
||||
!hasFrameTag);
|
||||
|
||||
// Check if we already have a frame number at the end of the
|
||||
// filename (e.g. output01.png)
|
||||
int frameBase = -1, frameWidth = 0;
|
||||
std::string left, right;
|
||||
if (autoFrameFromLastDigit)
|
||||
frameBase = split_filename(filename.c_str(), left, right, frameWidth);
|
||||
if (frameBase >= 0) {
|
||||
std::vector<char> buf(32);
|
||||
std::sprintf(&buf[0], "{frame%0*d}", frameWidth, frameBase);
|
||||
|
||||
if (hasLayer || hasFrameTag)
|
||||
format += " ";
|
||||
format += &buf[0];
|
||||
|
||||
// Remove the frame number from the filename part.
|
||||
filename = left;
|
||||
filename += right;
|
||||
}
|
||||
// Check if there is already a {frame} tag in the filename
|
||||
else if (get_frame_info_from_filename_format(filename, &frameBase, &frameWidth)) {
|
||||
// Do nothing
|
||||
}
|
||||
else {
|
||||
if (hasLayer || hasFrameTag)
|
||||
format += " {frame}";
|
||||
else
|
||||
format += "{frame1}";
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
format += ".{extension}";
|
||||
return format;
|
||||
}
|
||||
|
||||
std::string add_frame_format(
|
||||
const std::string& format,
|
||||
const std::string& newFrameFormat)
|
||||
std::string get_default_filename_format_for_sheet(
|
||||
const std::string& filename,
|
||||
const bool hasFrames,
|
||||
const bool hasLayer,
|
||||
const bool hasFrameTag)
|
||||
{
|
||||
std::string output = format;
|
||||
std::string format = "{title}";
|
||||
|
||||
size_t i = output.find("{frame");
|
||||
size_t j = output.find("{tagframe");
|
||||
if (i == std::string::npos &&
|
||||
j == std::string::npos) {
|
||||
output =
|
||||
base::join_path(
|
||||
base::get_file_path(format),
|
||||
base::get_file_title(format))
|
||||
+ newFrameFormat + "." +
|
||||
base::get_file_extension(format);
|
||||
if (hasLayer)
|
||||
format += " ({layer})";
|
||||
|
||||
if (hasFrameTag)
|
||||
format += " #{tag}";
|
||||
|
||||
if (hasFrames) {
|
||||
int frameBase, frameWidth;
|
||||
|
||||
// Check if there is already a {frame} tag in the filename
|
||||
if (get_frame_info_from_filename_format(filename, &frameBase, &frameWidth)) {
|
||||
// Do nothing
|
||||
}
|
||||
else {
|
||||
format += " {frame}";
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
format += ".{extension}";
|
||||
return format;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -63,21 +63,31 @@ namespace app {
|
||||
int m_tagFrame;
|
||||
};
|
||||
|
||||
// Returns the information inside {frame} tag.
|
||||
// E.g. For {frame001} returns width=3 and startFrom=1
|
||||
bool get_frame_info_from_filename_format(
|
||||
const std::string& format, int* startFrom, int* width);
|
||||
|
||||
// If "replaceFrame" is false, this function doesn't replace all the
|
||||
// information that depends on the current frame ({frame},
|
||||
// {tagframe}, {tag}, etc.)
|
||||
std::string filename_formatter(
|
||||
const std::string& format,
|
||||
FilenameInfo& info,
|
||||
bool replaceFrame = true);
|
||||
const bool replaceFrame = true);
|
||||
|
||||
std::string set_frame_format(
|
||||
const std::string& format,
|
||||
const std::string& newFrameFormat);
|
||||
std::string get_default_filename_format(
|
||||
std::string& filename,
|
||||
const bool withPath,
|
||||
const bool hasFrames,
|
||||
const bool hasLayer,
|
||||
const bool hasFrameTag);
|
||||
|
||||
std::string add_frame_format(
|
||||
const std::string& format,
|
||||
const std::string& newFrameFormat);
|
||||
std::string get_default_filename_format_for_sheet(
|
||||
const std::string& filename,
|
||||
const bool hasFrames,
|
||||
const bool hasLayer,
|
||||
const bool hasFrameTag);
|
||||
|
||||
} // namespace app
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -130,42 +130,6 @@ TEST(FilenameFormatter, WithFrame)
|
||||
FilenameInfo().filename("C:/temp/file.png").layerName("Background").frame(2)));
|
||||
}
|
||||
|
||||
TEST(SetFrameFormat, Tests)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
"{path}/{title}{frame1}.{extension}",
|
||||
set_frame_format(
|
||||
"{path}/{title}{frame}.{extension}",
|
||||
"{frame1}"));
|
||||
|
||||
EXPECT_EQ(
|
||||
"{path}/{title}{frame01}.{extension}",
|
||||
set_frame_format(
|
||||
"{path}/{title}{frame}.{extension}",
|
||||
"{frame01}"));
|
||||
|
||||
EXPECT_EQ(
|
||||
"{path}/{title}{frame}.{extension}",
|
||||
set_frame_format(
|
||||
"{path}/{title}{frame01}.{extension}",
|
||||
"{frame}"));
|
||||
}
|
||||
|
||||
TEST(AddFrameFormat, Tests)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
base::fix_path_separators("{path}/{title}{frame001}.{extension}"),
|
||||
add_frame_format(
|
||||
"{path}/{title}.{extension}",
|
||||
"{frame001}"));
|
||||
|
||||
EXPECT_EQ(
|
||||
"{path}/{title}{frame1}.{extension}",
|
||||
add_frame_format(
|
||||
"{path}/{title}{frame1}.{extension}",
|
||||
"{frame001}"));
|
||||
}
|
||||
|
||||
TEST(FilenameFormatter, WithTagFrame)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
@ -186,3 +150,71 @@ TEST(FilenameFormatter, WithTagFrame)
|
||||
"{path}/{title}_{frame}_{tagframe24}.{extension}",
|
||||
FilenameInfo().filename("./file.png").frame(2).tagFrame(1)));
|
||||
}
|
||||
|
||||
TEST(FilenameFormatter, GetFrameInfo)
|
||||
{
|
||||
int frameBase, width;
|
||||
|
||||
EXPECT_EQ(false, get_frame_info_from_filename_format("hi.png", nullptr, nullptr));
|
||||
|
||||
frameBase = width = -1;
|
||||
EXPECT_EQ(true, get_frame_info_from_filename_format("hi{frame}.png", &frameBase, &width));
|
||||
EXPECT_EQ(0, frameBase);
|
||||
EXPECT_EQ(0, width);
|
||||
|
||||
frameBase = width = -1;
|
||||
EXPECT_EQ(true, get_frame_info_from_filename_format("hi{frame1}.png", &frameBase, &width));
|
||||
EXPECT_EQ(1, frameBase);
|
||||
EXPECT_EQ(1, width);
|
||||
|
||||
frameBase = width = -1;
|
||||
EXPECT_EQ(true, get_frame_info_from_filename_format("hi{frame032}.png", &frameBase, &width));
|
||||
EXPECT_EQ(32, frameBase);
|
||||
EXPECT_EQ(3, width);
|
||||
}
|
||||
|
||||
TEST(FilenameFormatter, DefaultFormat)
|
||||
{
|
||||
std::string fn;
|
||||
|
||||
fn = "/path/hello.png";
|
||||
EXPECT_EQ("{title}.{extension}", get_default_filename_format(fn, false, false, false, false));
|
||||
EXPECT_EQ("/path/hello.png", fn);
|
||||
|
||||
fn = "/path/hello.png";
|
||||
EXPECT_EQ("{path}/{title}.{extension}", get_default_filename_format(fn, true, false, false, false));
|
||||
EXPECT_EQ("/path/hello.png", fn);
|
||||
|
||||
fn = "/path/hello.png";
|
||||
EXPECT_EQ("{path}/{title}{frame1}.{extension}", get_default_filename_format(fn, true, true, false, false));
|
||||
EXPECT_EQ("/path/hello.png", fn);
|
||||
|
||||
fn = "/path/hello.gif";
|
||||
EXPECT_EQ("{path}/{title}.{extension}", get_default_filename_format(fn, true, true, false, false));
|
||||
EXPECT_EQ("/path/hello.gif", fn);
|
||||
|
||||
fn = "/path/hello.png";
|
||||
EXPECT_EQ("{path}/{title} ({layer}) {frame}.{extension}", get_default_filename_format(fn, true, true, true, false));
|
||||
EXPECT_EQ("/path/hello.png", fn);
|
||||
|
||||
fn = "/path/hello.gif";
|
||||
EXPECT_EQ("{path}/{title} ({layer}).{extension}", get_default_filename_format(fn, true, true, true, false));
|
||||
EXPECT_EQ("/path/hello.gif", fn);
|
||||
|
||||
fn = "/path/hello1.png";
|
||||
EXPECT_EQ("{path}/{title}{frame1}.{extension}", get_default_filename_format(fn, true, true, false, false));
|
||||
EXPECT_EQ("/path/hello.png", fn);
|
||||
|
||||
fn = "/path/hello1.gif";
|
||||
EXPECT_EQ("{path}/{title}.{extension}", get_default_filename_format(fn, true, true, false, false));
|
||||
EXPECT_EQ("/path/hello1.gif", fn);
|
||||
|
||||
fn = "/path/hello001.png";
|
||||
EXPECT_EQ("{path}/{title}{frame001}.{extension}", get_default_filename_format(fn, true, true, false, false));
|
||||
EXPECT_EQ("/path/hello.png", fn);
|
||||
|
||||
// When layers or tags are used in the filename format, the 1 is not converted to {frame1}
|
||||
fn = "/path/hello1.png";
|
||||
EXPECT_EQ("{path}/{title} #{tag} {frame}.{extension}", get_default_filename_format(fn, true, true, false, true));
|
||||
EXPECT_EQ("/path/hello1.png", fn);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -28,6 +28,7 @@ namespace doc {
|
||||
FrameTags* owner() const { return m_owner; }
|
||||
frame_t fromFrame() const { return m_from; }
|
||||
frame_t toFrame() const { return m_to; }
|
||||
frame_t frames() const { return m_to - m_from + 1; }
|
||||
const std::string& name() const { return m_name; }
|
||||
color_t color() const { return m_color; }
|
||||
AniDir aniDir() const { return m_aniDir; }
|
||||
|
@ -10,7 +10,7 @@
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/app_options.h"
|
||||
#include "app/cli/app_options.h"
|
||||
#include "app/console.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/send_crash.h"
|
||||
|
@ -4,6 +4,6 @@
|
||||
include_directories(${DUKTAPE_DIR})
|
||||
|
||||
add_library(duktape ${DUKTAPE_DIR}/duktape.c)
|
||||
add_library(script-lib engine.cpp)
|
||||
add_library(script-lib engine.cpp engine_delegate.cpp)
|
||||
|
||||
target_link_libraries(script-lib duktape)
|
||||
|
22
src/script/engine_delegate.cpp
Normal file
22
src/script/engine_delegate.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// Aseprite Scripting Library
|
||||
// Copyright (c) 2015-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "script/engine_delegate.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace script {
|
||||
|
||||
void StdoutEngineDelegate::onConsolePrint(const char* text)
|
||||
{
|
||||
std::printf("%s\n", text);
|
||||
}
|
||||
|
||||
} // namespace script
|
@ -16,6 +16,11 @@ namespace script {
|
||||
virtual void onConsolePrint(const char* text) = 0;
|
||||
};
|
||||
|
||||
class StdoutEngineDelegate : public EngineDelegate {
|
||||
public:
|
||||
void onConsolePrint(const char* text) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user