mirror of https://github.com/aseprite/aseprite.git
				
				
				
			Merge branch '1.0'
Conflicts: src/app/commands/cmd_sprite_size.cpp src/app/document_exporter.cpp
This commit is contained in:
		
						commit
						5e2cefe212
					
				|  | @ -57,7 +57,7 @@ https://github.com/aseprite/aseprite | |||
| 
 | ||||
| You can clone it locally using the following command (read-only URL): | ||||
| 
 | ||||
|     git clone git://github.com/aseprite/aseprite.git | ||||
|     git clone --recursive https://github.com/aseprite/aseprite.git | ||||
| 
 | ||||
| On Windows you can use programs like | ||||
| [msysgit](http://msysgit.github.io/) to clone the repository. | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ | |||
|           </hbox> | ||||
|           <check text="Show timeline automatically" id="autotimeline" tooltip="Show the timeline automatically
when a new frame or layer is added." /> | ||||
|           <check text="Expand menu bar items on mouseover" id="expand_menubar_on_mouseover" tooltip="Check this option to get
this old menus behavior." /> | ||||
|           <check text="Center editor when zoom with keys or zoom tool" id="center_on_zoom" /> | ||||
|           <separator horizontal="true" /> | ||||
|           <link id="locate_file" text="Locate Configuration File" /> | ||||
|           <link id="locate_crash_folder" text="Locate Crash Folder" /> | ||||
|  |  | |||
|  | @ -153,6 +153,46 @@ static RETSIGTYPE osx_signal_handler(int num) | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static BOOL handle_mouse_enter( | ||||
|   NSRect frame, NSRect view, NSPoint point, | ||||
|   int* mx, int* my, int* buttons) | ||||
| { | ||||
|   if (_mouse_installed && !_mouse_on && | ||||
|       (osx_window) && (NSPointInRect(point, view))) { | ||||
|     *mx = point.x; | ||||
|     *my = frame.size.height - point.y; | ||||
|     *buttons = 0; | ||||
| 
 | ||||
|     _mouse_on = TRUE; | ||||
|     osx_hide_native_mouse(); | ||||
|     if (osx_mouse_enter_callback) | ||||
|       osx_mouse_enter_callback(); | ||||
| 
 | ||||
|     return YES; | ||||
|   } | ||||
|   else | ||||
|     return NO; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static BOOL handle_mouse_leave() | ||||
| { | ||||
|   if (_mouse_installed) { | ||||
|     if (_mouse_on) { | ||||
|       _mouse_on = FALSE; | ||||
|       osx_show_native_mouse(); | ||||
|       if (osx_mouse_leave_callback) | ||||
|         osx_mouse_leave_callback(); | ||||
|     } | ||||
|     return YES; | ||||
|   } | ||||
|   else | ||||
|     return NO; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* osx_event_handler: | ||||
|  *  Event handling function; gets repeatedly called inside a dedicated thread. | ||||
|  */ | ||||
|  | @ -164,8 +204,8 @@ void osx_event_handler() | |||
|    NSPoint point; | ||||
|    NSRect frame, view; | ||||
|    int dx = 0, dy = 0, dz = 0; | ||||
|    int mx=_mouse_x; | ||||
|    int my=_mouse_y; | ||||
|    int mx = _mouse_x; | ||||
|    int my = _mouse_y; | ||||
|    static int buttons = 0; | ||||
|    int event_type; | ||||
|    BOOL gotmouseevent = NO; | ||||
|  | @ -225,19 +265,10 @@ void osx_event_handler() | |||
|          case NSLeftMouseDown: | ||||
|          case NSOtherMouseDown: | ||||
|          case NSRightMouseDown: | ||||
|             if (![NSApp isActive]) { | ||||
|             /* App is regaining focus */ | ||||
|                if (_mouse_installed) { | ||||
|                   if ((osx_window) && (NSPointInRect(point, view))) { | ||||
|                      mx = point.x; | ||||
|                      my = frame.size.height - point.y; | ||||
|                      buttons = 0; | ||||
|                      if (!_mouse_on) { | ||||
|                        _mouse_on = TRUE; | ||||
|                        osx_hide_native_mouse(); | ||||
|                      } | ||||
|                   } | ||||
|                } | ||||
|             if (![NSApp isActive]) { | ||||
|                handle_mouse_enter(frame, view, point, &mx, &my, &buttons); | ||||
| 
 | ||||
|                if (osx_window) | ||||
|                   [osx_window invalidateCursorRectsForView: [osx_window contentView]]; | ||||
|                if (_keyboard_installed) | ||||
|  | @ -259,6 +290,9 @@ void osx_event_handler() | |||
|          case NSLeftMouseUp: | ||||
|          case NSOtherMouseUp: | ||||
|          case NSRightMouseUp: | ||||
|             if ([NSApp isActive]) | ||||
|                handle_mouse_enter(frame, view, point, &mx, &my, &buttons); | ||||
| 
 | ||||
|             buttons &= ~((event_type == NSLeftMouseUp) ? 0x1 : 0); | ||||
|             buttons &= ~((event_type == NSRightMouseUp) ? 0x2 : 0); | ||||
|             buttons &= ~((event_type == NSOtherMouseUp) ? 0x4 : 0); | ||||
|  | @ -271,11 +305,14 @@ void osx_event_handler() | |||
|          case NSRightMouseDragged: | ||||
|          case NSOtherMouseDragged: | ||||
|          case NSMouseMoved: | ||||
|             if ([NSApp isActive]) | ||||
|                handle_mouse_enter(frame, view, point, &mx, &my, &buttons); | ||||
| 
 | ||||
|             dx += [event deltaX]; | ||||
|             dy += [event deltaY]; | ||||
| 
 | ||||
|             mx=point.x; | ||||
|             my=frame.size.height-point.y; | ||||
|             mx = point.x; | ||||
|             my = frame.size.height - point.y; | ||||
| 
 | ||||
|             [NSApp sendEvent: event]; | ||||
|             gotmouseevent = YES; | ||||
|  | @ -288,35 +325,16 @@ void osx_event_handler() | |||
| 
 | ||||
|          case NSMouseEntered: | ||||
|             if (([event trackingNumber] == osx_mouse_tracking_rect) && ([NSApp isActive])) { | ||||
|                if (_mouse_installed) { | ||||
|                   mx = point.x; | ||||
|                   my = frame.size.height - point.y; | ||||
|                   buttons = 0; | ||||
|                if (handle_mouse_enter(frame, view, point, &mx, &my, &buttons)) | ||||
|                   gotmouseevent = YES; | ||||
|                   if (!_mouse_on) { | ||||
|                     _mouse_on = TRUE; | ||||
|                     osx_hide_native_mouse(); | ||||
|                   } | ||||
| 
 | ||||
|                   if (osx_mouse_enter_callback) | ||||
|                     osx_mouse_enter_callback(); | ||||
|                } | ||||
|             } | ||||
|             [NSApp sendEvent: event]; | ||||
|             break; | ||||
| 
 | ||||
|          case NSMouseExited: | ||||
|             if ([event trackingNumber] == osx_mouse_tracking_rect) { | ||||
|                if (_mouse_installed) { | ||||
|                if (handle_mouse_leave()) | ||||
|                   gotmouseevent = YES; | ||||
|                   if (_mouse_on) { | ||||
|                     _mouse_on = FALSE; | ||||
|                     osx_show_native_mouse(); | ||||
|                   } | ||||
| 
 | ||||
|                   if (osx_mouse_leave_callback) | ||||
|                     osx_mouse_leave_callback(); | ||||
|                } | ||||
|             } | ||||
|             [NSApp sendEvent: event]; | ||||
|             break; | ||||
|  | @ -328,6 +346,8 @@ void osx_event_handler() | |||
|                      [osx_window invalidateCursorRectsForView: [osx_window contentView]]; | ||||
|                      if (_keyboard_installed) | ||||
|                         osx_keyboard_focused(TRUE, 0); | ||||
| 
 | ||||
|                      handle_mouse_enter(frame, view, point, &mx, &my, &buttons); | ||||
|                   } | ||||
|                   _switch_in(); | ||||
|                   break; | ||||
|  | @ -335,6 +355,7 @@ void osx_event_handler() | |||
|                case NSApplicationDeactivatedEventType: | ||||
|                   if (osx_window && _keyboard_installed) | ||||
|                      osx_keyboard_focused(FALSE, 0); | ||||
|                   handle_mouse_leave(); | ||||
|                   _switch_out(); | ||||
|                   break; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										178
									
								
								src/app/app.cpp
								
								
								
								
							
							
						
						
									
										178
									
								
								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" | ||||
|  | @ -111,9 +113,7 @@ public: | |||
| 
 | ||||
| App* App::m_instance = NULL; | ||||
| 
 | ||||
| // Initializes the application loading the modules, setting the
 | ||||
| // graphics mode, loading the configuration and resources, etc.
 | ||||
| App::App(int argc, const char* argv[]) | ||||
| App::App() | ||||
|   : m_modules(NULL) | ||||
|   , m_legacy(NULL) | ||||
|   , m_isGui(false) | ||||
|  | @ -122,23 +122,22 @@ App::App(int argc, const char* argv[]) | |||
| { | ||||
|   ASSERT(m_instance == NULL); | ||||
|   m_instance = this; | ||||
| } | ||||
| 
 | ||||
| void App::initialize(int argc, const char* argv[]) | ||||
| { | ||||
|   AppOptions options(argc, argv); | ||||
| 
 | ||||
|   // Initializes the application loading the modules, setting the
 | ||||
|   // graphics mode, loading the configuration and resources, etc.
 | ||||
|   m_modules = new Modules(!options.startUI(), options.verbose()); | ||||
|   m_isGui = options.startUI(); | ||||
|   m_isShell = options.startShell(); | ||||
|   m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0); | ||||
|   m_files = options.files(); | ||||
| 
 | ||||
|   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(); | ||||
| 
 | ||||
|  | @ -177,20 +176,16 @@ App::App(int argc, const char* argv[]) | |||
| 
 | ||||
|   // Set system palette to the default one.
 | ||||
|   set_current_palette(NULL, true); | ||||
| } | ||||
| 
 | ||||
| int App::run() | ||||
| { | ||||
|   UIContext* context = UIContext::instance(); | ||||
| 
 | ||||
|   // Initialize GUI interface
 | ||||
|   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); | ||||
| 
 | ||||
|  | @ -215,12 +210,87 @@ int App::run() | |||
|   // Procress options
 | ||||
|   PRINTF("Processing options...\n"); | ||||
| 
 | ||||
|   { | ||||
|   // Open file specified in the command line
 | ||||
|   if (!options.values().empty()) { | ||||
|     Console console; | ||||
|     for (const std::string& filename : m_files) { | ||||
|     bool splitLayers = false; | ||||
|     std::string importLayer; | ||||
| 
 | ||||
|     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()); | ||||
|         } | ||||
|         // --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.sheetPack()) { | ||||
|           if (m_exporter) | ||||
|             m_exporter->setTexturePack(true); | ||||
|         } | ||||
|         // --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 { | ||||
|         const std::string& filename = value.value(); | ||||
| 
 | ||||
|         // Load the sprite
 | ||||
|       Document* document = load_document(context, filename.c_str()); | ||||
|       if (!document) { | ||||
|         Document* doc = load_document(ctx, filename.c_str()); | ||||
|         if (!doc) { | ||||
|           if (!isGui()) | ||||
|             console.printf("Error loading file \"%s\"\n", filename.c_str()); | ||||
|         } | ||||
|  | @ -231,9 +301,36 @@ int App::run() | |||
|           if (isGui()) | ||||
|             getRecentFiles()->addRecentFile(filename.c_str()); | ||||
| 
 | ||||
|         // Add the document to the exporter.
 | ||||
|         if (m_exporter != NULL) | ||||
|           m_exporter->addDocument(document); | ||||
|           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; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | @ -245,7 +342,10 @@ int App::run() | |||
|     m_exporter->exportSheet(); | ||||
|     m_exporter.reset(NULL); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void App::run() | ||||
| { | ||||
|   // Run the GUI
 | ||||
|   if (isGui()) { | ||||
| #ifdef ENABLE_UPDATER | ||||
|  | @ -265,6 +365,20 @@ int App::run() | |||
| 
 | ||||
|     // Run the GUI main message loop
 | ||||
|     gui_run(); | ||||
|   } | ||||
| 
 | ||||
|   // Start shell to execute scripts.
 | ||||
|   if (m_isShell) { | ||||
|     m_systemConsole.prepareShell(); | ||||
| 
 | ||||
|     if (m_modules->m_scriptingEngine.supportEval()) { | ||||
|       Shell shell; | ||||
|       shell.run(m_modules->m_scriptingEngine); | ||||
|     } | ||||
|     else { | ||||
|       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(); | ||||
|  | @ -286,23 +400,10 @@ int App::run() | |||
|     delete doc; | ||||
|   } | ||||
| 
 | ||||
|   if (isGui()) { | ||||
|     // Destroy the window.
 | ||||
|     m_mainWindow.reset(NULL); | ||||
|   } | ||||
|   // Start shell to execute scripts.
 | ||||
|   else if (m_isShell) { | ||||
|     m_systemConsole.prepareShell(); | ||||
| 
 | ||||
|     if (m_modules->m_scriptingEngine.supportEval()) { | ||||
|       Shell shell; | ||||
|       shell.run(m_modules->m_scriptingEngine); | ||||
|     } | ||||
|     else { | ||||
|       std::cerr << "Your version of " PACKAGE " wasn't compiled with shell support.\n"; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| // Finishes the Aseprite application.
 | ||||
|  | @ -405,6 +506,7 @@ void app_refresh_screen() | |||
| 
 | ||||
| void app_rebuild_documents_tabs() | ||||
| { | ||||
|   if (App::instance()->isGui()) | ||||
|     App::instance()->getMainWindow()->getTabsBar()->updateTabsText(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ namespace app { | |||
| 
 | ||||
|   class App { | ||||
|   public: | ||||
|     App(int argc, const char* argv[]); | ||||
|     App(); | ||||
|     ~App(); | ||||
| 
 | ||||
|     static App* instance() { return m_instance; } | ||||
|  | @ -63,8 +63,10 @@ namespace app { | |||
|     bool isPortable(); | ||||
| 
 | ||||
|     // Runs the Aseprite application. In GUI mode it's the top-level
 | ||||
|     // window, in console/scripting it just runs the specified scripts.
 | ||||
|     int run(); | ||||
|     // window, in console/scripting it just runs the specified
 | ||||
|     // scripts.
 | ||||
|     void initialize(int argc, const char* argv[]); | ||||
|     void run(); | ||||
| 
 | ||||
|     tools::ToolBox* getToolBox() const; | ||||
|     RecentFiles* getRecentFiles() const; | ||||
|  |  | |||
|  | @ -289,9 +289,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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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,40 @@ 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 given document with other format")) | ||||
|   , m_scale(m_po.add("scale").requiresValue("<factor>").description("Resize all previous opened documents")) | ||||
|   , 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_sheetWidth(m_po.add("sheet-width").requiresValue("<pixels>").description("Sprite sheet width")) | ||||
|   , m_sheetHeight(m_po.add("sheet-height").requiresValue("<pixels>").description("Sprite sheet height")) | ||||
|   , m_sheetPack(m_po.add("sheet-pack").description("Use a packing algorithm to avoid waste of space\nin the texture")) | ||||
|   , m_splitLayers(m_po.add("split-layers").description("Import each layer of the next given sprite as\na 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")) | ||||
|   , 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 = verbose.enabled(); | ||||
|     m_paletteFileName = palette.value(); | ||||
|     m_startShell = shell.enabled(); | ||||
|     // m_dataFormat = dataFormat.value();
 | ||||
|     m_data = data.value(); | ||||
|     // m_textureFormat = textureFormat.value();
 | ||||
|     m_sheet = sheet.value(); | ||||
|     // if (scale.enabled())
 | ||||
|     //   m_scale = std::strtod(scale.value().c_str(), NULL);
 | ||||
|     // m_scaleMode = scaleMode.value();
 | ||||
|     m_verboseEnabled = m_po.enabled(m_verbose); | ||||
|     m_paletteFileName = m_po.value_of(m_palette); | ||||
|     m_startShell = m_po.enabled(m_shell); | ||||
| 
 | ||||
|     if (help.enabled()) { | ||||
|     if (m_po.enabled(m_help)) { | ||||
|       showHelp(); | ||||
|       m_startUI = false; | ||||
|     } | ||||
|     else if (version.enabled()) { | ||||
|     else if (m_po.enabled(m_version)) { | ||||
|       showVersion(); | ||||
|       m_startUI = false; | ||||
|     } | ||||
| 
 | ||||
|     if (shell.enabled() || batch.enabled()) { | ||||
|     if (m_po.enabled(m_shell) || m_po.enabled(m_batch)) { | ||||
|       m_startUI = false; | ||||
|     } | ||||
|   } | ||||
|  | @ -88,6 +79,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 | ||||
|  |  | |||
|  | @ -30,33 +30,34 @@ 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& files() 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; } | ||||
|   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& sheetWidth() const { return m_sheetWidth; } | ||||
|   const Option& sheetHeight() const { return m_sheetHeight; } | ||||
|   const Option& sheetPack() const { return m_sheetPack; } | ||||
|   const Option& splitLayers() const { return m_splitLayers; } | ||||
|   const Option& importLayer() const { return m_importLayer; } | ||||
| 
 | ||||
|   bool hasExporterParams() { | ||||
|     return | ||||
|       !m_dataFormat.empty() || | ||||
|       !m_data.empty() || | ||||
|       !m_textureFormat.empty() || | ||||
|       !m_sheet.empty(); | ||||
|   } | ||||
|   bool hasExporterParams() const; | ||||
| 
 | ||||
| private: | ||||
|   void showHelp(); | ||||
|  | @ -66,15 +67,26 @@ 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_sheetWidth; | ||||
|   Option& m_sheetHeight; | ||||
|   Option& m_sheetPack; | ||||
|   Option& m_splitLayers; | ||||
|   Option& m_importLayer; | ||||
| 
 | ||||
|   Option& m_verbose; | ||||
|   Option& m_help; | ||||
|   Option& m_version; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| } // namespace app
 | ||||
|  |  | |||
|  | @ -78,6 +78,9 @@ public: | |||
|         ui::MenuBar::expandOnMouseover())) | ||||
|       expandMenubarOnMouseover()->setSelected(true); | ||||
| 
 | ||||
|     if (get_config_bool("Editor", "CenterOnZoom", false)) | ||||
|       centerOnZoom()->setSelected(true); | ||||
| 
 | ||||
|     if (m_settings->experimental()->useNativeCursor()) | ||||
|       nativeCursor()->setSelected(true); | ||||
| 
 | ||||
|  | @ -154,6 +157,8 @@ public: | |||
|     set_config_bool("Options", "ExpandMenuBarOnMouseover", expandOnMouseover); | ||||
|     ui::MenuBar::setExpandOnMouseover(expandOnMouseover); | ||||
| 
 | ||||
|     set_config_bool("Editor", "CenterOnZoom", centerOnZoom()->isSelected()); | ||||
| 
 | ||||
|     m_settings->setShowSpriteEditorScrollbars(showScrollbars()->isSelected()); | ||||
|     m_settings->setZoomWithScrollWheel(wheelZoom()->isSelected()); | ||||
|     m_settings->setRightClickMode(static_cast<RightClickMode>(rightClickBehavior()->getSelectedItemIndex())); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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" | ||||
|  | @ -157,34 +159,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 = doc::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 = doc::algorithm::RESIZE_METHOD_BILINEAR; | ||||
|     else | ||||
|       m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR; | ||||
|   } | ||||
|   else | ||||
|     m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR; | ||||
| } | ||||
| 
 | ||||
| bool SpriteSizeCommand::onEnabled(Context* context) | ||||
|  | @ -195,9 +211,13 @@ 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()); | ||||
|   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"); | ||||
|  | @ -208,8 +228,8 @@ void SpriteSizeCommand::onExecute(Context* context) | |||
|     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_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)); | ||||
|  | @ -230,13 +250,15 @@ void SpriteSizeCommand::onExecute(Context* context) | |||
|     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(); | ||||
|     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); | ||||
|  | @ -246,7 +268,6 @@ void SpriteSizeCommand::onExecute(Context* context) | |||
| 
 | ||||
|   ContextWriter writer(reader); | ||||
|   update_screen_for_document(writer.document()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void SpriteSizeCommand::onLockRatioClick() | ||||
|  |  | |||
|  | @ -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 "doc/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; | ||||
|     doc::algorithm::ResizeMethod m_resizeMethod; | ||||
|   }; | ||||
| 
 | ||||
| } // namespace app
 | ||||
| 
 | ||||
| #endif | ||||
|  | @ -29,14 +29,16 @@ | |||
| #include "app/ui_context.h" | ||||
| #include "base/path.h" | ||||
| #include "base/unique_ptr.h" | ||||
| #include "gfx/size.h" | ||||
| #include "doc/cel.h" | ||||
| #include "doc/dithering_method.h" | ||||
| #include "doc/image.h" | ||||
| #include "doc/layer.h" | ||||
| #include "doc/palette.h" | ||||
| #include "doc/primitives.h" | ||||
| #include "doc/sprite.h" | ||||
| #include "doc/stock.h" | ||||
| #include "gfx/packing_rects.h" | ||||
| #include "gfx/size.h" | ||||
| 
 | ||||
| #include <cstdio> | ||||
| #include <fstream> | ||||
|  | @ -48,16 +50,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 +82,7 @@ public: | |||
| private: | ||||
|   Document* m_document; | ||||
|   Sprite* m_sprite; | ||||
|   Layer* m_layer; | ||||
|   FrameNumber m_frame; | ||||
|   std::string m_filename; | ||||
|   gfx::Size m_originalSize; | ||||
|  | @ -109,39 +114,99 @@ private: | |||
| class DocumentExporter::LayoutSamples { | ||||
| public: | ||||
|   virtual ~LayoutSamples() { } | ||||
|   virtual void layoutSamples(Samples& samples) = 0; | ||||
|   virtual void layoutSamples(Samples& samples, int& width, int& height) = 0; | ||||
| }; | ||||
| 
 | ||||
| class DocumentExporter::SimpleLayoutSamples : | ||||
|     public DocumentExporter::LayoutSamples { | ||||
| public: | ||||
|   void layoutSamples(Samples& samples) override { | ||||
|   void layoutSamples(Samples& samples, int& width, int& height) 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()) { | ||||
|       if (oldSprite) { | ||||
|           // If the user didn't specified a width for the texture, we put
 | ||||
|           // each sprite/layer in a different row.
 | ||||
|           if (width == 0) { | ||||
|               // New sprite or layer, go to next row.
 | ||||
|               if (oldSprite != sprite || oldLayer != layer) { | ||||
|                   framePt.x = 0; | ||||
|         framePt.y += size.h; | ||||
|                   framePt.y += oldSprite->height(); // We're skipping the previous sprite height
 | ||||
|               } | ||||
|           } | ||||
|           // When a texture width is specified, we can put different
 | ||||
|           // sprites/layers in each row until we reach the texture
 | ||||
|           // right-border.
 | ||||
|           else if (framePt.x+size.w > width) { | ||||
|               framePt.x = 0; | ||||
|               framePt.y += oldSprite->height(); | ||||
|               // TODO framePt.y+size.h > height ?
 | ||||
|           } | ||||
|       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; | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class DocumentExporter::BestFitLayoutSamples : | ||||
|     public DocumentExporter::LayoutSamples { | ||||
| public: | ||||
|   void layoutSamples(Samples& samples, int& width, int& height) override { | ||||
|     gfx::PackingRects pr; | ||||
| 
 | ||||
|     for (auto& sample : samples) { | ||||
|       const Sprite* sprite = sample.sprite(); | ||||
|       gfx::Size size(sprite->width(), sprite->height()); | ||||
| 
 | ||||
|       sample.setOriginalSize(size); | ||||
|       sample.setTrimmedBounds(gfx::Rect(gfx::Point(0, 0), size)); | ||||
| 
 | ||||
|       pr.add(size); | ||||
|     } | ||||
| 
 | ||||
|     if (width == 0 || height == 0) { | ||||
|       gfx::Size sz = pr.bestFit(); | ||||
|       width = sz.w; | ||||
|       height = sz.h; | ||||
|     } | ||||
|     else | ||||
|       pr.pack(gfx::Size(width, height)); | ||||
| 
 | ||||
|     auto it = samples.begin(); | ||||
|     for (auto& rc : pr) { | ||||
|       ASSERT(it != samples.end()); | ||||
|       it->setInTextureBounds(rc); | ||||
|       ++it; | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| DocumentExporter::DocumentExporter() | ||||
|  : m_dataFormat(DefaultDataFormat) | ||||
|  , m_textureFormat(DefaultTextureFormat) | ||||
|  , m_textureWidth(0) | ||||
|  , m_textureHeight(0) | ||||
|  , m_texturePack(false) | ||||
|  , m_scaleMode(DefaultScaleMode) | ||||
|  , m_scale(1.0) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void DocumentExporter::exportSheet() | ||||
| { | ||||
|   // We output the metadata to std::cout if the user didn't specify a file.
 | ||||
|  | @ -166,15 +231,22 @@ void DocumentExporter::exportSheet() | |||
|   } | ||||
| 
 | ||||
|   // 2) Layout those samples in a texture field.
 | ||||
|   if (m_texturePack) { | ||||
|     BestFitLayoutSamples layout; | ||||
|     layout.layoutSamples(samples, m_textureWidth, m_textureHeight); | ||||
|   } | ||||
|   else { | ||||
|     SimpleLayoutSamples layout; | ||||
|   layout.layoutSamples(samples); | ||||
|     layout.layoutSamples(samples, m_textureWidth, m_textureHeight); | ||||
|   } | ||||
| 
 | ||||
|   // 3) Create and render the texture.
 | ||||
|   base::UniquePtr<Document> textureDocument( | ||||
|     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 +265,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 +283,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)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -225,7 +301,7 @@ Document* DocumentExporter::createEmptyTexture(const Samples& samples) | |||
| { | ||||
|   Palette* palette = NULL; | ||||
|   PixelFormat pixelFormat = IMAGE_INDEXED; | ||||
|   gfx::Rect fullTextureBounds; | ||||
|   gfx::Rect fullTextureBounds(0, 0, m_textureWidth, m_textureHeight); | ||||
|   int maxColors = 256; | ||||
| 
 | ||||
|   for (Samples::const_iterator | ||||
|  | @ -268,21 +344,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 +373,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 +394,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()) | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -29,6 +29,7 @@ | |||
| 
 | ||||
| namespace doc { | ||||
|   class Image; | ||||
|   class Layer; | ||||
| } | ||||
| 
 | ||||
| namespace app { | ||||
|  | @ -50,11 +51,7 @@ namespace app { | |||
|       DefaultScaleMode | ||||
|     }; | ||||
| 
 | ||||
|     DocumentExporter() : | ||||
|       m_dataFormat(DefaultDataFormat), | ||||
|       m_textureFormat(DefaultTextureFormat), | ||||
|       m_scaleMode(DefaultScaleMode) { | ||||
|     } | ||||
|     DocumentExporter(); | ||||
| 
 | ||||
|     void setDataFormat(DataFormat format) { | ||||
|       m_dataFormat = format; | ||||
|  | @ -72,6 +69,18 @@ namespace app { | |||
|       m_textureFilename = filename; | ||||
|     } | ||||
| 
 | ||||
|     void setTextureWidth(int width) { | ||||
|       m_textureWidth = width; | ||||
|     } | ||||
| 
 | ||||
|     void setTextureHeight(int height) { | ||||
|       m_textureHeight = height; | ||||
|     } | ||||
| 
 | ||||
|     void setTexturePack(bool state) { | ||||
|       m_texturePack = state; | ||||
|     } | ||||
| 
 | ||||
|     void setScale(double scale) { | ||||
|       m_scale = scale; | ||||
|     } | ||||
|  | @ -80,8 +89,8 @@ namespace app { | |||
|       m_scaleMode = mode; | ||||
|     } | ||||
| 
 | ||||
|     void addDocument(Document* document) { | ||||
|       m_documents.push_back(document); | ||||
|     void addDocument(Document* document, doc::Layer* layer = NULL) { | ||||
|       m_documents.push_back(Item(document, layer)); | ||||
|     } | ||||
| 
 | ||||
|     void exportSheet(); | ||||
|  | @ -91,19 +100,33 @@ namespace app { | |||
|     class Samples; | ||||
|     class LayoutSamples; | ||||
|     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); | ||||
| 
 | ||||
|     class Item { | ||||
|     public: | ||||
|       Document* doc; | ||||
|       doc::Layer* layer; | ||||
|       Item(Document* doc, doc::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; | ||||
|     int m_textureWidth; | ||||
|     int m_textureHeight; | ||||
|     bool m_texturePack; | ||||
|     double m_scale; | ||||
|     ScaleMode m_scaleMode; | ||||
|     std::vector<Document*> m_documents; | ||||
|     Items m_documents; | ||||
| 
 | ||||
|     DISABLE_COPYING(DocumentExporter); | ||||
|   }; | ||||
|  |  | |||
|  | @ -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,16 +45,20 @@ Job::Job(const char* job_name) | |||
|   m_canceled_flag = false; | ||||
| 
 | ||||
|   m_mutex = new base::mutex(); | ||||
| 
 | ||||
|   if (App::instance()->isGui()) { | ||||
|     m_progress = StatusBar::instance()->addProgress(); | ||||
|   m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", job_name); | ||||
|     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() | ||||
| { | ||||
|   if (App::instance()->isGui()) { | ||||
|     ASSERT(!m_timer->isRunning()); | ||||
|     ASSERT(m_thread == NULL); | ||||
| 
 | ||||
|  | @ -62,6 +67,7 @@ Job::~Job() | |||
| 
 | ||||
|     if (m_progress) | ||||
|       delete m_progress; | ||||
|   } | ||||
| 
 | ||||
|   if (m_mutex) | ||||
|     delete m_mutex; | ||||
|  | @ -70,6 +76,8 @@ Job::~Job() | |||
| void Job::startJob() | ||||
| { | ||||
|   m_thread = new base::thread(&Job::thread_proc, this); | ||||
| 
 | ||||
|   if (m_alert_window) { | ||||
|     m_alert_window->openWindowInForeground(); | ||||
| 
 | ||||
|     // The job was canceled by the user?
 | ||||
|  | @ -78,11 +86,12 @@ void Job::startJob() | |||
|       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) { | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -220,6 +220,7 @@ 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.
 | ||||
|       if (Manager::getDefault()) | ||||
|         Manager::getDefault()->invalidate(); | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -1301,7 +1301,7 @@ void Editor::setZoomAndCenterInMouse(int zoom, int mouse_x, int mouse_y, ZoomBeh | |||
| 
 | ||||
|   switch (zoomBehavior) { | ||||
|     case kCofiguredZoomBehavior: | ||||
|       centerMouse = get_config_bool("Editor", "CenterMouseInZoom", true); | ||||
|       centerMouse = get_config_bool("Editor", "CenterOnZoom", false); | ||||
|       break; | ||||
|     case kCenterOnZoom: | ||||
|       centerMouse = true; | ||||
|  |  | |||
|  | @ -1246,7 +1246,7 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev) | |||
| { | ||||
|   int scale = jguiscale(); | ||||
|   Graphics* g = ev.getGraphics(); | ||||
|   AppMenuItem* widget = static_cast<AppMenuItem*>(ev.getSource()); | ||||
|   MenuItem* widget = static_cast<MenuItem*>(ev.getSource()); | ||||
|   gfx::Rect bounds = widget->getClientBounds(); | ||||
|   gfx::Color fg, bg; | ||||
|   int c, bar; | ||||
|  | @ -1328,19 +1328,21 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev) | |||
|       } | ||||
|     } | ||||
|     // Draw the keyboard shortcut
 | ||||
|     else if (widget->getKey() && !widget->getKey()->accels().empty()) { | ||||
|       int old_align = widget->getAlign(); | ||||
|     else if (AppMenuItem* appMenuItem = dynamic_cast<AppMenuItem*>(widget)) { | ||||
|       if (appMenuItem->getKey() && !appMenuItem->getKey()->accels().empty()) { | ||||
|         int old_align = appMenuItem->getAlign(); | ||||
| 
 | ||||
|         pos = bounds; | ||||
|         pos.w -= widget->child_spacing/4; | ||||
| 
 | ||||
|       std::string buf = widget->getKey()->accels().front().toString(); | ||||
|         std::string buf = appMenuItem->getKey()->accels().front().toString(); | ||||
| 
 | ||||
|         widget->setAlign(JI_RIGHT | JI_MIDDLE); | ||||
|         drawTextString(g, buf.c_str(), fg, ColorNone, widget, pos, 0); | ||||
|         widget->setAlign(old_align); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void SkinTheme::paintSplitter(PaintEvent& ev) | ||||
|  |  | |||
|  | @ -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" | ||||
|  | @ -64,8 +65,16 @@ UIContext::~UIContext() | |||
|   ASSERT(documents().empty()); | ||||
| } | ||||
| 
 | ||||
| bool UIContext::isUiAvailable() const | ||||
| { | ||||
|   return App::instance()->isGui(); | ||||
| } | ||||
| 
 | ||||
| DocumentView* UIContext::activeView() const | ||||
| { | ||||
|   if (!isUiAvailable()) | ||||
|     return NULL; | ||||
| 
 | ||||
|   Workspace* workspace = App::instance()->getMainWindow()->getWorkspace(); | ||||
|   WorkspaceView* view = workspace->activeView(); | ||||
|   if (DocumentView* docView = dynamic_cast<DocumentView*>(view)) | ||||
|  | @ -156,6 +165,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; | ||||
| 
 | ||||
|  | @ -179,8 +192,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
 | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ namespace app { | |||
|     UIContext(); | ||||
|     virtual ~UIContext(); | ||||
| 
 | ||||
|     virtual bool isUiAvailable() const { return true; } | ||||
|     bool isUiAvailable() const override; | ||||
| 
 | ||||
|     DocumentView* activeView() const; | ||||
|     void setActiveView(DocumentView* documentView); | ||||
|  |  | |||
|  | @ -85,7 +85,7 @@ void ProgramOptions::parse(int argc, const char* argv[]) | |||
|           } | ||||
| 
 | ||||
|           Option* option = *it; | ||||
|           option->setEnabled(true); | ||||
|           std::string optionValue; | ||||
| 
 | ||||
|           if (option->doesRequireValue()) { | ||||
|             if (usedBy != 0) { | ||||
|  | @ -104,9 +104,11 @@ void ProgramOptions::parse(int argc, const char* argv[]) | |||
|             } | ||||
| 
 | ||||
|             // Set the value specified for this argument
 | ||||
|             option->setValue(argv[++i]); | ||||
|             optionValue = argv[++i]; | ||||
|             usedBy = option->mnemonic(); | ||||
|           } | ||||
| 
 | ||||
|           m_values.push_back(Value(option, optionValue)); | ||||
|         } | ||||
|       } | ||||
|       // Use name
 | ||||
|  | @ -133,7 +135,6 @@ void ProgramOptions::parse(int argc, const char* argv[]) | |||
|         } | ||||
| 
 | ||||
|         Option* option = *it; | ||||
|         option->setEnabled(true); | ||||
| 
 | ||||
|         if (option->doesRequireValue()) { | ||||
|           // If the option was specified without '=', we can get the
 | ||||
|  | @ -147,15 +148,14 @@ void ProgramOptions::parse(int argc, const char* argv[]) | |||
|             } | ||||
|             optionValue = argv[++i]; | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|           // Set the value specified for this argument
 | ||||
|           option->setValue(optionValue); | ||||
|         m_values.push_back(Value(option, optionValue)); | ||||
|       } | ||||
|     } | ||||
|     } | ||||
|     // Add values
 | ||||
|     // Add values without a related option.
 | ||||
|     else { | ||||
|       m_values.push_back(arg); | ||||
|       m_values.push_back(Value(NULL, arg)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -163,14 +163,24 @@ void ProgramOptions::parse(int argc, const char* argv[]) | |||
| void ProgramOptions::reset() | ||||
| { | ||||
|   m_values.clear(); | ||||
|   for_each(m_options.begin(), m_options.end(), &ProgramOptions::resetOption); | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| void ProgramOptions::resetOption(Option* option) | ||||
| bool ProgramOptions::enabled(const Option& option) const | ||||
| { | ||||
|   option->setEnabled(false); | ||||
|   option->setValue(""); | ||||
|   for (const auto& value : m_values) { | ||||
|     if (value.option() == &option) | ||||
|       return true; | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| std::string ProgramOptions::value_of(const Option& option) const | ||||
| { | ||||
|   for (const auto& value : m_values) { | ||||
|     if (value.option() == &option) | ||||
|       return value.value(); | ||||
|   } | ||||
|   return ""; | ||||
| } | ||||
| 
 | ||||
| } // namespace base
 | ||||
|  | @ -182,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; | ||||
|  | @ -207,7 +217,8 @@ std::ostream& operator<<(std::ostream& os, const base::ProgramOptions& po) | |||
|       bool multilines = (option->description().find('\n') != std::string::npos); | ||||
| 
 | ||||
|       if (!multilines) { | ||||
|         os << std::setw(maxOptionWidth - optionWidth + 1) << ' ' << option->description(); | ||||
|         os << std::setw(maxOptionWidth - optionWidth + 1) << ' ' << option->description() | ||||
|            << "\n"; | ||||
|       } | ||||
|       else { | ||||
|         std::istringstream s(option->description()); | ||||
|  | @ -220,6 +231,7 @@ std::ostream& operator<<(std::ostream& os, const base::ProgramOptions& po) | |||
|         } | ||||
|       } | ||||
|     } | ||||
|     else | ||||
|       os << "\n"; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,16 +39,13 @@ namespace base { | |||
|     public: | ||||
|       Option(const std::string& name) | ||||
|         : m_name(name) | ||||
|         , m_mnemonic(0) | ||||
|         , m_enabled(false) { | ||||
|         , m_mnemonic(0) { | ||||
|       } | ||||
|       // Getters
 | ||||
|       const std::string& name() const { return m_name; } | ||||
|       const std::string& description() const { return m_description; } | ||||
|       const std::string& value() const { return m_value; } | ||||
|       const std::string& getValueName() const { return m_valueName; } | ||||
|       char mnemonic() const { return m_mnemonic; } | ||||
|       bool enabled() const { return m_enabled; } | ||||
|       bool doesRequireValue() const { return !m_valueName.empty(); } | ||||
|       // Setters
 | ||||
|       Option& description(const std::string& desc) { m_description = desc; return *this; } | ||||
|  | @ -58,21 +55,29 @@ namespace base { | |||
|         return *this; | ||||
|       } | ||||
|     private: | ||||
|       void setValue(const std::string& value) { m_value = value; } | ||||
|       void setEnabled(bool enabled) { m_enabled = enabled; } | ||||
| 
 | ||||
|       std::string m_name;        // Name of the option (e.g. "help" for "--help")
 | ||||
|       std::string m_description; // Description of the option (this can be used when the help is printed).
 | ||||
|       std::string m_value;       // The value specified by the user in the command line.
 | ||||
|       std::string m_valueName;   // Empty if this option doesn't require a value, or the name of the expected value.
 | ||||
|       char m_mnemonic;           // One character that can be used in the command line to use this option.
 | ||||
|       bool m_enabled;            // True if the user specified this argument.
 | ||||
| 
 | ||||
|       friend class ProgramOptions; | ||||
|     }; | ||||
| 
 | ||||
|     class Value { | ||||
|     public: | ||||
|       Value(Option* option, const std::string& value) | ||||
|         : m_option(option) | ||||
|         , m_value(value) { | ||||
|       } | ||||
|       const Option* option() const { return m_option; } | ||||
|       const std::string& value() const { return m_value; } | ||||
|     private: | ||||
|       Option* m_option; | ||||
|       std::string m_value; | ||||
|     }; | ||||
| 
 | ||||
|     typedef std::vector<Option*> OptionList; | ||||
|     typedef std::vector<std::string> ValueList; | ||||
|     typedef std::vector<Value> ValueList; | ||||
| 
 | ||||
|     ProgramOptions(); | ||||
| 
 | ||||
|  | @ -88,23 +93,19 @@ namespace base { | |||
|     // Detects which options where specified in the command line.
 | ||||
|     void parse(int argc, const char* argv[]); | ||||
| 
 | ||||
|     // Reset all options values/flags.
 | ||||
|     // Reset all option values/flags.
 | ||||
|     void reset(); | ||||
| 
 | ||||
|     // Returns the list of available options. To know the list of
 | ||||
|     // specified options you can iterate this list asking for
 | ||||
|     // Option::enabled() flag to know if the option was specified by
 | ||||
|     // the user in the command line.
 | ||||
|     // Returns the list of available options for the user.
 | ||||
|     const OptionList& options() const { return m_options; } | ||||
| 
 | ||||
|     // Returns the list of values that are not associated to any
 | ||||
|     // options. E.g. a list of files specified in the command line to
 | ||||
|     // be opened.
 | ||||
|     // List of specified options/values in the command line.
 | ||||
|     const ValueList& values() const { return m_values; } | ||||
| 
 | ||||
|   private: | ||||
|     static void resetOption(Option* option); | ||||
|     bool enabled(const Option& option) const; | ||||
|     std::string value_of(const Option& option) const; | ||||
| 
 | ||||
|   private: | ||||
|     OptionList m_options; | ||||
|     ValueList m_values; | ||||
|   }; | ||||
|  |  | |||
|  | @ -21,13 +21,13 @@ TEST(ProgramOptions, OptionMembers) | |||
|   EXPECT_EQ("help", help.name()); | ||||
|   EXPECT_EQ("Show the help", help.description()); | ||||
|   EXPECT_EQ('h', help.mnemonic()); | ||||
|   EXPECT_FALSE(help.enabled()); | ||||
|   EXPECT_FALSE(po.enabled(help)); | ||||
|   EXPECT_FALSE(help.doesRequireValue()); | ||||
| 
 | ||||
|   EXPECT_EQ("output", output.name()); | ||||
|   EXPECT_EQ("", output.description()); | ||||
|   EXPECT_EQ('O', output.mnemonic()); | ||||
|   EXPECT_FALSE(output.enabled()); | ||||
|   EXPECT_FALSE(po.enabled(output)); | ||||
|   EXPECT_TRUE(output.doesRequireValue()); | ||||
| } | ||||
| 
 | ||||
|  | @ -36,20 +36,20 @@ TEST(ProgramOptions, Reset) | |||
|   ProgramOptions po; | ||||
|   ProgramOptions::Option& help = po.add("help"); | ||||
|   ProgramOptions::Option& file = po.add("file").requiresValue("FILE"); | ||||
|   EXPECT_FALSE(help.enabled()); | ||||
|   EXPECT_FALSE(file.enabled()); | ||||
|   EXPECT_EQ("", file.value()); | ||||
|   EXPECT_FALSE(po.enabled(help)); | ||||
|   EXPECT_FALSE(po.enabled(file)); | ||||
|   EXPECT_EQ("", po.value_of(file)); | ||||
| 
 | ||||
|   const char* argv[] = { "program.exe", "--help", "--file=readme.txt" }; | ||||
|   po.parse(3, argv); | ||||
|   EXPECT_TRUE(help.enabled()); | ||||
|   EXPECT_TRUE(file.enabled()); | ||||
|   EXPECT_EQ("readme.txt", file.value()); | ||||
|   EXPECT_TRUE(po.enabled(help)); | ||||
|   EXPECT_TRUE(po.enabled(file)); | ||||
|   EXPECT_EQ("readme.txt", po.value_of(file)); | ||||
| 
 | ||||
|   po.reset(); | ||||
|   EXPECT_FALSE(help.enabled()); | ||||
|   EXPECT_FALSE(file.enabled()); | ||||
|   EXPECT_EQ("", file.value()); | ||||
|   EXPECT_FALSE(po.enabled(help)); | ||||
|   EXPECT_FALSE(po.enabled(file)); | ||||
|   EXPECT_EQ("", po.value_of(file)); | ||||
| } | ||||
| 
 | ||||
| TEST(ProgramOptions, Parse) | ||||
|  | @ -61,50 +61,59 @@ TEST(ProgramOptions, Parse) | |||
| 
 | ||||
|   const char* argv1[] = { "program.exe", "-?" }; | ||||
|   po.parse(2, argv1); | ||||
|   EXPECT_TRUE(help.enabled()); | ||||
|   EXPECT_TRUE(po.enabled(help)); | ||||
| 
 | ||||
|   const char* argv2[] = { "program.exe", "--help" }; | ||||
|   po.reset(); | ||||
|   po.parse(2, argv2); | ||||
|   EXPECT_TRUE(help.enabled()); | ||||
|   EXPECT_TRUE(po.enabled(help)); | ||||
| 
 | ||||
|   const char* argv3[] = { "program.exe", "--input", "hello.cpp", "--output", "hello.exe" }; | ||||
|   po.reset(); | ||||
|   po.parse(5, argv3); | ||||
|   EXPECT_FALSE(help.enabled()); | ||||
|   EXPECT_TRUE(input.enabled()); | ||||
|   EXPECT_TRUE(output.enabled()); | ||||
|   EXPECT_EQ("hello.cpp", input.value()); | ||||
|   EXPECT_EQ("hello.exe", output.value()); | ||||
|   EXPECT_FALSE(po.enabled(help)); | ||||
|   EXPECT_TRUE(po.enabled(input)); | ||||
|   EXPECT_TRUE(po.enabled(output)); | ||||
|   EXPECT_EQ("hello.cpp", po.value_of(input)); | ||||
|   EXPECT_EQ("hello.exe", po.value_of(output)); | ||||
| 
 | ||||
|   const char* argv4[] = { "program.exe", "--input=hi.c", "--output=out.exe" }; | ||||
|   po.reset(); | ||||
|   po.parse(3, argv4); | ||||
|   EXPECT_FALSE(help.enabled()); | ||||
|   EXPECT_TRUE(input.enabled()); | ||||
|   EXPECT_TRUE(output.enabled()); | ||||
|   EXPECT_EQ("hi.c", input.value()); | ||||
|   EXPECT_EQ("out.exe", output.value()); | ||||
|   EXPECT_FALSE(po.enabled(help)); | ||||
|   EXPECT_TRUE(po.enabled(input)); | ||||
|   EXPECT_TRUE(po.enabled(output)); | ||||
|   EXPECT_EQ("hi.c", po.value_of(input)); | ||||
|   EXPECT_EQ("out.exe", po.value_of(output)); | ||||
| 
 | ||||
|   const char* argv5[] = { "program.exe", "-?i", "input.md", "-o", "output.html", "extra-file.txt" }; | ||||
|   po.reset(); | ||||
|   po.parse(6, argv5); | ||||
|   EXPECT_TRUE(help.enabled()); | ||||
|   EXPECT_TRUE(input.enabled()); | ||||
|   EXPECT_TRUE(output.enabled()); | ||||
|   EXPECT_EQ("input.md", input.value()); | ||||
|   EXPECT_EQ("output.html", output.value()); | ||||
|   ASSERT_EQ(1, po.values().size()); | ||||
|   EXPECT_EQ("extra-file.txt", po.values()[0]); | ||||
|   EXPECT_TRUE(po.enabled(help)); | ||||
|   EXPECT_TRUE(po.enabled(input)); | ||||
|   EXPECT_TRUE(po.enabled(output)); | ||||
|   EXPECT_EQ("input.md", po.value_of(input)); | ||||
|   EXPECT_EQ("output.html", po.value_of(output)); | ||||
|   ASSERT_EQ(4, po.values().size()); | ||||
|   EXPECT_EQ(&help, po.values()[0].option()); | ||||
|   EXPECT_EQ(&input, po.values()[1].option()); | ||||
|   EXPECT_EQ(&output, po.values()[2].option()); | ||||
|   EXPECT_EQ(NULL, po.values()[3].option()); | ||||
|   EXPECT_EQ("", po.values()[0].value()); | ||||
|   EXPECT_EQ("input.md", po.values()[1].value()); | ||||
|   EXPECT_EQ("output.html", po.values()[2].value()); | ||||
|   EXPECT_EQ("extra-file.txt", po.values()[3].value()); | ||||
| 
 | ||||
|   const char* argv6[] = { "program.exe", "value1", "value2", "-o", "output", "value3", "--input=input", "value4" }; | ||||
|   po.reset(); | ||||
|   po.parse(8, argv6); | ||||
|   ASSERT_EQ(4, po.values().size()); | ||||
|   EXPECT_EQ("value1", po.values()[0]); | ||||
|   EXPECT_EQ("value2", po.values()[1]); | ||||
|   EXPECT_EQ("value3", po.values()[2]); | ||||
|   EXPECT_EQ("value4", po.values()[3]); | ||||
|   ASSERT_EQ(6, po.values().size()); | ||||
|   EXPECT_EQ("value1", po.values()[0].value()); | ||||
|   EXPECT_EQ("value2", po.values()[1].value()); | ||||
|   EXPECT_EQ("output", po.values()[2].value()); | ||||
|   EXPECT_EQ("value3", po.values()[3].value()); | ||||
|   EXPECT_EQ("input", po.values()[4].value()); | ||||
|   EXPECT_EQ("value4", po.values()[5].value()); | ||||
| } | ||||
| 
 | ||||
| TEST(ProgramOptions, ParseErrors) | ||||
|  | @ -125,19 +134,19 @@ TEST(ProgramOptions, ParseErrors) | |||
| 
 | ||||
|   const char* argv4[] = { "program.exe", "-?a" }; | ||||
|   po.reset(); | ||||
|   EXPECT_FALSE(help.enabled()); | ||||
|   EXPECT_FALSE(po.enabled(help)); | ||||
|   EXPECT_THROW(po.parse(2, argv4), InvalidProgramOption); | ||||
|   EXPECT_TRUE(help.enabled());  // -? is parsed anyway, -a is the invalid option
 | ||||
|   EXPECT_TRUE(po.enabled(help));  // -? is parsed anyway, -a is the invalid option
 | ||||
| 
 | ||||
|   const char* argv5[] = { "program.exe", "-io", "input-and-output.txt" }; | ||||
|   po.reset(); | ||||
|   EXPECT_THROW(po.parse(2, argv5), ProgramOptionNeedsValue); | ||||
|   po.reset(); | ||||
|   EXPECT_THROW(po.parse(3, argv5), InvalidProgramOptionsCombination); | ||||
|   EXPECT_TRUE(input.enabled()); | ||||
|   EXPECT_TRUE(output.enabled()); | ||||
|   EXPECT_EQ("input-and-output.txt", input.value()); | ||||
|   EXPECT_EQ("", output.value()); | ||||
|   EXPECT_TRUE(po.enabled(input)); | ||||
|   EXPECT_FALSE(po.enabled(output)); | ||||
|   EXPECT_EQ("input-and-output.txt", po.value_of(input)); | ||||
|   EXPECT_EQ("", po.value_of(output)); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
|  |  | |||
|  | @ -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(); } | ||||
|  |  | |||
|  | @ -18,23 +18,24 @@ typedef base::ProgramOptions PO; | |||
| static void run(int argc, const char* argv[]) | ||||
| { | ||||
|   PO po; | ||||
|   PO::Option& inputFn = po.add("input").requiresValue("<filename>"); | ||||
|   PO::Option& inputOpt = po.add("input").requiresValue("<filename>"); | ||||
|   PO::Option& widgetId = po.add("widgetid").requiresValue("<filename>"); | ||||
|   po.parse(argc, argv); | ||||
| 
 | ||||
|   // Try to load the XML file
 | ||||
|   TiXmlDocument* doc = NULL; | ||||
| 
 | ||||
|   if (inputFn.enabled()) { | ||||
|     base::FileHandle inputFile(base::open_file(inputFn.value(), "rb")); | ||||
|   std::string inputFilename = po.value_of(inputOpt); | ||||
|   if (!inputFilename.empty()) { | ||||
|     base::FileHandle inputFile(base::open_file(inputFilename, "rb")); | ||||
|     doc = new TiXmlDocument(); | ||||
|     doc->SetValue(inputFn.value().c_str()); | ||||
|     doc->SetValue(inputFilename.c_str()); | ||||
|     if (!doc->LoadFile(inputFile)) | ||||
|       throw std::runtime_error("invalid input file"); | ||||
|   } | ||||
| 
 | ||||
|   if (doc && widgetId.enabled()) | ||||
|     gen_ui_class(doc, inputFn.value(), widgetId.value()); | ||||
|   if (doc && po.enabled(widgetId)) | ||||
|     gen_ui_class(doc, inputFilename, po.value_of(widgetId)); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, const char* argv[]) | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| # ASEPRITE | ||||
| # Aseprite | ||||
| # Copyright (C) 2001-2014  David Capello | ||||
| 
 | ||||
| add_library(gfx-lib | ||||
|   hsv.cpp | ||||
|   packing_rects.cpp | ||||
|   region.cpp | ||||
|   rgb.cpp | ||||
|   transformation.cpp) | ||||
|  |  | |||
|  | @ -0,0 +1,98 @@ | |||
| // Aseprite Gfx Library
 | ||||
| // Copyright (C) 2001-2014 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 "gfx/packing_rects.h" | ||||
| 
 | ||||
| #include "gfx/region.h" | ||||
| #include "gfx/size.h" | ||||
| 
 | ||||
| namespace gfx { | ||||
| 
 | ||||
| void PackingRects::add(const Size& sz) | ||||
| { | ||||
|   m_rects.push_back(Rect(sz)); | ||||
| } | ||||
| 
 | ||||
| void PackingRects::add(const Rect& rc) | ||||
| { | ||||
|   m_rects.push_back(rc); | ||||
| } | ||||
| 
 | ||||
| Size PackingRects::bestFit() | ||||
| { | ||||
|   Size size(0, 0); | ||||
| 
 | ||||
|   // Calculate the amount of pixels that we need, the texture cannot
 | ||||
|   // be smaller than that.
 | ||||
|   int neededArea = 0; | ||||
|   for (const auto& rc : m_rects) { | ||||
|     neededArea += rc.w * rc.h; | ||||
|   } | ||||
| 
 | ||||
|   int w = 1; | ||||
|   int h = 1; | ||||
|   int z = 0; | ||||
|   bool fit = false; | ||||
|   while (true) { | ||||
|     if (w*h >= neededArea) { | ||||
|       fit = pack(Size(w, h)); | ||||
|       if (fit) { | ||||
|         size = Size(w, h); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if ((++z) & 1) | ||||
|       w *= 2; | ||||
|     else | ||||
|       h *= 2; | ||||
|   } | ||||
| 
 | ||||
|   return size; | ||||
| } | ||||
| 
 | ||||
| static bool by_area(const Rect* a, const Rect* b) { | ||||
|   return a->w*a->h > b->w*b->h; | ||||
| } | ||||
| 
 | ||||
| bool PackingRects::pack(const Size& size) | ||||
| { | ||||
|   m_bounds = Rect(size); | ||||
| 
 | ||||
|   // We cannot sort m_rects because we want to 
 | ||||
|   std::vector<Rect*> rectPtrs(m_rects.size()); | ||||
|   int i = 0; | ||||
|   for (auto& rc : m_rects) | ||||
|     rectPtrs[i++] = &rc; | ||||
|   std::sort(rectPtrs.begin(), rectPtrs.end(), by_area); | ||||
| 
 | ||||
|   gfx::Region rgn(m_bounds); | ||||
|   for (auto rcPtr : rectPtrs) { | ||||
|     gfx::Rect& rc = *rcPtr; | ||||
| 
 | ||||
|     for (int v=0; v<=m_bounds.h-rc.h; ++v) { | ||||
|       for (int u=0; u<=m_bounds.w-rc.w; ++u) { | ||||
|         gfx::Rect possible(u, v, rc.w, rc.h); | ||||
|         Region::Overlap overlap = rgn.contains(possible); | ||||
|         if (overlap == Region::In) { | ||||
|           rc = possible; | ||||
|           rgn.createSubtraction(rgn, gfx::Region(rc)); | ||||
|           goto next_rc; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return false; // There is not enough room for "rc"
 | ||||
|   next_rc:; | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace gfx
 | ||||
|  | @ -0,0 +1,53 @@ | |||
| // Aseprite Gfx Library
 | ||||
| // Copyright (C) 2001-2014 David Capello
 | ||||
| //
 | ||||
| // This file is released under the terms of the MIT license.
 | ||||
| // Read LICENSE.txt for more information.
 | ||||
| 
 | ||||
| #ifndef GFX_TEXTURE_SIZE_H_INCLUDED | ||||
| #define GFX_TEXTURE_SIZE_H_INCLUDED | ||||
| #pragma once | ||||
| 
 | ||||
| #include "gfx/fwd.h" | ||||
| #include "gfx/rect.h" | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace gfx { | ||||
| 
 | ||||
|   // TODO add support for rotations
 | ||||
|   class PackingRects { | ||||
|   public: | ||||
|     typedef std::vector<Rect> Rects; | ||||
|     typedef Rects::const_iterator const_iterator; | ||||
| 
 | ||||
|     // Iterate over all given rectangles (in the same order they where
 | ||||
|     // given in addSize() calls).
 | ||||
|     const_iterator begin() const { return m_rects.begin(); } | ||||
|     const_iterator end() const { return m_rects.end(); } | ||||
| 
 | ||||
|     size_t size() const { return m_rects.size(); } | ||||
|     const Rect& operator[](int i) const { return m_rects[i]; } | ||||
| 
 | ||||
|     // Adds a new rectangle.
 | ||||
|     void add(const Size& sz); | ||||
|     void add(const Rect& rc); | ||||
| 
 | ||||
|     // Returns the best size for the texture.
 | ||||
|     Size bestFit(); | ||||
| 
 | ||||
|     // Rearrange all given rectangles to best fit a texture size.
 | ||||
|     // Returns true if all rectangles were correctly arranged or false
 | ||||
|     // if there is not enough space.
 | ||||
|     bool pack(const Size& size); | ||||
| 
 | ||||
|     // Returns the bounds of the packed area.
 | ||||
|     const Rect& bounds() const { return m_bounds; } | ||||
| 
 | ||||
|   private: | ||||
|     Rect m_bounds; | ||||
|     Rects m_rects; | ||||
|   }; | ||||
| 
 | ||||
| } // namespace gfx
 | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,96 @@ | |||
| // Aseprite Gfx Library
 | ||||
| // Copyright (C) 2001-2014 David Capello
 | ||||
| //
 | ||||
| // This file is released under the terms of the MIT license.
 | ||||
| // Read LICENSE.txt for more information.
 | ||||
| 
 | ||||
| #include <gtest/gtest.h> | ||||
| 
 | ||||
| #include "gfx/packing_rects.h" | ||||
| #include "gfx/rect_io.h" | ||||
| #include "gfx/size.h" | ||||
| 
 | ||||
| using namespace gfx; | ||||
| 
 | ||||
| TEST(PackingRects, Simple) | ||||
| { | ||||
|   PackingRects pr; | ||||
|   pr.add(Size(256, 128)); | ||||
|   EXPECT_FALSE(pr.pack(Size(256, 120))); | ||||
|   EXPECT_TRUE(pr.pack(Size(256, 128))); | ||||
| 
 | ||||
|   EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]); | ||||
|   EXPECT_EQ(Rect(0, 0, 256, 128), pr.bounds()); | ||||
| } | ||||
| 
 | ||||
| TEST(PackingRects, SimpleTwoRects) | ||||
| { | ||||
|   PackingRects pr; | ||||
|   pr.add(Size(256, 128)); | ||||
|   pr.add(Size(256, 120)); | ||||
|   EXPECT_TRUE(pr.pack(Size(256, 256))); | ||||
| 
 | ||||
|   EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds()); | ||||
|   EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]); | ||||
|   EXPECT_EQ(Rect(0, 128, 256, 120), pr[1]); | ||||
| } | ||||
| 
 | ||||
| TEST(PackingRects, BestFit) | ||||
| { | ||||
|   PackingRects pr; | ||||
|   pr.add(Size(10, 12)); | ||||
|   pr.bestFit(); | ||||
|   EXPECT_EQ(Rect(0, 0, 16, 16), pr.bounds()); | ||||
| } | ||||
| 
 | ||||
| TEST(PackingRects, BestFitTwoRects) | ||||
| { | ||||
|   PackingRects pr; | ||||
|   pr.add(Size(256, 128)); | ||||
|   pr.add(Size(256, 127)); | ||||
|   pr.bestFit(); | ||||
| 
 | ||||
|   EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds()); | ||||
|   EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]); | ||||
|   EXPECT_EQ(Rect(0, 128, 256, 127), pr[1]); | ||||
| } | ||||
| 
 | ||||
| TEST(PackingRects, BestFit6Frames100x100) | ||||
| { | ||||
|   PackingRects pr; | ||||
|   pr.add(Size(100, 100)); | ||||
|   pr.add(Size(100, 100)); | ||||
|   pr.add(Size(100, 100)); | ||||
|   pr.add(Size(100, 100)); | ||||
|   pr.add(Size(100, 100)); | ||||
|   pr.add(Size(100, 100)); | ||||
|   pr.bestFit(); | ||||
| 
 | ||||
|   EXPECT_EQ(Rect(0, 0, 512, 256), pr.bounds()); | ||||
|   EXPECT_EQ(Rect(0, 0, 100, 100), pr[0]); | ||||
|   EXPECT_EQ(Rect(100, 0, 100, 100), pr[1]); | ||||
|   EXPECT_EQ(Rect(200, 0, 100, 100), pr[2]); | ||||
|   EXPECT_EQ(Rect(300, 0, 100, 100), pr[3]); | ||||
|   EXPECT_EQ(Rect(400, 0, 100, 100), pr[4]); | ||||
|   EXPECT_EQ(Rect(0, 100, 100, 100), pr[5]); | ||||
| } | ||||
| 
 | ||||
| TEST(PackingRects, KeepSameRectsOrder) | ||||
| { | ||||
|   PackingRects pr; | ||||
|   pr.add(Size(10, 10)); | ||||
|   pr.add(Size(20, 20)); | ||||
|   pr.add(Size(30, 30)); | ||||
|   pr.bestFit(); | ||||
| 
 | ||||
|   EXPECT_EQ(Rect(0, 0, 64, 32), pr.bounds()); | ||||
|   EXPECT_EQ(Rect(50, 0, 10, 10), pr[0]); | ||||
|   EXPECT_EQ(Rect(30, 0, 20, 20), pr[1]); | ||||
|   EXPECT_EQ(Rect(0, 0, 30, 30), pr[2]); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|   ::testing::InitGoogleTest(&argc, argv); | ||||
|   return RUN_ALL_TESTS(); | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| // Aseprite Gfx Library
 | ||||
| // Copyright (C) 2001-2014 David Capello
 | ||||
| //
 | ||||
| // This file is released under the terms of the MIT license.
 | ||||
| // Read LICENSE.txt for more information.
 | ||||
| 
 | ||||
| #ifndef GFX_RECT_IO_H_INCLUDED | ||||
| #define GFX_RECT_IO_H_INCLUDED | ||||
| #pragma once | ||||
| 
 | ||||
| #include "gfx/rect.h" | ||||
| #include <iosfwd> | ||||
| 
 | ||||
| namespace gfx { | ||||
| 
 | ||||
|   std::ostream& operator<<(std::ostream& os, const Rect& rect) | ||||
|   { | ||||
|     return os << "(" | ||||
|               << rect.x << ", " | ||||
|               << rect.y << ", " | ||||
|               << rect.w << ", " | ||||
|               << rect.h << ")"; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -6,26 +6,14 @@ | |||
| 
 | ||||
| #include <gtest/gtest.h> | ||||
| 
 | ||||
| #include "gfx/rect.h" | ||||
| #include "gfx/size.h" | ||||
| #include "gfx/border.h" | ||||
| #include "gfx/rect.h" | ||||
| #include "gfx/rect_io.h" | ||||
| #include "gfx/size.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace gfx; | ||||
| 
 | ||||
| namespace gfx { | ||||
| 
 | ||||
|   ostream& operator<<(ostream& os, const Rect& rect) | ||||
|   { | ||||
|     return os << "(" | ||||
|               << rect.x << ", " | ||||
|               << rect.y << ", " | ||||
|               << rect.w << ", " | ||||
|               << rect.h << ")"; | ||||
|   } | ||||
|    | ||||
| } | ||||
| 
 | ||||
| TEST(Rect, Ctor) | ||||
| { | ||||
|   EXPECT_EQ(Rect(0, 0, 0, 0), Rect()); | ||||
|  |  | |||
|  | @ -154,6 +154,11 @@ bool Region::contains(const PointT<int>& pt) const | |||
| 
 | ||||
| Region::Overlap Region::contains(const Rect& rect) const | ||||
| { | ||||
|   static_assert( | ||||
|     Out   == PIXMAN_REGION_OUT && | ||||
|     In    == PIXMAN_REGION_IN && | ||||
|     Part  == PIXMAN_REGION_PART, "Pixman constants have changed"); | ||||
| 
 | ||||
|   pixman_box32 box = { rect.x, rect.y, rect.x2(), rect.y2() }; | ||||
|   return (Region::Overlap)pixman_region32_contains_rectangle(&m_region, &box); | ||||
| } | ||||
|  |  | |||
|  | @ -70,7 +70,7 @@ int app_main(int argc, char* argv[]) | |||
|     she::ScopedHandle<she::System> system(she::create_system()); | ||||
|     MemLeak memleak; | ||||
|     ui::GuiSystem guiSystem; | ||||
|     app::App app(argc, const_cast<const char**>(argv)); | ||||
|     app::App app; | ||||
| 
 | ||||
|     // Change the name of the memory dump file
 | ||||
|     { | ||||
|  | @ -79,7 +79,9 @@ int app_main(int argc, char* argv[]) | |||
|         memoryDump.setFileName(filename); | ||||
|     } | ||||
| 
 | ||||
|     return app.run(); | ||||
|     app.initialize(argc, const_cast<const char**>(argv)); | ||||
|     app.run(); | ||||
|     return 0; | ||||
|   } | ||||
|   catch (std::exception& e) { | ||||
|     std::cerr << e.what() << '\n'; | ||||
|  |  | |||
|  | @ -75,20 +75,27 @@ Accelerator::Accelerator(const std::string& str) | |||
|     // Scancode
 | ||||
| 
 | ||||
|     // Word with one character
 | ||||
|     else if (tok.size() == 1) { | ||||
|       if ((tok[0] >= 'a') && (tok[0] <= 'z')) { | ||||
|         m_unicodeChar = tok[0]; | ||||
|       } | ||||
|       else { | ||||
|         m_unicodeChar = tok[0]; | ||||
|     else if (base::utf8_length(tok) == 1) { | ||||
|       std::wstring wstr = base::from_utf8(tok); | ||||
|       if (wstr.size() != 1) { | ||||
|         ASSERT(false && "Something wrong converting utf-8 to wchar string"); | ||||
|         continue; | ||||
|       } | ||||
| 
 | ||||
|       if ((tok[0] >= 'a') && (tok[0] <= 'z')) | ||||
|         m_scancode = (KeyScancode)((int)kKeyA + std::tolower(tok[0]) - 'a'); | ||||
|       else if ((tok[0] >= '0') && (tok[0] <= '9')) | ||||
|         m_scancode = (KeyScancode)((int)kKey0 + tok[0] - '0'); | ||||
|       wchar_t wchr = wstr[0]; | ||||
|       wchr = tolower(wchr); | ||||
| 
 | ||||
|       if ((wchr >= 'a') && (wchr <= 'z')) { | ||||
|         m_unicodeChar = wchr; | ||||
|         m_scancode = (KeyScancode)((int)kKeyA + wchr - 'a'); | ||||
|       } | ||||
|       else { | ||||
|         switch (tok[0]) { | ||||
|         m_unicodeChar = wchr; | ||||
| 
 | ||||
|         if ((wchr >= '0') && (wchr <= '9')) | ||||
|           m_scancode = (KeyScancode)((int)kKey0 + wchr - '0'); | ||||
|         else { | ||||
|           switch (wchr) { | ||||
|             case '~': m_scancode = kKeyTilde; break; | ||||
|             case '-': m_scancode = kKeyMinus; break; | ||||
|             case '=': m_scancode = kKeyEquals; break; | ||||
|  | @ -104,6 +111,7 @@ Accelerator::Accelerator(const std::string& str) | |||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     // Other ones
 | ||||
|     else { | ||||
|       // F1, F2, ..., F11, F12
 | ||||
|  |  | |||
|  | @ -105,7 +105,6 @@ bool TextBox::onProcessMessage(Message* msg) | |||
|     case kMouseMoveMessage: { | ||||
|       View* view = View::getView(this); | ||||
|       if (view && hasCapture()) { | ||||
|         gfx::Rect vp = view->getViewportBounds(); | ||||
|         gfx::Point scroll = view->getViewScroll(); | ||||
|         gfx::Point newPos = static_cast<MouseMessage*>(msg)->position(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue