mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-30 04:20:23 +00:00
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:
|
||||
/* App is regaining focus */
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
|
222
src/app/app.cpp
222
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,25 +210,127 @@ 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) {
|
||||
// Load the sprite
|
||||
Document* document = load_document(context, filename.c_str());
|
||||
if (!document) {
|
||||
if (!isGui())
|
||||
console.printf("Error loading file \"%s\"\n", filename.c_str());
|
||||
}
|
||||
else {
|
||||
// Add the given file in the argument as a "recent file" only
|
||||
// if we are running in GUI mode. If the program is executed
|
||||
// in batch mode this is not desirable.
|
||||
if (isGui())
|
||||
getRecentFiles()->addRecentFile(filename.c_str());
|
||||
bool splitLayers = false;
|
||||
std::string importLayer;
|
||||
|
||||
// Add the document to the exporter.
|
||||
if (m_exporter != NULL)
|
||||
m_exporter->addDocument(document);
|
||||
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* doc = load_document(ctx, filename.c_str());
|
||||
if (!doc) {
|
||||
if (!isGui())
|
||||
console.printf("Error loading file \"%s\"\n", filename.c_str());
|
||||
}
|
||||
else {
|
||||
// Add the given file in the argument as a "recent file" only
|
||||
// if we are running in GUI mode. If the program is executed
|
||||
// in batch mode this is not desirable.
|
||||
if (isGui())
|
||||
getRecentFiles()->addRecentFile(filename.c_str());
|
||||
|
||||
if (m_exporter != NULL) {
|
||||
if (!importLayer.empty()) {
|
||||
std::vector<Layer*> layers;
|
||||
Layer* foundLayer = NULL;
|
||||
doc->sprite()->getLayersList(layers);
|
||||
for (Layer* layer : layers) {
|
||||
if (layer->name() == importLayer) {
|
||||
foundLayer = layer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundLayer)
|
||||
m_exporter->addDocument(doc, foundLayer);
|
||||
}
|
||||
else if (splitLayers) {
|
||||
std::vector<Layer*> layers;
|
||||
doc->sprite()->getLayersList(layers);
|
||||
for (auto layer : layers)
|
||||
m_exporter->addDocument(doc, layer);
|
||||
}
|
||||
else
|
||||
m_exporter->addDocument(doc);
|
||||
}
|
||||
}
|
||||
|
||||
if (!importLayer.empty())
|
||||
importLayer.clear();
|
||||
|
||||
if (splitLayers)
|
||||
splitLayers = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,32 +365,10 @@ int App::run()
|
||||
|
||||
// Run the GUI main message loop
|
||||
gui_run();
|
||||
|
||||
// Destroy all documents in the UIContext.
|
||||
const doc::Documents& docs = m_modules->m_ui_context.documents();
|
||||
while (!docs.empty()) {
|
||||
doc::Document* doc = docs.back();
|
||||
|
||||
// First we close the document. In this way we receive recent
|
||||
// notifications related to the document as an app::Document. If
|
||||
// we delete the document directly, we destroy the app::Document
|
||||
// too early, and then doc::~Document() call
|
||||
// DocumentsObserver::onRemoveDocument(). In this way, observers
|
||||
// could think that they have a fully created app::Document when
|
||||
// in reality it's a doc::Document (in the middle of a
|
||||
// destruction process).
|
||||
//
|
||||
// TODO: This problem is because we're extending doc::Document,
|
||||
// in the future, we should remove app::Document.
|
||||
doc->close();
|
||||
delete doc;
|
||||
}
|
||||
|
||||
// Destroy the window.
|
||||
m_mainWindow.reset(NULL);
|
||||
}
|
||||
|
||||
// Start shell to execute scripts.
|
||||
else if (m_isShell) {
|
||||
if (m_isShell) {
|
||||
m_systemConsole.prepareShell();
|
||||
|
||||
if (m_modules->m_scriptingEngine.supportEval()) {
|
||||
@ -302,7 +380,30 @@ int App::run()
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
// Destroy all documents in the UIContext.
|
||||
const doc::Documents& docs = m_modules->m_ui_context.documents();
|
||||
while (!docs.empty()) {
|
||||
doc::Document* doc = docs.back();
|
||||
|
||||
// First we close the document. In this way we receive recent
|
||||
// notifications related to the document as an app::Document. If
|
||||
// we delete the document directly, we destroy the app::Document
|
||||
// too early, and then doc::~Document() call
|
||||
// DocumentsObserver::onRemoveDocument(). In this way, observers
|
||||
// could think that they have a fully created app::Document when
|
||||
// in reality it's a doc::Document (in the middle of a
|
||||
// destruction process).
|
||||
//
|
||||
// TODO: This problem is because we're extending doc::Document,
|
||||
// in the future, we should remove app::Document.
|
||||
doc->close();
|
||||
delete doc;
|
||||
}
|
||||
|
||||
if (isGui()) {
|
||||
// Destroy the window.
|
||||
m_mainWindow.reset(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Finishes the Aseprite application.
|
||||
@ -405,7 +506,8 @@ void app_refresh_screen()
|
||||
|
||||
void app_rebuild_documents_tabs()
|
||||
{
|
||||
App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
|
||||
if (App::instance()->isGui())
|
||||
App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
|
||||
}
|
||||
|
||||
PixelFormat app_get_current_pixel_format()
|
||||
|
@ -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,58 +211,63 @@ bool SpriteSizeCommand::onEnabled(Context* context)
|
||||
|
||||
void SpriteSizeCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
|
||||
const ContextReader reader(context);
|
||||
const Sprite* sprite(reader.sprite());
|
||||
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;
|
||||
|
||||
// load the window widget
|
||||
base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size"));
|
||||
m_widthPx = app::find_widget<Entry>(window, "width_px");
|
||||
m_heightPx = app::find_widget<Entry>(window, "height_px");
|
||||
m_widthPerc = app::find_widget<Entry>(window, "width_perc");
|
||||
m_heightPerc = app::find_widget<Entry>(window, "height_perc");
|
||||
m_lockRatio = app::find_widget<CheckBox>(window, "lock_ratio");
|
||||
ComboBox* method = app::find_widget<ComboBox>(window, "method");
|
||||
Widget* ok = app::find_widget<Widget>(window, "ok");
|
||||
if (context->isUiAvailable()) {
|
||||
// load the window widget
|
||||
base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size"));
|
||||
m_widthPx = app::find_widget<Entry>(window, "width_px");
|
||||
m_heightPx = app::find_widget<Entry>(window, "height_px");
|
||||
m_widthPerc = app::find_widget<Entry>(window, "width_perc");
|
||||
m_heightPerc = app::find_widget<Entry>(window, "height_perc");
|
||||
m_lockRatio = app::find_widget<CheckBox>(window, "lock_ratio");
|
||||
ComboBox* method = app::find_widget<ComboBox>(window, "method");
|
||||
Widget* ok = app::find_widget<Widget>(window, "ok");
|
||||
|
||||
m_widthPx->setTextf("%d", 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));
|
||||
m_heightPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPxChange, this));
|
||||
m_widthPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPercChange, this));
|
||||
m_heightPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPercChange, this));
|
||||
m_lockRatio->Click.connect(Bind<void>(&SpriteSizeCommand::onLockRatioClick, this));
|
||||
m_widthPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPxChange, this));
|
||||
m_heightPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPxChange, this));
|
||||
m_widthPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPercChange, this));
|
||||
m_heightPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPercChange, this));
|
||||
|
||||
method->addItem("Nearest-neighbor");
|
||||
method->addItem("Bilinear");
|
||||
method->setSelectedItemIndex(get_config_int("SpriteSize", "Method",
|
||||
doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR));
|
||||
method->addItem("Nearest-neighbor");
|
||||
method->addItem("Bilinear");
|
||||
method->setSelectedItemIndex(get_config_int("SpriteSize", "Method",
|
||||
doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR));
|
||||
|
||||
window->remapWindow();
|
||||
window->centerWindow();
|
||||
window->remapWindow();
|
||||
window->centerWindow();
|
||||
|
||||
load_window_pos(window, "SpriteSize");
|
||||
window->setVisible(true);
|
||||
window->openWindowInForeground();
|
||||
save_window_pos(window, "SpriteSize");
|
||||
load_window_pos(window, "SpriteSize");
|
||||
window->setVisible(true);
|
||||
window->openWindowInForeground();
|
||||
save_window_pos(window, "SpriteSize");
|
||||
|
||||
if (window->getKiller() == ok) {
|
||||
int new_width = m_widthPx->getTextInt();
|
||||
int new_height = m_heightPx->getTextInt();
|
||||
ResizeMethod resize_method =
|
||||
(ResizeMethod)method->getSelectedItemIndex();
|
||||
if (window->getKiller() != ok)
|
||||
return;
|
||||
|
||||
new_width = m_widthPx->getTextInt();
|
||||
new_height = m_heightPx->getTextInt();
|
||||
resize_method = (ResizeMethod)method->getSelectedItemIndex();
|
||||
|
||||
set_config_int("SpriteSize", "Method", resize_method);
|
||||
|
||||
{
|
||||
SpriteSizeJob job(reader, new_width, new_height, resize_method);
|
||||
job.startJob();
|
||||
job.waitJob();
|
||||
}
|
||||
|
||||
ContextWriter writer(reader);
|
||||
update_screen_for_document(writer.document());
|
||||
}
|
||||
|
||||
{
|
||||
SpriteSizeJob job(reader, new_width, new_height, resize_method);
|
||||
job.startJob();
|
||||
job.waitJob();
|
||||
}
|
||||
|
||||
ContextWriter writer(reader);
|
||||
update_screen_for_document(writer.document());
|
||||
}
|
||||
|
||||
void SpriteSizeCommand::onLockRatioClick()
|
||||
|
72
src/app/commands/cmd_sprite_size.h
Normal file
72
src/app/commands/cmd_sprite_size.h
Normal file
@ -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()) {
|
||||
framePt.x = 0;
|
||||
framePt.y += size.h;
|
||||
}
|
||||
else {
|
||||
framePt.x += size.w;
|
||||
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 += 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 ?
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
SimpleLayoutSamples layout;
|
||||
layout.layoutSamples(samples);
|
||||
if (m_texturePack) {
|
||||
BestFitLayoutSamples layout;
|
||||
layout.layoutSamples(samples, m_textureWidth, m_textureHeight);
|
||||
}
|
||||
else {
|
||||
SimpleLayoutSamples layout;
|
||||
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,24 +45,29 @@ Job::Job(const char* job_name)
|
||||
m_canceled_flag = false;
|
||||
|
||||
m_mutex = new base::mutex();
|
||||
m_progress = StatusBar::instance()->addProgress();
|
||||
m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", job_name);
|
||||
|
||||
m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window));
|
||||
m_timer->Tick.connect(&Job::onMonitoringTick, this);
|
||||
m_timer->start();
|
||||
if (App::instance()->isGui()) {
|
||||
m_progress = StatusBar::instance()->addProgress();
|
||||
m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", jobName);
|
||||
|
||||
m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window));
|
||||
m_timer->Tick.connect(&Job::onMonitoringTick, this);
|
||||
m_timer->start();
|
||||
}
|
||||
}
|
||||
|
||||
Job::~Job()
|
||||
{
|
||||
ASSERT(!m_timer->isRunning());
|
||||
ASSERT(m_thread == NULL);
|
||||
if (App::instance()->isGui()) {
|
||||
ASSERT(!m_timer->isRunning());
|
||||
ASSERT(m_thread == NULL);
|
||||
|
||||
if (m_alert_window != NULL)
|
||||
m_alert_window->closeWindow(NULL);
|
||||
if (m_alert_window != NULL)
|
||||
m_alert_window->closeWindow(NULL);
|
||||
|
||||
if (m_progress)
|
||||
delete m_progress;
|
||||
if (m_progress)
|
||||
delete m_progress;
|
||||
}
|
||||
|
||||
if (m_mutex)
|
||||
delete m_mutex;
|
||||
@ -70,19 +76,22 @@ Job::~Job()
|
||||
void Job::startJob()
|
||||
{
|
||||
m_thread = new base::thread(&Job::thread_proc, this);
|
||||
m_alert_window->openWindowInForeground();
|
||||
|
||||
// The job was canceled by the user?
|
||||
{
|
||||
base::scoped_lock hold(*m_mutex);
|
||||
if (!m_done_flag)
|
||||
m_canceled_flag = true;
|
||||
if (m_alert_window) {
|
||||
m_alert_window->openWindowInForeground();
|
||||
|
||||
// The job was canceled by the user?
|
||||
{
|
||||
base::scoped_lock hold(*m_mutex);
|
||||
if (!m_done_flag)
|
||||
m_canceled_flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Job::waitJob()
|
||||
{
|
||||
if (m_timer->isRunning())
|
||||
if (m_timer && m_timer->isRunning())
|
||||
m_timer->stop();
|
||||
|
||||
if (m_thread) {
|
||||
|
@ -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,7 +220,8 @@ void update_screen_for_document(Document* document)
|
||||
// Well, change to the default palette.
|
||||
if (set_current_palette(NULL, false)) {
|
||||
// If the palette changes, refresh the whole screen.
|
||||
Manager::getDefault()->invalidate();
|
||||
if (Manager::getDefault())
|
||||
Manager::getDefault()->invalidate();
|
||||
}
|
||||
}
|
||||
// With a document.
|
||||
|
@ -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,17 +1328,19 @@ 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;
|
||||
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);
|
||||
widget->setAlign(JI_RIGHT | JI_MIDDLE);
|
||||
drawTextString(g, buf.c_str(), fg, ColorNone, widget, pos, 0);
|
||||
widget->setAlign(old_align);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,7 +231,8 @@ std::ostream& operator<<(std::ostream& os, const base::ProgramOptions& po)
|
||||
}
|
||||
}
|
||||
}
|
||||
os << "\n";
|
||||
else
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
return os;
|
||||
|
@ -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)
|
||||
|
98
src/gfx/packing_rects.cpp
Normal file
98
src/gfx/packing_rects.cpp
Normal file
@ -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
|
53
src/gfx/packing_rects.h
Normal file
53
src/gfx/packing_rects.h
Normal file
@ -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
|
96
src/gfx/packing_rects_tests.cpp
Normal file
96
src/gfx/packing_rects_tests.cpp
Normal file
@ -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();
|
||||
}
|
27
src/gfx/rect_io.h
Normal file
27
src/gfx/rect_io.h
Normal file
@ -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,32 +75,40 @@ 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]) {
|
||||
case '~': m_scancode = kKeyTilde; break;
|
||||
case '-': m_scancode = kKeyMinus; break;
|
||||
case '=': m_scancode = kKeyEquals; break;
|
||||
case '[': m_scancode = kKeyOpenbrace; break;
|
||||
case ']': m_scancode = kKeyClosebrace; break;
|
||||
case ';': m_scancode = kKeyColon; break;
|
||||
case '\'': m_scancode = kKeyQuote; break;
|
||||
case '\\': m_scancode = kKeyBackslash; break;
|
||||
case ',': m_scancode = kKeyComma; break;
|
||||
case '.': m_scancode = kKeyStop; break;
|
||||
case '/': m_scancode = kKeySlash; break;
|
||||
case '*': m_scancode = kKeyAsterisk; break;
|
||||
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;
|
||||
case '[': m_scancode = kKeyOpenbrace; break;
|
||||
case ']': m_scancode = kKeyClosebrace; break;
|
||||
case ';': m_scancode = kKeyColon; break;
|
||||
case '\'': m_scancode = kKeyQuote; break;
|
||||
case '\\': m_scancode = kKeyBackslash; break;
|
||||
case ',': m_scancode = kKeyComma; break;
|
||||
case '.': m_scancode = kKeyStop; break;
|
||||
case '/': m_scancode = kKeySlash; break;
|
||||
case '*': m_scancode = kKeyAsterisk; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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…
x
Reference in New Issue
Block a user