[aseprite] 40/134: Add more command line options to export or convert sprites

Tobias Hansen thansen at moszumanska.debian.org
Sat Mar 14 17:10:01 UTC 2015


This is an automated email from the git hooks/post-receive script.

thansen pushed a commit to branch master
in repository aseprite.

commit ce962f4999e4b52dace5a4d263c96c2fbbf17e45
Author: David Capello <davidcapello at gmail.com>
Date:   Thu Nov 6 21:04:32 2014 -0300

    Add more command line options to export or convert sprites
    
    Added --save-as, --scale, --split-layers, and --import-layer command line
    options.
---
 src/app/app.cpp                      | 190 +++++++++++++++++++++++++----------
 src/app/app_menus.cpp                |   5 +-
 src/app/app_options.cpp              |  59 +++++------
 src/app/app_options.h                |  52 +++++-----
 src/app/commands/cmd_save_file.cpp   |   2 +-
 src/app/commands/cmd_save_file.h     |   4 +
 src/app/commands/cmd_sprite_size.cpp | 161 ++++++++++++++++-------------
 src/app/commands/cmd_sprite_size.h   |  72 +++++++++++++
 src/app/document_exporter.cpp        |  95 +++++++++++-------
 src/app/document_exporter.h          |  20 +++-
 src/app/job.cpp                      |  47 +++++----
 src/app/job.h                        |   2 +-
 src/app/modules/gui.cpp              |   3 +-
 src/app/ui_context.cpp               |  15 ++-
 src/base/program_options.cpp         |   4 +-
 src/doc/documents.h                  |   1 +
 16 files changed, 483 insertions(+), 249 deletions(-)

diff --git a/src/app/app.cpp b/src/app/app.cpp
index 009a7f3..bfc9a8f 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -1,5 +1,5 @@
 /* Aseprite
- * Copyright (C) 2001-2013  David Capello
+ * Copyright (C) 2001-2014  David Capello
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,6 +25,8 @@
 #include "app/app_options.h"
 #include "app/check_update.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"
@@ -133,14 +135,9 @@ void App::initialize(int argc, const char* argv[])
   m_isShell = options.startShell();
   m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0);
 
-  if (options.hasExporterParams()) {
+  if (options.hasExporterParams())
     m_exporter.reset(new DocumentExporter);
 
-    m_exporter->setDataFilename(options.data());
-    m_exporter->setTextureFilename(options.sheet());
-    m_exporter->setScale(options.scale());
-  }
-
   // Register well-known image file types.
   FileFormatsManager::instance()->registerAllFormats();
 
@@ -181,14 +178,14 @@ void App::initialize(int argc, const char* argv[])
   set_current_palette(NULL, true);
 
   // Initialize GUI interface
-  UIContext* context = UIContext::instance();
+  UIContext* ctx = UIContext::instance();
   if (isGui()) {
     PRINTF("GUI mode\n");
 
     // Setup the GUI cursor and redraw screen
 
     ui::set_use_native_cursors(
-      context->settings()->experimental()->useNativeCursor());
+      ctx->settings()->experimental()->useNativeCursor());
 
     jmouse_set_cursor(kArrowCursor);
 
@@ -214,30 +211,111 @@ void App::initialize(int argc, const char* argv[])
   PRINTF("Processing options...\n");
 
   // Open file specified in the command line
-  {
+  if (!options.values().empty()) {
     Console console;
-    for (const auto& value : options.values()) {
-      if (value.option() != NULL) // File names aren't associated to any option
-        continue;
+    bool splitLayers = false;
+    std::string importLayer;
 
-      const std::string& filename = value.value();
-
-      // Load the sprite
-      Document* document = load_document(context, filename.c_str());
-      if (!document) {
-        if (!isGui())
-          console.printf("Error loading file \"%s\"\n", filename.c_str());
+    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());
+        }
+        // --sheet <file.png>
+        else if (opt == &options.sheet()) {
+          if (m_exporter)
+            m_exporter->setTextureFilename(value.value());
+        }
+        // --split-layers
+        else if (opt == &options.splitLayers()) {
+          splitLayers = true;
+        }
+        // --import-layer <layer-name>
+        else if (opt == &options.importLayer()) {
+          importLayer = 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);
+
+            Command* command = CommandsModule::instance()->getCommandByName(CommandId::SaveFileCopyAs);
+            static_cast<SaveFileBaseCommand*>(command)->setFilename(value.value());
+            ctx->executeCommand(command);
+          }
+        }
+        // --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(doc);
+            ctx->executeCommand(command);
+          }
+        }
       }
+      // File names aren't associated to any option
       else {
-        // Add the given file in the argument as a "recent file" only
-        // if we are running in GUI mode. If the program is executed
-        // in batch mode this is not desirable.
-        if (isGui())
-          getRecentFiles()->addRecentFile(filename.c_str());
-
-        // Add the document to the exporter.
-        if (m_exporter != NULL)
-          m_exporter->addDocument(document);
+        const std::string& filename = value.value();
+
+        // Load the sprite
+        Document* doc = load_document(ctx, filename.c_str());
+        if (!doc) {
+          if (!isGui())
+            console.printf("Error loading file \"%s\"\n", filename.c_str());
+        }
+        else {
+          // Add the given file in the argument as a "recent file" only
+          // if we are running in GUI mode. If the program is executed
+          // in batch mode this is not desirable.
+          if (isGui())
+            getRecentFiles()->addRecentFile(filename.c_str());
+
+          if (m_exporter != NULL) {
+            if (!importLayer.empty()) {
+              std::vector<Layer*> layers;
+              Layer* foundLayer = NULL;
+              doc->sprite()->getLayersList(layers);
+              for (Layer* layer : layers) {
+                if (layer->name() == importLayer) {
+                  foundLayer = layer;
+                  break;
+                }
+              }
+              if (foundLayer)
+                m_exporter->addDocument(doc, foundLayer);
+            }
+            else if (splitLayers) {
+              std::vector<Layer*> layers;
+              doc->sprite()->getLayersList(layers);
+              for (auto layer : layers)
+                m_exporter->addDocument(doc, layer);
+            }
+            else
+              m_exporter->addDocument(doc);
+          }
+        }
+
+        if (!importLayer.empty())
+          importLayer.clear();
+
+        if (splitLayers)
+          splitLayers = false;
       }
     }
   }
@@ -272,32 +350,10 @@ void App::run()
 
     // Run the GUI main message loop
     gui_run();
-
-    // Destroy all documents in the UIContext.
-    const doc::Documents& docs = m_modules->m_ui_context.documents();
-    while (!docs.empty()) {
-      doc::Document* doc = docs.back();
-
-      // First we close the document. In this way we receive recent
-      // notifications related to the document as an app::Document. If
-      // we delete the document directly, we destroy the app::Document
-      // too early, and then doc::~Document() call
-      // DocumentsObserver::onRemoveDocument(). In this way, observers
-      // could think that they have a fully created app::Document when
-      // in reality it's a doc::Document (in the middle of a
-      // destruction process).
-      //
-      // TODO: This problem is because we're extending doc::Document,
-      // in the future, we should remove app::Document.
-      doc->close();
-      delete doc;
-    }
-
-    // Destroy the window.
-    m_mainWindow.reset(NULL);
   }
+
   // Start shell to execute scripts.
-  else if (m_isShell) {
+  if (m_isShell) {
     m_systemConsole.prepareShell();
 
     if (m_modules->m_scriptingEngine.supportEval()) {
@@ -308,6 +364,31 @@ void App::run()
       std::cerr << "Your version of " PACKAGE " wasn't compiled with shell support.\n";
     }
   }
+
+  // Destroy all documents in the UIContext.
+  const doc::Documents& docs = m_modules->m_ui_context.documents();
+  while (!docs.empty()) {
+    doc::Document* doc = docs.back();
+
+    // First we close the document. In this way we receive recent
+    // notifications related to the document as an app::Document. If
+    // we delete the document directly, we destroy the app::Document
+    // too early, and then doc::~Document() call
+    // DocumentsObserver::onRemoveDocument(). In this way, observers
+    // could think that they have a fully created app::Document when
+    // in reality it's a doc::Document (in the middle of a
+    // destruction process).
+    //
+    // TODO: This problem is because we're extending doc::Document,
+    // in the future, we should remove app::Document.
+    doc->close();
+    delete doc;
+  }
+
+  if (isGui()) {
+    // Destroy the window.
+    m_mainWindow.reset(NULL);
+  }
 }
 
 // Finishes the Aseprite application.
@@ -410,7 +491,8 @@ void app_refresh_screen()
 
 void app_rebuild_documents_tabs()
 {
-  App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
+  if (App::instance()->isGui())
+    App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
 }
 
 PixelFormat app_get_current_pixel_format()
diff --git a/src/app/app_menus.cpp b/src/app/app_menus.cpp
index 66cd5ef..6e1f32c 100644
--- a/src/app/app_menus.cpp
+++ b/src/app/app_menus.cpp
@@ -287,9 +287,10 @@ void AppMenus::applyShortcutToMenuitemsWithCommand(Menu* menu, Command* command,
     Widget* child = *it;
 
     if (child->getType() == kMenuItemWidget) {
-      ASSERT(dynamic_cast<AppMenuItem*>(child) != NULL);
+      AppMenuItem* menuitem = dynamic_cast<AppMenuItem*>(child);
+      if (!menuitem)
+        continue;
 
-      AppMenuItem* menuitem = static_cast<AppMenuItem*>(child);
       Command* mi_command = menuitem->getCommand();
       Params* mi_params = menuitem->getParams();
 
diff --git a/src/app/app_options.cpp b/src/app/app_options.cpp
index fb4e744..4f47f71 100644
--- a/src/app/app_options.cpp
+++ b/src/app/app_options.cpp
@@ -1,5 +1,5 @@
 /* Aseprite
- * Copyright (C) 2001-2013  David Capello
+ * Copyright (C) 2001-2014  David Capello
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,49 +35,37 @@ AppOptions::AppOptions(int argc, const char* argv[])
   : m_exeName(base::get_file_name(argv[0]))
   , m_startUI(true)
   , m_startShell(false)
-  , m_verbose(false)
-  , m_scale(1.0)
+  , m_verboseEnabled(false)
+  , 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").description("Do not start the UI"))
+  , m_saveAs(m_po.add("save-as").requiresValue("<filename>").description("Save the last give document with other name/format"))
+  , m_scale(m_po.add("scale").requiresValue("<factor>").description("Scale all opened documents at the moment"))
+  , m_data(m_po.add("data").requiresValue("<filename.json>").description("File to store the sprite sheet metadata"))
+  , m_sheet(m_po.add("sheet").requiresValue("<filename.png>").description("Image file to save the texture"))
+  , m_splitLayers(m_po.add("split-layers").description("Import each layer of the next given sprite as\n a separated image in the sheet"))
+  , m_importLayer(m_po.add("import-layer").requiresValue("<name>").description("Import just one layer of the next given sprite"))
+  , m_verbose(m_po.add("verbose").description("Explain what is being done (in stderr or log file)"))
+  , m_help(m_po.add("help").mnemonic('?').description("Display this help and exits"))
+  , m_version(m_po.add("version").description("Output version information and exit"))
 {
-  Option& palette = m_po.add("palette").requiresValue("<filename>").description("Use a specific palette by default");
-  Option& shell = m_po.add("shell").description("Start an interactive console to execute scripts");
-  Option& batch = m_po.add("batch").description("Do not start the UI");
-  // Option& dataFormat = m_po.add("format").requiresValue("<name>").description("Select the format for the sprite sheet data");
-  Option& data = m_po.add("data").requiresValue("<filename>").description("File to store the sprite sheet metadata (.json file)");
-  //Option& textureFormat = m_po.add("texture-format").requiresValue("<name>").description("Output texture format.");
-  Option& sheet = m_po.add("sheet").requiresValue("<filename>").description("Image file to save the texture (.png)");
-  //Option& scale = m_po.add("scale").requiresValue("<float>").description("");
-  //Option& scaleMode = m_po.add("scale-mode").requiresValue("<mode>").description("Export the first given document to a JSON object");
-  //Option& splitLayers = m_po.add("split-layers").description("Specifies that each layer of the given file should be saved as a different image in the sheet.");
-  //Option& rotsprite = m_po.add("rotsprite").requiresValue("<angle1,angle2,...>").description("Specifies different angles to export the given image.");
-  //Option& merge = m_po.add("merge").requiresValue("<datafiles>").description("Merge several sprite sheets in one.");
-  Option& verbose = m_po.add("verbose").description("Explain what is being done (in stderr or a log file)");
-  Option& help = m_po.add("help").mnemonic('?').description("Display this help and exits");
-  Option& version = m_po.add("version").description("Output version information and exit");
-
   try {
     m_po.parse(argc, argv);
 
-    m_verbose = m_po.enabled(verbose);
-    m_paletteFileName = m_po.value_of(palette);
-    m_startShell = m_po.enabled(shell);
-    // m_dataFormat = m_po.value_of(dataFormat);
-    m_data = m_po.value_of(data);
-    // m_textureFormat = m_po.value_of(textureFormat);
-    m_sheet = m_po.value_of(sheet);
-    // if (scale.enabled())
-    //   m_scale = std::strtod(m_po.value_of(scale).c_str(), NULL);
-    // m_scaleMode = m_po.value_of(scaleMode);
+    m_verboseEnabled = m_po.enabled(m_verbose);
+    m_paletteFileName = m_po.value_of(m_palette);
+    m_startShell = m_po.enabled(m_shell);
 
-    if (m_po.enabled(help)) {
+    if (m_po.enabled(m_help)) {
       showHelp();
       m_startUI = false;
     }
-    else if (m_po.enabled(version)) {
+    else if (m_po.enabled(m_version)) {
       showVersion();
       m_startUI = false;
     }
 
-    if (m_po.enabled(shell) || m_po.enabled(batch)) {
+    if (m_po.enabled(m_shell) || m_po.enabled(m_batch)) {
       m_startUI = false;
     }
   }
@@ -88,6 +76,13 @@ AppOptions::AppOptions(int argc, const char* argv[])
   }
 }
 
+bool AppOptions::hasExporterParams() const
+{
+  return
+    m_po.enabled(m_data) ||
+    m_po.enabled(m_sheet);
+}
+
 void AppOptions::showHelp()
 {
   std::cout
diff --git a/src/app/app_options.h b/src/app/app_options.h
index 045869f..dc7523a 100644
--- a/src/app/app_options.h
+++ b/src/app/app_options.h
@@ -30,33 +30,31 @@ namespace app {
 
 class AppOptions {
 public:
+  typedef base::ProgramOptions PO;
+  typedef PO::Option Option;
+  typedef PO::ValueList ValueList;
+
   AppOptions(int argc, const char* argv[]);
 
   bool startUI() const { return m_startUI; }
   bool startShell() const { return m_startShell; }
-  bool verbose() const { return m_verbose; }
+  bool verbose() const { return m_verboseEnabled; }
 
   const std::string& paletteFileName() const { return m_paletteFileName; }
 
-  const base::ProgramOptions::ValueList& values() const {
+  const ValueList& values() const {
     return m_po.values();
   }
 
   // Export options
-  const std::string& dataFormat() const { return m_dataFormat; }
-  const std::string& data() const { return m_data; }
-  const std::string& textureFormat() const { return m_textureFormat; }
-  const std::string& sheet() const { return m_sheet; }
-  const double scale() const { return m_scale; }
-  const std::string& scaleMode() const { return m_scaleMode; }
-
-  bool hasExporterParams() {
-    return
-      !m_dataFormat.empty() ||
-      !m_data.empty() ||
-      !m_textureFormat.empty() ||
-      !m_sheet.empty();
-  }
+  const Option& saveAs() const { return m_saveAs; }
+  const Option& scale() const { return m_scale; }
+  const Option& data() const { return m_data; }
+  const Option& sheet() const { return m_sheet; }
+  const Option& splitLayers() const { return m_splitLayers; }
+  const Option& importLayer() const { return m_importLayer; }
+
+  bool hasExporterParams() const;
 
 private:
   void showHelp();
@@ -66,15 +64,23 @@ private:
   base::ProgramOptions m_po;
   bool m_startUI;
   bool m_startShell;
-  bool m_verbose;
+  bool m_verboseEnabled;
   std::string m_paletteFileName;
 
-  std::string m_dataFormat;
-  std::string m_data;
-  std::string m_textureFormat;
-  std::string m_sheet;
-  double m_scale;
-  std::string m_scaleMode;
+  Option& m_palette;
+  Option& m_shell;
+  Option& m_batch;
+  Option& m_saveAs;
+  Option& m_scale;
+  Option& m_data;
+  Option& m_sheet;
+  Option& m_splitLayers;
+  Option& m_importLayer;
+
+  Option& m_verbose;
+  Option& m_help;
+  Option& m_version;
+
 };
 
 } // namespace app
diff --git a/src/app/commands/cmd_save_file.cpp b/src/app/commands/cmd_save_file.cpp
index 7d8320e..0defd78 100644
--- a/src/app/commands/cmd_save_file.cpp
+++ b/src/app/commands/cmd_save_file.cpp
@@ -100,7 +100,7 @@ static void save_document_in_background(Context* context, Document* document, bo
   else if (fop_is_stop(fop)) {
     document->impossibleToBackToSavedState();
   }
-  else {
+  else if (context->isUiAvailable()) {
     App::instance()->getRecentFiles()->addRecentFile(document->filename().c_str());
     if (mark_as_saved)
       document->markAsSaved();
diff --git a/src/app/commands/cmd_save_file.h b/src/app/commands/cmd_save_file.h
index 8affee5..f400979 100644
--- a/src/app/commands/cmd_save_file.h
+++ b/src/app/commands/cmd_save_file.h
@@ -35,6 +35,10 @@ namespace app {
       return m_selectedFilename;
     }
 
+    void setFilename(const std::string& fn) {
+      m_filename = fn;
+    }
+
   protected:
     void onLoadParams(Params* params) override;
     bool onEnabled(Context* context) override;
diff --git a/src/app/commands/cmd_sprite_size.cpp b/src/app/commands/cmd_sprite_size.cpp
index ab642e6..99cee9f 100644
--- a/src/app/commands/cmd_sprite_size.cpp
+++ b/src/app/commands/cmd_sprite_size.cpp
@@ -20,7 +20,9 @@
 #include "config.h"
 #endif
 
+#include "app/commands/cmd_sprite_size.h"
 #include "app/commands/command.h"
+#include "app/commands/params.h"
 #include "app/context_access.h"
 #include "app/document_api.h"
 #include "app/find_widget.h"
@@ -159,34 +161,48 @@ protected:
 
 };
 
-class SpriteSizeCommand : public Command {
-public:
-  SpriteSizeCommand();
-  Command* clone() const override { return new SpriteSizeCommand(*this); }
-
-protected:
-  bool onEnabled(Context* context);
-  void onExecute(Context* context);
-
-private:
-  void onLockRatioClick();
-  void onWidthPxChange();
-  void onHeightPxChange();
-  void onWidthPercChange();
-  void onHeightPercChange();
-
-  CheckBox* m_lockRatio;
-  Entry* m_widthPx;
-  Entry* m_heightPx;
-  Entry* m_widthPerc;
-  Entry* m_heightPerc;
-};
-
 SpriteSizeCommand::SpriteSizeCommand()
   : Command("SpriteSize",
             "Sprite Size",
             CmdRecordableFlag)
 {
+  m_width = 0;
+  m_height = 0;
+  m_scaleX = 1.0;
+  m_scaleY = 1.0;
+  m_resizeMethod = raster::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
+}
+
+Command* SpriteSizeCommand::clone() const
+{
+  return new SpriteSizeCommand(*this);
+}
+
+void SpriteSizeCommand::onLoadParams(Params* params)
+{
+  std::string width = params->get("width");
+  if (!width.empty()) {
+    m_width = std::strtol(width.c_str(), NULL, 10);
+  }
+  else
+    m_width = 0;
+
+  std::string height = params->get("height");
+  if (!height.empty()) {
+    m_height = std::strtol(height.c_str(), NULL, 10);
+  }
+  else
+    m_height = 0;
+
+  std::string resize_method = params->get("resize-method");
+  if (!resize_method.empty()) {
+    if (resize_method == "bilinear")
+      m_resizeMethod = raster::algorithm::RESIZE_METHOD_BILINEAR;
+    else
+      m_resizeMethod = raster::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
+  }
+  else
+    m_resizeMethod = raster::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
 }
 
 bool SpriteSizeCommand::onEnabled(Context* context)
@@ -197,58 +213,63 @@ bool SpriteSizeCommand::onEnabled(Context* context)
 
 void SpriteSizeCommand::onExecute(Context* context)
 {
-  const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
+  const ContextReader reader(context);
   const Sprite* sprite(reader.sprite());
-
-  // load the window widget
-  base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size"));
-  m_widthPx = app::find_widget<Entry>(window, "width_px");
-  m_heightPx = app::find_widget<Entry>(window, "height_px");
-  m_widthPerc = app::find_widget<Entry>(window, "width_perc");
-  m_heightPerc = app::find_widget<Entry>(window, "height_perc");
-  m_lockRatio = app::find_widget<CheckBox>(window, "lock_ratio");
-  ComboBox* method = app::find_widget<ComboBox>(window, "method");
-  Widget* ok = app::find_widget<Widget>(window, "ok");
-
-  m_widthPx->setTextf("%d", sprite->width());
-  m_heightPx->setTextf("%d", sprite->height());
-
-  m_lockRatio->Click.connect(Bind<void>(&SpriteSizeCommand::onLockRatioClick, this));
-  m_widthPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPxChange, this));
-  m_heightPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPxChange, this));
-  m_widthPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPercChange, this));
-  m_heightPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPercChange, this));
-
-  method->addItem("Nearest-neighbor");
-  method->addItem("Bilinear");
-  method->setSelectedItemIndex(get_config_int("SpriteSize", "Method",
-                                              raster::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR));
-
-  window->remapWindow();
-  window->centerWindow();
-
-  load_window_pos(window, "SpriteSize");
-  window->setVisible(true);
-  window->openWindowInForeground();
-  save_window_pos(window, "SpriteSize");
-
-  if (window->getKiller() == ok) {
-    int new_width = m_widthPx->getTextInt();
-    int new_height = m_heightPx->getTextInt();
-    ResizeMethod resize_method =
-      (ResizeMethod)method->getSelectedItemIndex();
+  int new_width = (m_width ? m_width: sprite->width()*m_scaleX);
+  int new_height = (m_height ? m_height: sprite->height()*m_scaleY);
+  ResizeMethod resize_method = m_resizeMethod;
+
+  if (context->isUiAvailable()) {
+    // load the window widget
+    base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size"));
+    m_widthPx = app::find_widget<Entry>(window, "width_px");
+    m_heightPx = app::find_widget<Entry>(window, "height_px");
+    m_widthPerc = app::find_widget<Entry>(window, "width_perc");
+    m_heightPerc = app::find_widget<Entry>(window, "height_perc");
+    m_lockRatio = app::find_widget<CheckBox>(window, "lock_ratio");
+    ComboBox* method = app::find_widget<ComboBox>(window, "method");
+    Widget* ok = app::find_widget<Widget>(window, "ok");
+
+    m_widthPx->setTextf("%d", new_width);
+    m_heightPx->setTextf("%d", new_height);
+
+    m_lockRatio->Click.connect(Bind<void>(&SpriteSizeCommand::onLockRatioClick, this));
+    m_widthPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPxChange, this));
+    m_heightPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPxChange, this));
+    m_widthPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPercChange, this));
+    m_heightPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPercChange, this));
+
+    method->addItem("Nearest-neighbor");
+    method->addItem("Bilinear");
+    method->setSelectedItemIndex(get_config_int("SpriteSize", "Method",
+        raster::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR));
+
+    window->remapWindow();
+    window->centerWindow();
+
+    load_window_pos(window, "SpriteSize");
+    window->setVisible(true);
+    window->openWindowInForeground();
+    save_window_pos(window, "SpriteSize");
+
+    if (window->getKiller() != ok)
+      return;
+
+    new_width = m_widthPx->getTextInt();
+    new_height = m_heightPx->getTextInt();
+    resize_method = (ResizeMethod)method->getSelectedItemIndex();
 
     set_config_int("SpriteSize", "Method", resize_method);
+  }
 
-    {
-      SpriteSizeJob job(reader, new_width, new_height, resize_method);
-      job.startJob();
-      job.waitJob();
-    }
-
-    ContextWriter writer(reader);
-    update_screen_for_document(writer.document());
+  {
+    SpriteSizeJob job(reader, new_width, new_height, resize_method);
+    job.startJob();
+    job.waitJob();
   }
+
+  ContextWriter writer(reader);
+  update_screen_for_document(writer.document());
 }
 
 void SpriteSizeCommand::onLockRatioClick()
diff --git a/src/app/commands/cmd_sprite_size.h b/src/app/commands/cmd_sprite_size.h
new file mode 100644
index 0000000..11887c6
--- /dev/null
+++ b/src/app/commands/cmd_sprite_size.h
@@ -0,0 +1,72 @@
+/* Aseprite
+ * Copyright (C) 2001-2014  David Capello
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef APP_COMMANDS_CMD_SPRITE_SIZE_H_INCLUDED
+#define APP_COMMANDS_CMD_SPRITE_SIZE_H_INCLUDED
+#pragma once
+
+#include "app/commands/command.h"
+#include "raster/algorithm/resize_image.h"
+
+#include <string>
+
+namespace ui {
+  class CheckBox;
+  class Entry;
+}
+
+namespace app {
+
+  class SpriteSizeCommand : public Command {
+  public:
+    SpriteSizeCommand();
+    Command* clone() const override;
+
+    void setScale(double x, double y) {
+      m_scaleX = x;
+      m_scaleY = y;
+    }
+
+  protected:
+    virtual void onLoadParams(Params* params) override;
+    virtual bool onEnabled(Context* context) override;
+    virtual void onExecute(Context* context) override;
+
+  private:
+    void onLockRatioClick();
+    void onWidthPxChange();
+    void onHeightPxChange();
+    void onWidthPercChange();
+    void onHeightPercChange();
+
+    ui::CheckBox* m_lockRatio;
+    ui::Entry* m_widthPx;
+    ui::Entry* m_heightPx;
+    ui::Entry* m_widthPerc;
+    ui::Entry* m_heightPerc;
+
+    int m_width;
+    int m_height;
+    double m_scaleX;
+    double m_scaleY;
+    raster::algorithm::ResizeMethod m_resizeMethod;
+  };
+
+} // namespace app
+
+#endif
diff --git a/src/app/document_exporter.cpp b/src/app/document_exporter.cpp
index e5c4626..0034748 100644
--- a/src/app/document_exporter.cpp
+++ b/src/app/document_exporter.cpp
@@ -35,6 +35,7 @@
 #include "raster/image.h"
 #include "raster/layer.h"
 #include "raster/palette.h"
+#include "raster/primitives.h"
 #include "raster/sprite.h"
 #include "raster/stock.h"
 
@@ -48,16 +49,18 @@ namespace app {
 
 class DocumentExporter::Sample {
 public:
-  Sample(Document* document, Sprite* sprite,
+  Sample(Document* document, Sprite* sprite, Layer* layer,
     FrameNumber frame, const std::string& filename) :
     m_document(document),
     m_sprite(sprite),
+    m_layer(layer),
     m_frame(frame),
     m_filename(filename) {
   }
 
   Document* document() const { return m_document; }
   Sprite* sprite() const { return m_sprite; }
+  Layer* layer() const { return m_layer; }
   FrameNumber frame() const { return m_frame; }
   std::string filename() const { return m_filename; }
   const gfx::Size& originalSize() const { return m_originalSize; }
@@ -78,6 +81,7 @@ public:
 private:
   Document* m_document;
   Sprite* m_sprite;
+  Layer* m_layer;
   FrameNumber m_frame;
   std::string m_filename;
   gfx::Size m_originalSize;
@@ -117,27 +121,29 @@ class DocumentExporter::SimpleLayoutSamples :
 public:
   void layoutSamples(Samples& samples) override {
     const Sprite* oldSprite = NULL;
+    const Layer* oldLayer = NULL;
 
     gfx::Point framePt(0, 0);
-    for (Samples::iterator it=samples.begin(), end=samples.end();
-         it != end; ++it) {
-      const Sprite* sprite = it->sprite();
+    for (auto& sample : samples) {
+      const Sprite* sprite = sample.sprite();
+      const Layer* layer = sample.layer();
       gfx::Size size(sprite->width(), sprite->height());
 
-      it->setOriginalSize(size);
-      it->setTrimmedBounds(gfx::Rect(gfx::Point(0, 0), size));
-      it->setInTextureBounds(gfx::Rect(framePt, size));
-
-      // All frames of each sprite in one row.
-      if (oldSprite != NULL && oldSprite != it->sprite()) {
+      // New sprite or layer, go to next row.
+      if (oldSprite && (oldSprite != sprite || oldLayer != layer)) {
         framePt.x = 0;
         framePt.y += size.h;
       }
-      else {
-        framePt.x += size.w;
-      }
 
-      oldSprite = it->sprite();
+      sample.setOriginalSize(size);
+      sample.setTrimmedBounds(gfx::Rect(gfx::Point(0, 0), size));
+      sample.setInTextureBounds(gfx::Rect(framePt, size));
+
+      // Next frame position.
+      framePt.x += size.w;
+
+      oldSprite = sprite;
+      oldLayer = layer;
     }
   }
 };
@@ -174,7 +180,8 @@ void DocumentExporter::exportSheet()
     createEmptyTexture(samples));
 
   Sprite* texture = textureDocument->sprite();
-  Image* textureImage = static_cast<LayerImage*>(texture->folder()->getFirstLayer())
+  Image* textureImage = static_cast<LayerImage*>(
+    texture->folder()->getFirstLayer())
     ->getCel(FrameNumber(0))->image();
 
   renderTexture(samples, textureImage);
@@ -193,15 +200,14 @@ void DocumentExporter::captureSamples(Samples& samples)
 {
   std::vector<char> buf(32);
 
-  for (std::vector<Document*>::iterator
-         it = m_documents.begin(),
-         end = m_documents.end(); it != end; ++it) {
-    Document* document = *it;
-    Sprite* sprite = document->sprite();
-    
+  for (auto& item : m_documents) {
+    Document* doc = item.doc;
+    Sprite* sprite = doc->sprite();
+    Layer* layer = item.layer;
+
     for (FrameNumber frame=FrameNumber(0);
          frame<sprite->totalFrames(); ++frame) {
-      std::string filename = document->filename();
+      std::string filename = doc->filename();
 
       if (sprite->totalFrames() > FrameNumber(1)) {
         int frameNumWidth =
@@ -212,11 +218,16 @@ void DocumentExporter::captureSamples(Samples& samples)
 
         std::string path = base::get_file_path(filename);
         std::string title = base::get_file_title(filename);
+        if (layer) {
+          title += "-";
+          title += layer->name();
+        }
+
         std::string ext = base::get_file_extension(filename);
         filename = base::join_path(path, title + &buf[0] + "." + ext);
       }
 
-      samples.addSample(Sample(document, sprite, frame, filename));
+      samples.addSample(Sample(doc, sprite, layer, frame, filename));
     }
   }
 }
@@ -268,21 +279,26 @@ void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage
 {
   textureImage->clear(0);
 
-  for (Samples::const_iterator
-         it = samples.begin(),
-         end = samples.end(); it != end; ++it) {
+  for (const auto& sample : samples) {
     // Make the sprite compatible with the texture so the render()
     // works correctly.
-    if (it->sprite()->pixelFormat() != textureImage->pixelFormat()) {
-      DocumentApi docApi(it->document(), NULL); // DocumentApi without undo
-      docApi.setPixelFormat(it->sprite(), textureImage->pixelFormat(),
+    if (sample.sprite()->pixelFormat() != textureImage->pixelFormat()) {
+      DocumentApi docApi(sample.document(), NULL); // DocumentApi without undo
+      docApi.setPixelFormat(
+        sample.sprite(),
+        textureImage->pixelFormat(),
         DITHERING_NONE);
     }
 
-    it->sprite()->render(textureImage,
-      it->inTextureBounds().x - it->trimmedBounds().x,
-      it->inTextureBounds().y - it->trimmedBounds().y,
-      it->frame());
+    int x = sample.inTextureBounds().x - sample.trimmedBounds().x;
+    int y = sample.inTextureBounds().y - sample.trimmedBounds().y;
+
+    if (sample.layer()) {
+      layer_render(sample.layer(), textureImage, x, y, sample.frame());
+    }
+    else {
+      sample.sprite()->render(textureImage, x, y, sample.frame());
+    }
   }
 }
 
@@ -292,18 +308,19 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
   for (Samples::const_iterator
          it = samples.begin(),
          end = samples.end(); it != end; ) {
-    gfx::Size srcSize = it->originalSize();
-    gfx::Rect spriteSourceBounds = it->trimmedBounds();
-    gfx::Rect frameBounds = it->inTextureBounds();
+    const Sample& sample = *it;
+    gfx::Size srcSize = sample.originalSize();
+    gfx::Rect spriteSourceBounds = sample.trimmedBounds();
+    gfx::Rect frameBounds = sample.inTextureBounds();
 
-    os << "   \"" << it->filename() << "\": {\n"
+    os << "   \"" << sample.filename() << "\": {\n"
        << "    \"frame\": { "
        << "\"x\": " << frameBounds.x << ", "
        << "\"y\": " << frameBounds.y << ", "
        << "\"w\": " << frameBounds.w << ", "
        << "\"h\": " << frameBounds.h << " },\n"
        << "    \"rotated\": false,\n"
-       << "    \"trimmed\": " << (it->trimmed() ? "true": "false") << ",\n"
+       << "    \"trimmed\": " << (sample.trimmed() ? "true": "false") << ",\n"
        << "    \"spriteSourceSize\": { "
        << "\"x\": " << spriteSourceBounds.x << ", "
        << "\"y\": " << spriteSourceBounds.y << ", "
@@ -312,7 +329,7 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
        << "    \"sourceSize\": { "
        << "\"w\": " << srcSize.w << ", "
        << "\"h\": " << srcSize.h << " },\n"
-       << "    \"duration\": " << it->sprite()->getFrameDuration(it->frame()) << "\n"
+       << "    \"duration\": " << sample.sprite()->getFrameDuration(sample.frame()) << "\n"
        << "   }";
 
     if (++it != samples.end())
diff --git a/src/app/document_exporter.h b/src/app/document_exporter.h
index 434478c..df7e097 100644
--- a/src/app/document_exporter.h
+++ b/src/app/document_exporter.h
@@ -29,6 +29,7 @@
 
 namespace raster {
   class Image;
+  class Layer;
 }
 
 namespace app {
@@ -53,7 +54,8 @@ namespace app {
     DocumentExporter() :
       m_dataFormat(DefaultDataFormat),
       m_textureFormat(DefaultTextureFormat),
-      m_scaleMode(DefaultScaleMode) {
+      m_scaleMode(DefaultScaleMode),
+      m_scale(1.0) {
     }
 
     void setDataFormat(DataFormat format) {
@@ -80,8 +82,8 @@ namespace app {
       m_scaleMode = mode;
     }
 
-    void addDocument(Document* document) {
-      m_documents.push_back(document);
+    void addDocument(Document* document, raster::Layer* layer = NULL) {
+      m_documents.push_back(Item(document, layer));
     }
 
     void exportSheet();
@@ -97,13 +99,23 @@ namespace app {
     void renderTexture(const Samples& samples, raster::Image* textureImage);
     void createDataFile(const Samples& samples, std::ostream& os, raster::Image* textureImage);
 
+    class Item {
+    public:
+      Document* doc;
+      raster::Layer* layer;
+      Item(Document* doc, raster::Layer* layer)
+        : doc(doc), layer(layer) {
+      }
+    };
+    typedef std::vector<Item> Items;
+
     DataFormat m_dataFormat;
     std::string m_dataFilename;
     TextureFormat m_textureFormat;
     std::string m_textureFilename;
     double m_scale;
     ScaleMode m_scaleMode;
-    std::vector<Document*> m_documents;
+    Items m_documents;
 
     DISABLE_COPYING(DocumentExporter);
   };
diff --git a/src/app/job.cpp b/src/app/job.cpp
index 1ea0472..51640c7 100644
--- a/src/app/job.cpp
+++ b/src/app/job.cpp
@@ -22,6 +22,7 @@
 
 #include "app/job.h"
 
+#include "app/app.h"
 #include "app/ui/status_bar.h"
 #include "base/mutex.h"
 #include "base/scoped_lock.h"
@@ -34,7 +35,7 @@ static const int kMonitoringPeriod = 100;
 
 namespace app {
 
-Job::Job(const char* job_name)
+Job::Job(const char* jobName)
 {
   m_mutex = NULL;
   m_thread = NULL;
@@ -44,24 +45,29 @@ Job::Job(const char* job_name)
   m_canceled_flag = false;
 
   m_mutex = new base::mutex();
-  m_progress = StatusBar::instance()->addProgress();
-  m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", job_name);
 
-  m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window));
-  m_timer->Tick.connect(&Job::onMonitoringTick, this);
-  m_timer->start();
+  if (App::instance()->isGui()) {
+    m_progress = StatusBar::instance()->addProgress();
+    m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", jobName);
+
+    m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window));
+    m_timer->Tick.connect(&Job::onMonitoringTick, this);
+    m_timer->start();
+  }
 }
 
 Job::~Job()
 {
-  ASSERT(!m_timer->isRunning());
-  ASSERT(m_thread == NULL);
+  if (App::instance()->isGui()) {
+    ASSERT(!m_timer->isRunning());
+    ASSERT(m_thread == NULL);
 
-  if (m_alert_window != NULL)
-    m_alert_window->closeWindow(NULL);
+    if (m_alert_window != NULL)
+      m_alert_window->closeWindow(NULL);
 
-  if (m_progress)
-    delete m_progress;
+    if (m_progress)
+      delete m_progress;
+  }
 
   if (m_mutex)
     delete m_mutex;
@@ -70,19 +76,22 @@ Job::~Job()
 void Job::startJob()
 {
   m_thread = new base::thread(&Job::thread_proc, this);
-  m_alert_window->openWindowInForeground();
 
-  // The job was canceled by the user?
-  {
-    base::scoped_lock hold(*m_mutex);
-    if (!m_done_flag)
-      m_canceled_flag = true;
+  if (m_alert_window) {
+    m_alert_window->openWindowInForeground();
+
+    // The job was canceled by the user?
+    {
+      base::scoped_lock hold(*m_mutex);
+      if (!m_done_flag)
+        m_canceled_flag = true;
+    }
   }
 }
 
 void Job::waitJob()
 {
-  if (m_timer->isRunning())
+  if (m_timer && m_timer->isRunning())
     m_timer->stop();
 
   if (m_thread) {
diff --git a/src/app/job.h b/src/app/job.h
index 61fac80..a1067dc 100644
--- a/src/app/job.h
+++ b/src/app/job.h
@@ -34,7 +34,7 @@ namespace app {
 
   class Job {
   public:
-    Job(const char* job_name);
+    Job(const char* jobName);
     virtual ~Job();
 
     // Starts the job calling onJob() event in another thread and
diff --git a/src/app/modules/gui.cpp b/src/app/modules/gui.cpp
index d307d07..f6617f9 100644
--- a/src/app/modules/gui.cpp
+++ b/src/app/modules/gui.cpp
@@ -228,7 +228,8 @@ void update_screen_for_document(Document* document)
     // Well, change to the default palette.
     if (set_current_palette(NULL, false)) {
       // If the palette changes, refresh the whole screen.
-      Manager::getDefault()->invalidate();
+      if (Manager::getDefault())
+        Manager::getDefault()->invalidate();
     }
   }
   // With a document.
diff --git a/src/app/ui_context.cpp b/src/app/ui_context.cpp
index ba6cf86..13c46cd 100644
--- a/src/app/ui_context.cpp
+++ b/src/app/ui_context.cpp
@@ -22,6 +22,7 @@
 
 #include "app/app.h"
 #include "app/document.h"
+#include "app/document_location.h"
 #include "app/modules/editors.h"
 #include "app/settings/ui_settings_impl.h"
 #include "app/ui/color_bar.h"
@@ -167,6 +168,10 @@ void UIContext::onRemoveDocument(doc::Document* doc)
 {
   Context::onRemoveDocument(doc);
 
+  // We don't destroy views in batch mode.
+  if (!isUiAvailable())
+    return;
+
   Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
   DocumentViews docViews;
 
@@ -190,8 +195,16 @@ void UIContext::onRemoveDocument(doc::Document* doc)
 void UIContext::onGetActiveLocation(DocumentLocation* location) const
 {
   DocumentView* view = activeView();
-  if (view)
+  if (view) {
     view->getDocumentLocation(location);
+  }
+  // Default/dummy location (maybe for batch/command line mode)
+  else if (Document* doc = activeDocument()) {
+    location->document(doc);
+    location->sprite(doc->sprite());
+    location->layer(doc->sprite()->indexToLayer(LayerIndex(0)));
+    location->frame(FrameNumber(0));
+  }
 }
 
 } // namespace app
diff --git a/src/base/program_options.cpp b/src/base/program_options.cpp
index 1ef589a..addc4d7 100644
--- a/src/base/program_options.cpp
+++ b/src/base/program_options.cpp
@@ -192,8 +192,8 @@ std::ostream& operator<<(std::ostream& os, const base::ProgramOptions& po)
          it=po.options().begin(), end=po.options().end(); it != end; ++it) {
     const base::ProgramOptions::Option* option = *it;
     size_t optionWidth =
-      std::min<int>(26, 6+option->name().size()+1+
-                        (option->doesRequireValue() ? option->getValueName().size()+1: 0));
+      6+option->name().size()+1+
+      (option->doesRequireValue() ? option->getValueName().size()+1: 0);
 
     if (maxOptionWidth < optionWidth)
       maxOptionWidth = optionWidth;
diff --git a/src/doc/documents.h b/src/doc/documents.h
index 248f8cd..f589dd7 100644
--- a/src/doc/documents.h
+++ b/src/doc/documents.h
@@ -35,6 +35,7 @@ namespace doc {
 
     Document* front() const { return m_docs.front(); }
     Document* back() const { return m_docs.back(); }
+    Document* lastAdded() const { return front(); }
 
     int size() const { return m_docs.size(); }
     bool empty() const { return m_docs.empty(); }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git



More information about the Pkg-games-commits mailing list