Merge branch 'beta' into master

The next official release will be v1.2.
This commit is contained in:
David Capello 2017-01-30 16:02:21 -03:00
commit 79f82cfbee
234 changed files with 10504 additions and 4540 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Aseprite -->
<!-- Copyright (C) 2001-2017 by David Capello -->
<gui version="1.1.12-dev">
<gui version="1.2-dev">
<!-- Keyboard shortcuts -->
<keyboard version="1">
@ -61,7 +61,11 @@
<!-- Layer -->
<key command="LayerProperties" shortcut="Shift+P" />
<key command="LayerVisibility" shortcut="Shift+X" />
<key command="OpenGroup" shortcut="Shift+E" />
<key command="NewLayer" shortcut="Shift+N" />
<key command="NewLayer" shortcut="Alt+Shift+N">
<param name="group" value="true" />
</key>
<key command="GotoPreviousLayer" shortcut="Down" context="Normal" />
<key command="GotoNextLayer" shortcut="Up" context="Normal" />
<!-- Frame -->
@ -106,6 +110,7 @@
<key command="SnapToGrid" shortcut="Shift+S" />
<key command="SetLoopSection" shortcut="F2" />
<key command="ShowOnionSkin" shortcut="F3" />
<key command="ToggleTimelineThumbnails" shortcut="F6" />
<key command="Timeline" shortcut="Tab">
<param name="switch" value="true" />
</key>
@ -676,8 +681,12 @@
<menu text="&amp;Layer">
<item command="LayerProperties" text="&amp;Properties..." />
<item command="LayerVisibility" text="&amp;Visible" />
<item command="OpenGroup" text="&amp;Open Group" />
<separator />
<item command="NewLayer" text="&amp;New Layer" />
<item command="NewLayer" text="New &amp;Group">
<param name="group" value="true" />
</item>
<item command="RemoveLayer" text="&amp;Remove Layer" />
<item command="BackgroundFromLayer" text="&amp;Background from Layer" />
<item command="LayerFromBackground" text="&amp;Layer from Background" />
@ -685,6 +694,11 @@
<item command="DuplicateLayer" text="&amp;Duplicate" />
<item command="MergeDownLayer" text="&amp;Merge Down" />
<item command="FlattenLayers" text="&amp;Flatten" />
<separator />
<item command="NewLayer" text="Add R&amp;eference Layer" >
<param name="reference" value="true" />
<param name="from-file" value="true" />
</item>
</menu>
<menu text="F&amp;rame">
<item command="FrameProperties" text="Frame &amp;Properties...">
@ -845,6 +859,9 @@
<item command="LayerProperties" text="&amp;Properties..." />
<separator />
<item command="NewLayer" text="&amp;New" />
<item command="NewLayer" text="New &amp;Group" >
<param name="group" value="true" />
</item>
<item command="RemoveLayer" text="&amp;Remove" />
<item command="BackgroundFromLayer" text="&amp;Background from Layer" />
<item command="LayerFromBackground" text="&amp;Layer from Background" />

View File

@ -57,6 +57,7 @@
<enum id="EyedropperSample">
<value id="ALL_LAYERS" value="0" />
<value id="CURRENT_LAYER" value="1" />
<value id="FIRST_REFERENCE_LAYER" value="2" />
</enum>
<enum id="SelectionMode">
<value id="DEFAULT" value="0" />
@ -141,6 +142,7 @@
<section id="experimental" text="Experimental">
<option id="use_native_file_dialog" type="bool" default="false" />
<option id="flash_layer" type="bool" default="false" migrate="Options.FlashLayer" />
<option id="nonactive_layers_opacity" type="int" default="255" />
</section>
<section id="news">
<option id="cache_file" type="std::string" />
@ -210,6 +212,8 @@
<option id="height" type="int" default="32" />
<option id="color_mode" type="doc::PixelFormat" default="doc::IMAGE_RGB" />
<option id="background_color" type="int" default="0" />
<option id="advanced" type="bool" default="false" />
<option id="pixel_ratio" type="std::string" />
</section>
<section id="text_tool">
<option id="font_face" type="std::string" />
@ -219,6 +223,12 @@
<section id="symmetry_mode">
<option id="enabled" type="bool" default="false" />
</section>
<section id="thumbnails">
<option id="cache_limit" type="int" default="5000" />
</section>
<section id="perf">
<option id="show_render_time" type="bool" default="false" />
</section>
</global>
<tool>
@ -247,7 +257,7 @@
<document>
<section id="site">
<option id="frame" type="doc::frame_t" default="doc::frame_t(0)" />
<option id="layer" type="doc::LayerIndex" />
<option id="layer" type="doc::layer_t" default="doc::layer_t(0)" />
</section>
<section id="tiled">
<option id="mode" type="filters::TiledMode" default="filters::TiledMode::NONE" migrate="Tools.Tiled" />
@ -278,6 +288,12 @@
<section id="timeline">
<option id="first_frame" type="int" default="1" />
</section>
<section id="thumbnails">
<option id="zoom" type="double" default="1" />
<option id="enabled" type="bool" default="false" />
<option id="overlay_enabled" type="bool" default="false" />
<option id="overlay_size" type="int" default="5" />
</section>
<section id="onionskin">
<option id="active" type="bool" default="false" migrate="Onionskin.Enabled" />
<option id="prev_frames" type="int" default="1" migrate="Onionskin.PrevFrames" />
@ -292,6 +308,9 @@
<section id="save_copy">
<option id="filename" type="std::string" />
<option id="resize_scale" type="double" default="1" />
<option id="layer" type="std::string" />
<option id="frame_tag" type="std::string" />
<option id="apply_pixel_ratio" type="bool" default="false" />
</section>
<section id="sprite_sheet">
<option id="defined" type="bool" default="false" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -18,6 +18,7 @@
<dim id="timeline_outline_width" value="2" />
<dim id="palette_outline_width" value="3" />
<dim id="color_slider_height" value="14" />
<dim id="timeline_base_size" value="12" />
</dimensions>
<colors>
@ -331,6 +332,10 @@
<part id="timeline_continuous_active" x="288" y="36" w="12" h="12" />
<part id="timeline_discontinuous_normal" x="276" y="48" w="12" h="12" />
<part id="timeline_discontinuous_active" x="288" y="48" w="12" h="12" />
<part id="timeline_closed_group_normal" x="276" y="60" w="12" h="12" />
<part id="timeline_closed_group_active" x="288" y="60" w="12" h="12" />
<part id="timeline_open_group_normal" x="276" y="72" w="12" h="12" />
<part id="timeline_open_group_active" x="288" y="72" w="12" h="12" />
<part id="timeline_empty_frame_normal" x="240" y="60" w="12" h="12" />
<part id="timeline_empty_frame_active" x="252" y="60" w="12" h="12" />
<part id="timeline_keyframe_normal" x="240" y="72" w="12" h="12" />
@ -537,12 +542,18 @@
<style id="timeline_open_eye:active">
<icon part="timeline_open_eye_active" />
</style>
<style id="timeline_open_eye:disabled">
<icon color="disabled" />
</style>
<style id="timeline_closed_eye" base="timeline_box">
<icon part="timeline_closed_eye_normal" align="center" valign="middle" />
</style>
<style id="timeline_closed_eye:active">
<icon part="timeline_closed_eye_active" />
</style>
<style id="timeline_closed_eye:disabled">
<icon color="disabled" />
</style>
<!-- timeline_padlock -->
<style id="timeline_open_padlock" base="timeline_box">
@ -551,12 +562,18 @@
<style id="timeline_open_padlock:active">
<icon part="timeline_open_padlock_active" />
</style>
<style id="timeline_open_padlock:disabled">
<icon color="disabled" />
</style>
<style id="timeline_closed_padlock" base="timeline_box">
<icon part="timeline_closed_padlock_normal" align="center" valign="middle" />
</style>
<style id="timeline_closed_padlock:active">
<icon part="timeline_closed_padlock_active" />
</style>
<style id="timeline_closed_padlock:disabled">
<icon color="disabled" />
</style>
<!-- timeline_continuous -->
<style id="timeline_continuous" base="timeline_box">
@ -572,6 +589,20 @@
<icon part="timeline_discontinuous_active" />
</style>
<!-- timeline_group -->
<style id="timeline_closed_group" base="timeline_box">
<icon part="timeline_closed_group_normal" align="center" valign="middle" />
</style>
<style id="timeline_closed_group:active">
<icon part="timeline_closed_group_active" />
</style>
<style id="timeline_open_group" base="timeline_box">
<icon part="timeline_open_group_normal" align="center" valign="middle" />
</style>
<style id="timeline_open_group:active">
<icon part="timeline_open_group_active" />
</style>
<!-- timeline_layer -->
<style id="timeline_layer" base="timeline_box">
<text align="left" valign="middle" padding-left="4" />

View File

@ -20,23 +20,11 @@
<label text="File type:" />
<hbox cell_align="horizontal">
<combobox id="file_type" minwidth="70" />
<hbox id="resize_options">
<label text="Resize:" />
<combobox id="resize">
<listitem text="25%" value="0.25" />
<listitem text="50%" value="0.5" />
<listitem text="100%" value="1" />
<listitem text="200%" value="2" />
<listitem text="300%" value="3" />
<listitem text="400%" value="4" />
<listitem text="500%" value="5" />
<listitem text="600%" value="6" />
<listitem text="700%" value="7" />
<listitem text="800%" value="8" />
<listitem text="900%" value="9" />
<listitem text="1000%" value="10" />
</combobox>
</hbox>
<vbox>
<boxfiller />
<link id="extra_options" text="" />
<boxfiller />
</vbox>
<boxfiller />
<box horizontal="true" homogeneous="true">
<button text="&amp;OK" closewindow="true" id="ok" magnet="true" width="60" />

View File

@ -0,0 +1,31 @@
<!-- ASEPRITE -->
<!-- Copyright (C) 2016 by David Capello -->
<gui>
<vbox id="file_selector_extras">
<grid columns="2">
<label id="resize_label" text="Resize:" />
<combobox id="resize" cell_align="horizontal">
<listitem text="25%" value="0.25" />
<listitem text="50%" value="0.5" />
<listitem text="100%" value="1" />
<listitem text="200%" value="2" />
<listitem text="300%" value="3" />
<listitem text="400%" value="4" />
<listitem text="500%" value="5" />
<listitem text="600%" value="6" />
<listitem text="700%" value="7" />
<listitem text="800%" value="8" />
<listitem text="900%" value="9" />
<listitem text="1000%" value="10" />
</combobox>
<label id="layers_label" text="Layers:" />
<combobox id="layers" text="" cell_align="horizontal" />
<label id="frames_label" text="Frames:" />
<combobox id="frames" text="" cell_align="horizontal" />
<check id="pixel_ratio" text="Apply pixel ratio" cell_hspan="2" />
</grid>
</vbox>
</gui>

View File

@ -1,32 +1,20 @@
<!-- ASEPRITE -->
<!-- Copyright (C) 2001-2013 by David Capello -->
<!-- Copyright (C) 2001-2016 by David Capello -->
<gui>
<window text="New Image Layer" id="new_layer">
<box vertical="true">
<box horizontal="true">
<box vertical="true" homogeneous="true">
<label text="Name:" />
<window text="New Layer" id="new_layer">
<box vertical="true">
<box horizontal="true">
<box vertical="true" homogeneous="true">
<label text="Name:" />
</box>
<box vertical="true" homogeneous="true">
<entry maxsize="256" text="New Layer" id="name" magnet="true" />
</box>
</box>
<box vertical="true" homogeneous="true">
<entry maxsize="256" text="New Layer" id="name" magnet="true" />
<box horizontal="true" homogeneous="true">
<button text="&amp;OK" closewindow="true" id="ok" magnet="true" />
<button text="&amp;Cancel" closewindow="true" />
</box>
</box>
<box horizontal="true" homogeneous="true">
<button text="&amp;OK" closewindow="true" id="ok" magnet="true" />
<button text="&amp;Cancel" closewindow="true" />
</box>
</box>
</window>
<window text="New Layer Set" id="new_layer_set">
<box vertical="true">
<box horizontal="true">
<label text="Name:" />
<entry maxsize="256" text="New Set" id="name" />
</box>
<box horizontal="true" homogeneous="true">
<button text="&amp;OK" closewindow="true" id="ok" magnet="true" />
<button text="&amp;Cancel" closewindow="true" />
</box>
</box>
</window>
</window>
</gui>

View File

@ -1,5 +1,5 @@
<!-- ASEPRITE -->
<!-- Copyright (C) 2001-2013, 2015 by David Capello -->
<!-- Copyright (C) 2001-2016 by David Capello -->
<gui>
<window text="New Sprite" id="new_sprite">
<box vertical="true">
@ -26,6 +26,18 @@
<item text="&amp;Black" icon="icon_black" />
</buttonset>
<check id="advanced_check" text="Advanced Options" />
<vbox id="advanced">
<grid columns="2">
<label text="Pixel Ratio:" />
<combobox id="pixel_ratio" cell_align="horizontal">
<listitem text="Square Pixels (1:1)" value="1:1" />
<listitem text="Double-wide Pixels (2:1)" value="2:1" />
<listitem text="Double-high Pixels (1:2)" value="1:2" />
</combobox>
</grid>
</vbox>
<box horizontal="true">
<box horizontal="true" expansive="true" />
<box horizontal="true" homogeneous="true">

View File

@ -240,6 +240,10 @@
<separator text="User Interface" horizontal="true" />
<check id="native_file_dialog" text="Use native file dialog" />
<check id="flash_layer" text="Flash layer when it is selected" />
<hbox>
<label text="Opacity for non-active layers:" />
<slider id="nonactive_layers_opacity" min="0" max="255" width="128" />
</hbox>
</vbox>
</panel>

View File

@ -1,5 +1,5 @@
<!-- ASEPRITE -->
<!-- Copyright (C) 2001-2013, 2015 by David Capello -->
<!-- Copyright (C) 2001-2016 by David Capello -->
<gui>
<window text="Sprite Properties" id="sprite_properties">
<vbox>
@ -16,8 +16,18 @@
<label text="Frames:" />
<label text="" id="frames" />
<separator text="Advanced" horizontal="true" cell_hspan="2" />
<label text="Transparent Color:" />
<hbox id="transparent_color_placeholder" tooltip="Palette entry used as&#10;transparent color in each&#10;layer (only for indexed images)." />
<label text="Pixel Ratio:" />
<combobox id="pixel_ratio" cell_align="horizontal">
<listitem text="Square Pixels (1:1)" value="1:1" />
<listitem text="Double-wide Pixels (2:1)" value="2:1" />
<listitem text="Double-high Pixels (1:2)" value="1:2" />
</combobox>
</grid>
<separator horizontal="true" />
<hbox>

View File

@ -19,6 +19,17 @@
<label text="First Frame:" />
<entry id="first_frame" maxsize="3" />
</hbox>
<separator cell_hspan="2" text="Thumbnails:" left="true" horizontal="true" />
<grid columns="3">
<check id="thumb_enabled" text="Force" />
<label text="Zoom:" />
<slider min="1" max="10" id="zoom" cell_align="horizontal" width="128" />
<check id="thumb_overlay_enabled" text="Overlay"/>
<label text="Size:" />
<slider min="2" max="10" id="thumb_overlay_size" cell_align="horizontal" width="128" />
</grid>
</vbox>
</hbox>

View File

@ -21,6 +21,7 @@ BYTE | An 8-bit unsigned integer value
WORD | A 16-bit unsigned integer value
SIGNED WORD | A 16-bit signed integer value
DWORD | A 32-bit unsigned integer value
FIXED | A 32-bit fixed point (16.16) value
BYTE[n] | "n" bytes.
STRING | WORD: string length (number of bytes)
| BYTE[length]: characters (in UTF-8)
@ -72,7 +73,9 @@ DWORD | Set be 0
BYTE | Palette entry (index) which represent transparent color in all non-background layers (only for Indexed sprites).
BYTE[3] | Ignore these bytes
WORD | Number of colors (0 means 256 for old sprites)
BYTE[94] | For future (set to zero)
BYTE | Pixel width (pixel ratio is "pixel width/pixel height"). If this or pixel height field is zero, pixel ratio is 1:1
BYTE | Pixel height
BYTE[92] | For future (set to zero)
4. Frames
---------
@ -143,7 +146,11 @@ Ignore this chunk if you find the new palette chunk (0x2019)
4 = Lock movement
8 = Background
16 = Prefer linked cels
WORD Layer type (0=normal (image) layer, 1=layer set)
32 = The layer group should be displayed collapsed
64 = The layer is a reference layer
WORD Layer type
0 = Normal (image) layer
1 = Group
WORD Layer child level (see NOTE.1)
WORD Default layer width in pixels (ignored)
WORD Default layer height in pixels (ignored)
@ -194,7 +201,7 @@ Ignore this chunk if you find the new palette chunk (0x2019)
WORD Frame position to link with
+ For cel type = 2 (compressed image):
+ For cel type = 2 (Compressed Image):
WORD Width in pixels
WORD Height in pixels
@ -203,6 +210,20 @@ Ignore this chunk if you find the new palette chunk (0x2019)
http://www.ietf.org/rfc/rfc1951.txt
```
### Cel Extra Chunk (0x2006)
Adds extra information to the latest read cel.
```
DWORD Flags (set to zero)
1 - precise bounds are set
FIXED Precise X position
FIXED Precise Y position
FIXED Width of the cel in the sprite (scaled in real-time)
FIXED Height of the cel in the sprite
BYTE[16] For future use (set to zero)
```
### Mask Chunk (0x2016) DEPRECATED
```
@ -217,12 +238,10 @@ Ignore this chunk if you find the new palette chunk (0x2019)
packed into the high order bits)
```
### Path Chunk (0x2017)
Never used.
### Frame Tags Chunk (0x2018)
```

View File

@ -195,6 +195,7 @@ if(ENABLE_TESTS)
find_tests(render render-lib)
find_tests(css css-lib)
find_tests(ui ui-lib)
find_tests(app/cli app-lib)
find_tests(app/file app-lib)
find_tests(app app-lib)
find_tests(. app-lib)

View File

@ -114,9 +114,13 @@ add_library(app-lib
app.cpp
app_brushes.cpp
app_menus.cpp
app_options.cpp
app_render.cpp
check_update.cpp
cli/app_options.cpp
cli/cli_open_file.cpp
cli/cli_processor.cpp
cli/default_cli_delegate.cpp
cli/preview_cli_delegate.cpp
cmd.cpp
cmd/add_cel.cpp
cmd/add_frame.cpp
@ -151,6 +155,7 @@ add_library(app-lib
cmd/remove_palette.cpp
cmd/replace_image.cpp
cmd/reselect_mask.cpp
cmd/set_cel_bounds.cpp
cmd/set_cel_data.cpp
cmd/set_cel_frame.cpp
cmd/set_cel_opacity.cpp
@ -168,6 +173,7 @@ add_library(app-lib
cmd/set_mask_position.cpp
cmd/set_palette.cpp
cmd/set_pixel_format.cpp
cmd/set_pixel_ratio.cpp
cmd/set_sprite_size.cpp
cmd/set_total_frames.cpp
cmd/set_transparent_color.cpp
@ -248,11 +254,11 @@ add_library(app-lib
commands/cmd_new_frame.cpp
commands/cmd_new_frame_tag.cpp
commands/cmd_new_layer.cpp
commands/cmd_new_layer_set.cpp
commands/cmd_new_sprite_from_selection.cpp
commands/cmd_onionskin.cpp
commands/cmd_open_browser.cpp
commands/cmd_open_file.cpp
commands/cmd_open_group.cpp
commands/cmd_open_in_folder.cpp
commands/cmd_open_with_app.cpp
commands/cmd_options.cpp
@ -290,6 +296,7 @@ add_library(app-lib
commands/cmd_tiled_mode.cpp
commands/cmd_timeline.cpp
commands/cmd_toggle_preview.cpp
commands/cmd_toggle_timeline_thumbnails.cpp
commands/cmd_undo.cpp
commands/cmd_undo_history.cpp
commands/cmd_unlink_cel.cpp
@ -346,6 +353,7 @@ add_library(app-lib
res/palettes_loader_delegate.cpp
res/resources_loader.cpp
resource_finder.cpp
restore_visible_layers.cpp
rw_lock.cpp
send_crash.cpp
shade.cpp
@ -410,6 +418,7 @@ add_library(app-lib
ui/icon_button.cpp
ui/input_chain.cpp
ui/keyboard_shortcuts.cpp
ui/layer_frame_comboboxes.cpp
ui/main_menu_bar.cpp
ui/main_window.cpp
ui/news_listbox.cpp
@ -442,6 +451,7 @@ add_library(app-lib
ui/workspace_tabs.cpp
ui/zoom_entry.cpp
ui_context.cpp
thumbnails.cpp
util/autocrop.cpp
util/clipboard.cpp
util/clipboard_native.cpp
@ -452,6 +462,7 @@ add_library(app-lib
util/msk_file.cpp
util/new_image_from_mask.cpp
util/pic_file.cpp
util/pixel_ratio.cpp
util/range_utils.cpp
webserver.cpp
widget_loader.cpp

View File

@ -10,21 +10,18 @@
#include "app/app.h"
#include "app/app_options.h"
#include "app/check_update.h"
#include "app/cli/app_options.h"
#include "app/cli/cli_processor.h"
#include "app/cli/default_cli_delegate.h"
#include "app/cli/preview_cli_delegate.h"
#include "app/color_utils.h"
#include "app/commands/cmd_save_file.h"
#include "app/commands/cmd_sprite_size.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/console.h"
#include "app/crash/data_recovery.h"
#include "app/document_exporter.h"
#include "app/document_undo.h"
#include "app/file/file.h"
#include "app/file/file_formats_manager.h"
#include "app/file_system.h"
#include "app/filename_formatter.h"
#include "app/gui_xml.h"
#include "app/ini_file.h"
#include "app/log.h"
@ -52,18 +49,11 @@
#include "app/ui_context.h"
#include "app/util/clipboard.h"
#include "app/webserver.h"
#include "base/convert_to.h"
#include "base/exception.h"
#include "base/fs.h"
#include "base/scoped_lock.h"
#include "base/split_string.h"
#include "base/unique_ptr.h"
#include "doc/document_observer.h"
#include "doc/frame_tag.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/layers_range.h"
#include "doc/palette.h"
#include "doc/site.h"
#include "doc/sprite.h"
#include "render/render.h"
@ -138,15 +128,6 @@ public:
};
#ifdef ENABLE_SCRIPTING
class StdoutEngineDelegate : public script::EngineDelegate {
public:
void onConsolePrint(const char* text) override {
printf("%s\n", text);
}
};
#endif
App* App::m_instance = NULL;
App::App()
@ -155,7 +136,6 @@ App::App()
, m_legacy(NULL)
, m_isGui(false)
, m_isShell(false)
, m_exporter(NULL)
, m_backupIndicator(nullptr)
{
ASSERT(m_instance == NULL);
@ -164,7 +144,7 @@ App::App()
void App::initialize(const AppOptions& options)
{
m_isGui = options.startUI();
m_isGui = options.startUI() && !options.previewCLI();
m_isShell = options.startShell();
if (m_isGui)
m_uiSystem.reset(new ui::UISystem);
@ -189,25 +169,18 @@ void App::initialize(const AppOptions& options)
m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0);
m_brushes.reset(new AppBrushes);
if (options.hasExporterParams())
m_exporter.reset(new DocumentExporter);
// Data recovery is enabled only in GUI mode
if (isGui() && preferences().general.dataRecovery())
m_modules->createDataRecovery();
// Register well-known image file types.
FileFormatsManager::instance()->registerAllFormats();
if (isPortable())
LOG("APP: Running in portable mode\n");
// Load or create the default palette, or migrate the default
// palette from an old format palette to the new one, etc.
load_default_palette(options.paletteFileName());
load_default_palette();
// Initialize GUI interface
UIContext* ctx = UIContext::instance();
if (isGui()) {
LOG("APP: GUI mode\n");
@ -238,429 +211,15 @@ void App::initialize(const AppOptions& options)
// Procress options
LOG("APP: Processing options...\n");
{
base::UniquePtr<CliDelegate> delegate;
if (options.previewCLI())
delegate.reset(new PreviewCliDelegate);
else
delegate.reset(new DefaultCliDelegate);
bool ignoreEmpty = false;
bool trim = false;
Params cropParams;
SpriteSheetType sheetType = SpriteSheetType::None;
// Open file specified in the command line
if (!options.values().empty()) {
Console console;
bool splitLayers = false;
bool splitLayersSaveAs = false;
bool allLayers = false;
bool listLayers = false;
bool listTags = false;
std::string importLayer;
std::string importLayerSaveAs;
std::string filenameFormat;
std::string frameTagName;
std::string frameRange;
for (const auto& value : options.values()) {
const AppOptions::Option* opt = value.option();
// Special options/commands
if (opt) {
// --data <file.json>
if (opt == &options.data()) {
if (m_exporter)
m_exporter->setDataFilename(value.value());
}
// --format <format>
else if (opt == &options.format()) {
if (m_exporter) {
DocumentExporter::DataFormat format = DocumentExporter::DefaultDataFormat;
if (value.value() == "json-hash")
format = DocumentExporter::JsonHashDataFormat;
else if (value.value() == "json-array")
format = DocumentExporter::JsonArrayDataFormat;
m_exporter->setDataFormat(format);
}
}
// --sheet <file.png>
else if (opt == &options.sheet()) {
if (m_exporter)
m_exporter->setTextureFilename(value.value());
}
// --sheet-width <width>
else if (opt == &options.sheetWidth()) {
if (m_exporter)
m_exporter->setTextureWidth(strtol(value.value().c_str(), NULL, 0));
}
// --sheet-height <height>
else if (opt == &options.sheetHeight()) {
if (m_exporter)
m_exporter->setTextureHeight(strtol(value.value().c_str(), NULL, 0));
}
// --sheet-pack
else if (opt == &options.sheetType()) {
if (value.value() == "horizontal")
sheetType = SpriteSheetType::Horizontal;
else if (value.value() == "vertical")
sheetType = SpriteSheetType::Vertical;
else if (value.value() == "rows")
sheetType = SpriteSheetType::Rows;
else if (value.value() == "columns")
sheetType = SpriteSheetType::Columns;
else if (value.value() == "packed")
sheetType = SpriteSheetType::Packed;
}
// --sheet-pack
else if (opt == &options.sheetPack()) {
sheetType = SpriteSheetType::Packed;
}
// --split-layers
else if (opt == &options.splitLayers()) {
splitLayers = true;
splitLayersSaveAs = true;
}
// --layer <layer-name>
else if (opt == &options.layer()) {
importLayer = value.value();
importLayerSaveAs = value.value();
}
// --all-layers
else if (opt == &options.allLayers()) {
allLayers = true;
}
// --frame-tag <tag-name>
else if (opt == &options.frameTag()) {
frameTagName = value.value();
}
// --frame-range from,to
else if (opt == &options.frameRange()) {
frameRange = value.value();
}
// --ignore-empty
else if (opt == &options.ignoreEmpty()) {
ignoreEmpty = true;
}
// --border-padding
else if (opt == &options.borderPadding()) {
if (m_exporter)
m_exporter->setBorderPadding(strtol(value.value().c_str(), NULL, 0));
}
// --shape-padding
else if (opt == &options.shapePadding()) {
if (m_exporter)
m_exporter->setShapePadding(strtol(value.value().c_str(), NULL, 0));
}
// --inner-padding
else if (opt == &options.innerPadding()) {
if (m_exporter)
m_exporter->setInnerPadding(strtol(value.value().c_str(), NULL, 0));
}
// --trim
else if (opt == &options.trim()) {
trim = true;
}
// --crop x,y,width,height
else if (opt == &options.crop()) {
std::vector<std::string> parts;
base::split_string(value.value(), parts, ",");
if (parts.size() < 4)
throw std::runtime_error("--crop needs four parameters separated by comma (,)\n"
"Usage: --crop x,y,width,height\n"
"E.g. --crop 0,0,32,32");
cropParams.set("x", parts[0].c_str());
cropParams.set("y", parts[1].c_str());
cropParams.set("width", parts[2].c_str());
cropParams.set("height", parts[3].c_str());
}
// --filename-format
else if (opt == &options.filenameFormat()) {
filenameFormat = value.value();
}
// --save-as <filename>
else if (opt == &options.saveAs()) {
Document* doc = NULL;
if (!ctx->documents().empty())
doc = dynamic_cast<Document*>(ctx->documents().lastAdded());
if (!doc) {
console.printf("A document is needed before --save-as argument\n");
}
else {
ctx->setActiveDocument(doc);
std::string format = filenameFormat;
Command* saveAsCommand = CommandsModule::instance()->getCommandByName(CommandId::SaveFileCopyAs);
Command* trimCommand = CommandsModule::instance()->getCommandByName(CommandId::AutocropSprite);
Command* cropCommand = CommandsModule::instance()->getCommandByName(CommandId::CropSprite);
Command* undoCommand = CommandsModule::instance()->getCommandByName(CommandId::Undo);
// --save-as with --split-layers
if (splitLayersSaveAs) {
std::string fn, fmt;
if (format.empty()) {
if (doc->sprite()->totalFrames() > frame_t(1))
format = "{path}/{title} ({layer}) {frame}.{extension}";
else
format = "{path}/{title} ({layer}).{extension}";
}
// Store in "visibility" the original "visible" state of every layer.
std::vector<bool> visibility(doc->sprite()->countLayers());
int i = 0;
for (Layer* layer : doc->sprite()->layers())
visibility[i++] = layer->isVisible();
// For each layer, hide other ones and save the sprite.
i = 0;
for (Layer* show : doc->sprite()->layers()) {
// If the user doesn't want all layers and this one is hidden.
if (!visibility[i++])
continue; // Just ignore this layer.
// Make this layer ("show") the only one visible.
for (Layer* hide : doc->sprite()->layers())
hide->setVisible(hide == show);
FilenameInfo fnInfo;
fnInfo
.filename(value.value())
.layerName(show->name());
fn = filename_formatter(format, fnInfo);
fmt = filename_formatter(format, fnInfo, false);
if (!cropParams.empty())
ctx->executeCommand(cropCommand, cropParams);
// TODO --trim command with --save-as doesn't make too
// much sense as we lost the trim rectangle
// information (e.g. we don't have sheet .json) Also,
// we should trim each frame individually (a process
// that can be done only in fop_operate()).
if (trim)
ctx->executeCommand(trimCommand);
Params params;
params.set("filename", fn.c_str());
params.set("filename-format", fmt.c_str());
ctx->executeCommand(saveAsCommand, params);
if (trim) { // Undo trim command
ctx->executeCommand(undoCommand);
// Just in case allow non-linear history is enabled
// we clear redo information
doc->undoHistory()->clearRedo();
}
}
// Restore layer visibility
i = 0;
for (Layer* layer : doc->sprite()->layers())
layer->setVisible(visibility[i++]);
}
else {
// Show only one layer
if (!importLayerSaveAs.empty()) {
for (Layer* layer : doc->sprite()->layers())
layer->setVisible(layer->name() == importLayerSaveAs);
}
if (!cropParams.empty())
ctx->executeCommand(cropCommand, cropParams);
if (trim)
ctx->executeCommand(trimCommand);
Params params;
params.set("filename", value.value().c_str());
params.set("filename-format", format.c_str());
ctx->executeCommand(saveAsCommand, params);
if (trim) { // Undo trim command
ctx->executeCommand(undoCommand);
// Just in case allow non-linear history is enabled
// we clear redo information
doc->undoHistory()->clearRedo();
}
}
}
}
// --scale <factor>
else if (opt == &options.scale()) {
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
double scale = strtod(value.value().c_str(), NULL);
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
// Scale all sprites
for (auto doc : ctx->documents()) {
ctx->setActiveDocument(static_cast<app::Document*>(doc));
ctx->executeCommand(command);
}
}
// --shrink-to <width,height>
else if (opt == &options.shrinkTo()) {
std::vector<std::string> dimensions;
base::split_string(value.value(), dimensions, ",");
if (dimensions.size() < 2)
throw std::runtime_error("--shrink-to needs two parameters separated by comma (,)\n"
"Usage: --shrink-to width,height\n"
"E.g. --shrink-to 128,64");
double maxWidth = base::convert_to<double>(dimensions[0]);
double maxHeight = base::convert_to<double>(dimensions[1]);
double scaleWidth, scaleHeight, scale;
// Shrink all sprites if needed
for (auto doc : ctx->documents()) {
ctx->setActiveDocument(static_cast<app::Document*>(doc));
scaleWidth = (doc->width() > maxWidth ? maxWidth / doc->width() : 1.0);
scaleHeight = (doc->height() > maxHeight ? maxHeight / doc->height() : 1.0);
if (scaleWidth < 1.0 || scaleHeight < 1.0) {
scale = MIN(scaleWidth, scaleHeight);
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
ctx->executeCommand(command);
}
}
}
#ifdef ENABLE_SCRIPTING
// --script <filename>
else if (opt == &options.script()) {
std::string script = value.value();
StdoutEngineDelegate delegate;
AppScripting engine(&delegate);
engine.evalFile(script);
}
#endif
// --list-layers
else if (opt == &options.listLayers()) {
listLayers = true;
if (m_exporter)
m_exporter->setListLayers(true);
}
// --list-tags
else if (opt == &options.listTags()) {
listTags = true;
if (m_exporter)
m_exporter->setListFrameTags(true);
}
}
// File names aren't associated to any option
else {
const std::string& filename = base::normalize_path(value.value());
app::Document* oldDoc = ctx->activeDocument();
Command* openCommand = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
Params params;
params.set("filename", filename.c_str());
ctx->executeCommand(openCommand, params);
app::Document* doc = ctx->activeDocument();
// If the active document is equal to the previous one, it
// means that we couldn't open this specific document.
if (doc == oldDoc)
doc = nullptr;
// List layers and/or tags
if (doc) {
// Show all layers
if (allLayers) {
for (Layer* layer : doc->sprite()->layers())
layer->setVisible(true);
}
if (listLayers) {
listLayers = false;
for (Layer* layer : doc->sprite()->layers()) {
if (layer->isVisible())
std::cout << layer->name() << "\n";
}
}
if (listTags) {
listTags = false;
for (FrameTag* tag : doc->sprite()->frameTags())
std::cout << tag->name() << "\n";
}
if (m_exporter) {
FrameTag* frameTag = nullptr;
if (!frameTagName.empty()) {
frameTag = doc->sprite()->frameTags().getByName(frameTagName);
}
else if (!frameRange.empty()) {
std::vector<std::string> splitRange;
base::split_string(frameRange, splitRange, ",");
if (splitRange.size() < 2)
throw std::runtime_error("--frame-range needs two parameters separated by comma (,)\n"
"Usage: --frame-range from,to\n"
"E.g. --frame-range 0,99");
frameTag = new FrameTag(base::convert_to<frame_t>(splitRange[0]),
base::convert_to<frame_t>(splitRange[1]));
}
if (!importLayer.empty()) {
Layer* foundLayer = NULL;
for (Layer* layer : doc->sprite()->layers()) {
if (layer->name() == importLayer) {
foundLayer = layer;
break;
}
}
if (foundLayer)
m_exporter->addDocument(doc, foundLayer, frameTag);
}
else if (splitLayers) {
for (auto layer : doc->sprite()->layers()) {
if (layer->isVisible())
m_exporter->addDocument(doc, layer, frameTag);
}
}
else {
m_exporter->addDocument(doc, nullptr, frameTag);
}
}
}
if (!importLayer.empty())
importLayer.clear();
if (splitLayers)
splitLayers = false;
if (listLayers)
listLayers = false;
if (listTags)
listTags = false;
}
}
if (m_exporter && !filenameFormat.empty())
m_exporter->setFilenameFormat(filenameFormat);
}
// Export
if (m_exporter) {
LOG("APP: Exporting sheet...\n");
if (sheetType != SpriteSheetType::None)
m_exporter->setSpriteSheetType(sheetType);
if (ignoreEmpty)
m_exporter->setIgnoreEmptyCels(true);
if (trim)
m_exporter->setTrimCels(true);
base::UniquePtr<Document> spriteSheet(m_exporter->exportSheet());
m_exporter.reset(NULL);
LOG("APP: Export sprite sheet: Done\n");
CliProcessor cli(delegate.get(), options);
cli.process();
}
she::instance()->finishLaunching();
@ -706,7 +265,7 @@ void App::run()
#ifdef ENABLE_SCRIPTING
// Start shell to execute scripts.
if (m_isShell) {
StdoutEngineDelegate delegate;
script::StdoutEngineDelegate delegate;
AppScripting engine(&delegate);
engine.printLastResult();
Shell shell;

View File

@ -31,7 +31,6 @@ namespace app {
class BackupIndicator;
class ContextBar;
class Document;
class DocumentExporter;
class INotificationDelegate;
class InputChain;
class LegacyModules;
@ -115,7 +114,6 @@ namespace app {
bool m_isShell;
base::UniquePtr<MainWindow> m_mainWindow;
FileList m_files;
base::UniquePtr<DocumentExporter> m_exporter;
base::UniquePtr<AppBrushes> m_brushes;
BackupIndicator* m_backupIndicator;
base::mutex m_backupIndicatorMutex;

View File

@ -8,11 +8,10 @@
#include "config.h"
#endif
#include "app/app_options.h"
#include "app/cli/app_options.h"
#include "base/fs.h"
#include <cstdlib>
#include <iostream>
namespace app {
@ -23,14 +22,18 @@ AppOptions::AppOptions(int argc, const char* argv[])
: m_exeName(base::get_file_name(argv[0]))
, m_startUI(true)
, m_startShell(false)
, m_previewCLI(false)
, m_showHelp(false)
, m_showVersion(false)
, m_verboseLevel(kNoVerbose)
, m_palette(m_po.add("palette").requiresValue("<filename>").description("Use a specific palette by default"))
#ifdef ENABLE_SCRIPTING
, m_shell(m_po.add("shell").description("Start an interactive console to execute scripts"))
#endif
, m_batch(m_po.add("batch").mnemonic('b').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_preview(m_po.add("preview").mnemonic('p').description("Do not execute actions, just print what will be\ndone"))
, m_saveAs(m_po.add("save-as").requiresValue("<filename>").description("Save the last given sprite with other format"))
, m_palette(m_po.add("palette").requiresValue("<filename>").description("Change the palette of the last given sprite"))
, m_scale(m_po.add("scale").requiresValue("<factor>").description("Resize all previously opened sprites"))
, m_shrinkTo(m_po.add("shrink-to").requiresValue("width,height").description("Shrink each sprite if it is\nlarger than width or height"))
, m_data(m_po.add("data").requiresValue("<filename.json>").description("File to store the sprite sheet metadata"))
, m_format(m_po.add("format").requiresValue("<format>").description("Format to export the data file\n(json-hash, json-array)"))
@ -39,9 +42,11 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_sheetHeight(m_po.add("sheet-height").requiresValue("<pixels>").description("Sprite sheet height"))
, m_sheetType(m_po.add("sheet-type").requiresValue("<type>").description("Algorithm to create the sprite sheet:\n horizontal\n vertical\n rows\n columns\n packed"))
, m_sheetPack(m_po.add("sheet-pack").description("Same as --sheet-type packed"))
, m_splitLayers(m_po.add("split-layers").description("Import each layer of the next given sprite as\na separated image in the sheet"))
, m_layer(m_po.add("layer").alias("import-layer").requiresValue("<name>").description("Include just the given layer in the sheet"))
, m_splitLayers(m_po.add("split-layers").description("Save each visible layer of sprites\nas separated images in the sheet\n"))
, m_splitTags(m_po.add("split-tags").description("Save each tag as a separated file"))
, m_layer(m_po.add("layer").alias("import-layer").requiresValue("<name>").description("Include just the given layer in the sheet\nor save as operation"))
, m_allLayers(m_po.add("all-layers").description("Make all layers visible\nBy default hidden layers will be ignored"))
, m_ignoreLayer(m_po.add("ignore-layer").requiresValue("<name>").description("Exclude the given layer in the sheet\nor save as operation"))
, m_frameTag(m_po.add("frame-tag").requiresValue("<name>").description("Include tagged frames in the sheet"))
, m_frameRange(m_po.add("frame-range").requiresValue("from,to").description("Only export frames in the [from,to] range"))
, m_ignoreEmpty(m_po.add("ignore-empty").description("Do not export empty frames/cels"))
@ -56,6 +61,7 @@ AppOptions::AppOptions(int argc, const char* argv[])
#endif
, m_listLayers(m_po.add("list-layers").description("List layers of the next given sprite\nor include layers in JSON data"))
, m_listTags(m_po.add("list-tags").description("List tags of the next given sprite sprite\nor include frame tags in JSON data"))
, m_oneFrame(m_po.add("oneframe").description("Load just the first frame"))
, m_verbose(m_po.add("verbose").mnemonic('v').description("Explain what is being done"))
, m_debug(m_po.add("debug").description("Extreme verbose mode and\ncopy log to desktop"))
, m_help(m_po.add("help").mnemonic('?').description("Display this help and exits"))
@ -69,24 +75,16 @@ AppOptions::AppOptions(int argc, const char* argv[])
else if (m_po.enabled(m_verbose))
m_verboseLevel = kVerbose;
m_paletteFileName = m_po.value_of(m_palette);
#ifdef ENABLE_SCRIPTING
m_startShell = m_po.enabled(m_shell);
#endif
m_previewCLI = m_po.enabled(m_preview);
m_showHelp = m_po.enabled(m_help);
m_showVersion = m_po.enabled(m_version);
if (m_po.enabled(m_help)) {
showHelp();
m_startUI = false;
}
else if (m_po.enabled(m_version)) {
showVersion();
m_startUI = false;
}
if (
#ifdef ENABLE_SCRIPTING
m_po.enabled(m_shell) ||
#endif
if (m_startShell ||
m_showHelp ||
m_showVersion ||
m_po.enabled(m_batch)) {
m_startUI = false;
}
@ -105,21 +103,4 @@ bool AppOptions::hasExporterParams() const
m_po.enabled(m_sheet);
}
void AppOptions::showHelp()
{
std::cout
<< PACKAGE << " v" << VERSION << " | A pixel art program\n" << COPYRIGHT
<< "\n\nUsage:\n"
<< " " << m_exeName << " [OPTIONS] [FILES]...\n\n"
<< "Options:\n"
<< m_po
<< "\nFind more information in " << PACKAGE
<< " web site: " << WEBSITE << "\n\n";
}
void AppOptions::showVersion()
{
std::cout << PACKAGE << ' ' << VERSION << '\n';
}
}

View File

@ -4,8 +4,8 @@
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_APP_OPTIONS_H_INCLUDED
#define APP_APP_OPTIONS_H_INCLUDED
#ifndef APP_CLI_APP_OPTIONS_H_INCLUDED
#define APP_CLI_APP_OPTIONS_H_INCLUDED
#pragma once
#include <stdexcept>
@ -30,18 +30,23 @@ public:
AppOptions(int argc, const char* argv[]);
const std::string& exeName() const { return m_exeName; }
const base::ProgramOptions& programOptions() const { return m_po; }
bool startUI() const { return m_startUI; }
bool startShell() const { return m_startShell; }
bool previewCLI() const { return m_previewCLI; }
bool showHelp() const { return m_showHelp; }
bool showVersion() const { return m_showVersion; }
VerboseLevel verboseLevel() const { return m_verboseLevel; }
const std::string& paletteFileName() const { return m_paletteFileName; }
const ValueList& values() const {
return m_po.values();
}
// Export options
const Option& saveAs() const { return m_saveAs; }
const Option& palette() const { return m_palette; }
const Option& scale() const { return m_scale; }
const Option& shrinkTo() const { return m_shrinkTo; }
const Option& data() const { return m_data; }
@ -52,8 +57,10 @@ public:
const Option& sheetType() const { return m_sheetType; }
const Option& sheetPack() const { return m_sheetPack; }
const Option& splitLayers() const { return m_splitLayers; }
const Option& splitTags() const { return m_splitTags; }
const Option& layer() const { return m_layer; }
const Option& allLayers() const { return m_allLayers; }
const Option& ignoreLayer() const { return m_ignoreLayer; }
const Option& frameTag() const { return m_frameTag; }
const Option& frameRange() const { return m_frameRange; }
const Option& ignoreEmpty() const { return m_ignoreEmpty; }
@ -68,26 +75,27 @@ public:
#endif
const Option& listLayers() const { return m_listLayers; }
const Option& listTags() const { return m_listTags; }
const Option& oneFrame() const { return m_oneFrame; }
bool hasExporterParams() const;
private:
void showHelp();
void showVersion();
std::string m_exeName;
base::ProgramOptions m_po;
bool m_startUI;
bool m_startShell;
bool m_previewCLI;
bool m_showHelp;
bool m_showVersion;
VerboseLevel m_verboseLevel;
std::string m_paletteFileName;
Option& m_palette;
#ifdef ENABLE_SCRIPTING
Option& m_shell;
#endif
Option& m_batch;
Option& m_preview;
Option& m_saveAs;
Option& m_palette;
Option& m_scale;
Option& m_shrinkTo;
Option& m_data;
@ -98,8 +106,10 @@ private:
Option& m_sheetType;
Option& m_sheetPack;
Option& m_splitLayers;
Option& m_splitTags;
Option& m_layer;
Option& m_allLayers;
Option& m_ignoreLayer;
Option& m_frameTag;
Option& m_frameRange;
Option& m_ignoreEmpty;
@ -114,6 +124,7 @@ private:
#endif
Option& m_listLayers;
Option& m_listTags;
Option& m_oneFrame;
Option& m_verbose;
Option& m_debug;

View File

@ -0,0 +1,37 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CLI_CLI_DELEGATE_H_INCLUDED
#define APP_CLI_CLI_DELEGATE_H_INCLUDED
#pragma once
#include <string>
namespace app {
class AppOptions;
class DocumentExporter;
struct CliOpenFile;
class CliDelegate {
public:
virtual ~CliDelegate() { }
virtual void showHelp(const AppOptions& options) { }
virtual void showVersion() { }
virtual void uiMode() { }
virtual void shellMode() { }
virtual void batchMode() { }
virtual void beforeOpenFile(const CliOpenFile& cof) { }
virtual void afterOpenFile(const CliOpenFile& cof) { }
virtual void saveFile(const CliOpenFile& cof) { }
virtual void loadPalette(const CliOpenFile& cof, const std::string& filename) { }
virtual void exportFiles(DocumentExporter& exporter) { }
virtual void execScript(const std::string& filename) { }
};
} // namespace app
#endif

View File

@ -0,0 +1,46 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cli/cli_open_file.h"
#include "app/document.h"
#include "app/file/file.h"
#include "doc/frame_tag.h"
#include "doc/frame_tags.h"
#include "doc/sprite.h"
namespace app {
CliOpenFile::CliOpenFile()
{
document = nullptr;
fromFrame = -1;
toFrame = -1;
splitLayers = false;
splitTags = false;
allLayers = false;
listLayers = false;
listTags = false;
ignoreEmpty = false;
trim = false;
oneFrame = false;
crop = gfx::Rect();
}
FileOpROI CliOpenFile::roi() const
{
ASSERT(document);
SelectedFrames selFrames;
if (hasFrameRange())
selFrames.insert(fromFrame, toFrame);
return FileOpROI(document, frameTag, selFrames, true);
}
} // namespace app

View File

@ -0,0 +1,60 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CLI_CLI_OPEN_FILE_H_INCLUDED
#define APP_CLI_CLI_OPEN_FILE_H_INCLUDED
#pragma once
#include "doc/frame.h"
#include "gfx/rect.h"
#include <string>
#include <vector>
namespace app {
class Document;
class FileOpROI;
struct CliOpenFile {
app::Document* document;
std::string filename;
std::string filenameFormat;
std::string frameTag;
std::vector<std::string> includeLayers;
std::vector<std::string> excludeLayers;
doc::frame_t fromFrame, toFrame;
bool splitLayers;
bool splitTags;
bool allLayers;
bool listLayers;
bool listTags;
bool ignoreEmpty;
bool trim;
bool oneFrame;
gfx::Rect crop;
CliOpenFile();
bool hasFrameTag() const {
return (!frameTag.empty());
}
bool hasFrameRange() const {
return (fromFrame >= 0 && toFrame >= 0);
}
bool hasLayersFilter() const {
return (!includeLayers.empty() ||
!excludeLayers.empty());
}
FileOpROI roi() const;
};
} // namespace app
#endif

View File

@ -0,0 +1,653 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cli/cli_processor.h"
#include "app/cli/app_options.h"
#include "app/cli/cli_delegate.h"
#include "app/commands/cmd_sprite_size.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/console.h"
#include "app/document.h"
#include "app/document_exporter.h"
#include "app/document_undo.h"
#include "app/file/file.h"
#include "app/filename_formatter.h"
#include "app/restore_visible_layers.h"
#include "app/ui_context.h"
#include "base/convert_to.h"
#include "base/fs.h"
#include "base/split_string.h"
#include "doc/frame_tag.h"
#include "doc/frame_tags.h"
#include "doc/layer.h"
#include "doc/selected_frames.h"
#include "doc/selected_layers.h"
namespace app {
namespace {
std::string get_layer_path(const Layer* layer)
{
std::string path;
for (; layer != layer->sprite()->root(); layer=layer->parent()) {
if (!path.empty())
path.insert(0, "/");
path.insert(0, layer->name());
}
return path;
}
bool match_path(const std::string& filter,
const std::string& layer_path,
const bool exclude)
{
if (filter == layer_path)
return true;
std::vector<std::string> a, b;
base::split_string(filter, a, "/");
base::split_string(layer_path, b, "/");
for (int i=0; i<a.size() && i<b.size(); ++i) {
if (a[i] != b[i] && a[i] != "*")
return false;
}
// Exclude group itself when all children are excluded. This special
// case is only for exclusion because if we leave the group
// selected, the propagation of the selection will include all
// visible children too (see SelectedLayers::propagateSelection()).
if (exclude &&
a.size() > 1 &&
a.size() == b.size()+1 &&
a[a.size()-1] == "*")
return true;
return (a.size() <= b.size());
}
bool filter_layer(const Layer* layer,
const std::string& layer_path,
const std::vector<std::string>& filters,
const bool result)
{
for (const auto& filter : filters) {
if (layer->name() == filter ||
match_path(filter, layer_path, !result))
return result;
}
return !result;
}
void filter_layers(const LayerList& layers,
const CliOpenFile& cof,
SelectedLayers& filteredLayers)
{
for (Layer* layer : layers) {
auto layer_path = get_layer_path(layer);
if ((cof.includeLayers.empty() && !layer->isVisibleHierarchy()) ||
(!cof.includeLayers.empty() && !filter_layer(layer, layer_path, cof.includeLayers, true)))
continue;
if (!cof.excludeLayers.empty() &&
!filter_layer(layer, layer_path, cof.excludeLayers, false))
continue;
filteredLayers.insert(layer);
}
}
} // anonymous namespace
CliProcessor::CliProcessor(CliDelegate* delegate,
const AppOptions& options)
: m_delegate(delegate)
, m_options(options)
, m_exporter(nullptr)
{
if (options.hasExporterParams())
m_exporter.reset(new DocumentExporter);
}
void CliProcessor::process()
{
// --help
if (m_options.showHelp()) {
m_delegate->showHelp(m_options);
}
// --version
else if (m_options.showVersion()) {
m_delegate->showVersion();
}
// Process other options and file names
else if (!m_options.values().empty()) {
Console console;
UIContext* ctx = UIContext::instance();
CliOpenFile cof;
SpriteSheetType sheetType = SpriteSheetType::None;
app::Document* lastDoc = nullptr;
for (const auto& value : m_options.values()) {
const AppOptions::Option* opt = value.option();
// Special options/commands
if (opt) {
// --data <file.json>
if (opt == &m_options.data()) {
if (m_exporter)
m_exporter->setDataFilename(value.value());
}
// --format <format>
else if (opt == &m_options.format()) {
if (m_exporter) {
DocumentExporter::DataFormat format = DocumentExporter::DefaultDataFormat;
if (value.value() == "json-hash")
format = DocumentExporter::JsonHashDataFormat;
else if (value.value() == "json-array")
format = DocumentExporter::JsonArrayDataFormat;
m_exporter->setDataFormat(format);
}
}
// --sheet <file.png>
else if (opt == &m_options.sheet()) {
if (m_exporter)
m_exporter->setTextureFilename(value.value());
}
// --sheet-width <width>
else if (opt == &m_options.sheetWidth()) {
if (m_exporter)
m_exporter->setTextureWidth(strtol(value.value().c_str(), NULL, 0));
}
// --sheet-height <height>
else if (opt == &m_options.sheetHeight()) {
if (m_exporter)
m_exporter->setTextureHeight(strtol(value.value().c_str(), NULL, 0));
}
// --sheet-pack
else if (opt == &m_options.sheetType()) {
if (value.value() == "horizontal")
sheetType = SpriteSheetType::Horizontal;
else if (value.value() == "vertical")
sheetType = SpriteSheetType::Vertical;
else if (value.value() == "rows")
sheetType = SpriteSheetType::Rows;
else if (value.value() == "columns")
sheetType = SpriteSheetType::Columns;
else if (value.value() == "packed")
sheetType = SpriteSheetType::Packed;
}
// --sheet-pack
else if (opt == &m_options.sheetPack()) {
sheetType = SpriteSheetType::Packed;
}
// --split-layers
else if (opt == &m_options.splitLayers()) {
cof.splitLayers = true;
}
// --split-tags
else if (opt == &m_options.splitTags()) {
cof.splitTags = true;
}
// --layer <layer-name>
else if (opt == &m_options.layer()) {
cof.includeLayers.push_back(value.value());
}
// --ignore-layer <layer-name>
else if (opt == &m_options.ignoreLayer()) {
cof.excludeLayers.push_back(value.value());
}
// --all-layers
else if (opt == &m_options.allLayers()) {
cof.allLayers = true;
}
// --frame-tag <tag-name>
else if (opt == &m_options.frameTag()) {
cof.frameTag = value.value();
}
// --frame-range from,to
else if (opt == &m_options.frameRange()) {
std::vector<std::string> splitRange;
base::split_string(value.value(), splitRange, ",");
if (splitRange.size() < 2)
throw std::runtime_error("--frame-range needs two parameters separated by comma (,)\n"
"Usage: --frame-range from,to\n"
"E.g. --frame-range 0,99");
cof.fromFrame = base::convert_to<frame_t>(splitRange[0]);
cof.toFrame = base::convert_to<frame_t>(splitRange[1]);
}
// --ignore-empty
else if (opt == &m_options.ignoreEmpty()) {
cof.ignoreEmpty = true;
if (m_exporter)
m_exporter->setIgnoreEmptyCels(true);
}
// --border-padding
else if (opt == &m_options.borderPadding()) {
if (m_exporter)
m_exporter->setBorderPadding(strtol(value.value().c_str(), NULL, 0));
}
// --shape-padding
else if (opt == &m_options.shapePadding()) {
if (m_exporter)
m_exporter->setShapePadding(strtol(value.value().c_str(), NULL, 0));
}
// --inner-padding
else if (opt == &m_options.innerPadding()) {
if (m_exporter)
m_exporter->setInnerPadding(strtol(value.value().c_str(), NULL, 0));
}
// --trim
else if (opt == &m_options.trim()) {
cof.trim = true;
if (m_exporter)
m_exporter->setTrimCels(true);
}
// --crop x,y,width,height
else if (opt == &m_options.crop()) {
std::vector<std::string> parts;
base::split_string(value.value(), parts, ",");
if (parts.size() < 4)
throw std::runtime_error("--crop needs four parameters separated by comma (,)\n"
"Usage: --crop x,y,width,height\n"
"E.g. --crop 0,0,32,32");
cof.crop.x = base::convert_to<int>(parts[0]);
cof.crop.y = base::convert_to<int>(parts[1]);
cof.crop.w = base::convert_to<int>(parts[2]);
cof.crop.h = base::convert_to<int>(parts[3]);
}
// --filename-format
else if (opt == &m_options.filenameFormat()) {
cof.filenameFormat = value.value();
if (m_exporter)
m_exporter->setFilenameFormat(cof.filenameFormat);
}
// --save-as <filename>
else if (opt == &m_options.saveAs()) {
if (lastDoc) {
std::string fn = value.value();
// Automatic --split-layer or --split-tags in case the
// output filename already contains {layer} or {tag}
// template elements.
bool hasLayerTemplate = (is_layer_in_filename_format(fn) ||
is_group_in_filename_format(fn));
bool hasTagTemplate = is_tag_in_filename_format(fn);
if (hasLayerTemplate || hasTagTemplate) {
cof.splitLayers = (cof.splitLayers || hasLayerTemplate);
cof.splitTags = (cof.splitTags || hasTagTemplate);
cof.filenameFormat =
get_default_filename_format(
fn,
true, // With path
(lastDoc->sprite()->totalFrames() > 1), // Has frames
false, // Has layer
false); // Has frame tag
}
cof.document = lastDoc;
cof.filename = fn;
saveFile(cof);
}
else
console.printf("A document is needed before --save-as argument\n");
}
// --palette <filename>
else if (opt == &m_options.palette()) {
if (lastDoc) {
ASSERT(cof.document == lastDoc);
std::string filename = value.value();
m_delegate->loadPalette(cof, filename);
}
else {
console.printf("You need to load a document to change its palette with --palette\n");
}
}
// --scale <factor>
else if (opt == &m_options.scale()) {
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
double scale = strtod(value.value().c_str(), NULL);
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
// Scale all sprites
for (auto doc : ctx->documents()) {
ctx->setActiveDocument(static_cast<app::Document*>(doc));
ctx->executeCommand(command);
}
}
// --shrink-to <width,height>
else if (opt == &m_options.shrinkTo()) {
std::vector<std::string> dimensions;
base::split_string(value.value(), dimensions, ",");
if (dimensions.size() < 2)
throw std::runtime_error("--shrink-to needs two parameters separated by comma (,)\n"
"Usage: --shrink-to width,height\n"
"E.g. --shrink-to 128,64");
double maxWidth = base::convert_to<double>(dimensions[0]);
double maxHeight = base::convert_to<double>(dimensions[1]);
double scaleWidth, scaleHeight, scale;
// Shrink all sprites if needed
for (auto doc : ctx->documents()) {
ctx->setActiveDocument(static_cast<app::Document*>(doc));
scaleWidth = (doc->width() > maxWidth ? maxWidth / doc->width() : 1.0);
scaleHeight = (doc->height() > maxHeight ? maxHeight / doc->height() : 1.0);
if (scaleWidth < 1.0 || scaleHeight < 1.0) {
scale = MIN(scaleWidth, scaleHeight);
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
ctx->executeCommand(command);
}
}
}
#ifdef ENABLE_SCRIPTING
// --script <filename>
else if (opt == &m_options.script()) {
std::string filename = value.value();
m_delegate->execScript(filename);
}
#endif
// --list-layers
else if (opt == &m_options.listLayers()) {
cof.listLayers = true;
if (m_exporter)
m_exporter->setListLayers(true);
}
// --list-tags
else if (opt == &m_options.listTags()) {
cof.listTags = true;
if (m_exporter)
m_exporter->setListFrameTags(true);
}
// --oneframe
else if (opt == &m_options.oneFrame()) {
cof.oneFrame = true;
}
}
// File names aren't associated to any option
else {
cof.document = nullptr;
cof.filename = base::normalize_path(value.value());
if (openFile(cof))
lastDoc = cof.document;
}
}
if (m_exporter) {
if (sheetType != SpriteSheetType::None)
m_exporter->setSpriteSheetType(sheetType);
m_delegate->exportFiles(*m_exporter.get());
m_exporter.reset(nullptr);
}
}
// Running mode
if (m_options.startUI()) {
m_delegate->uiMode();
}
else if (m_options.startShell()) {
m_delegate->shellMode();
}
else {
m_delegate->batchMode();
}
}
bool CliProcessor::openFile(CliOpenFile& cof)
{
m_delegate->beforeOpenFile(cof);
Context* ctx = UIContext::instance();
app::Document* oldDoc = ctx->activeDocument();
Command* openCommand = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
Params params;
params.set("filename", cof.filename.c_str());
if (cof.oneFrame)
params.set("oneframe", "true");
ctx->executeCommand(openCommand, params);
app::Document* doc = ctx->activeDocument();
// If the active document is equal to the previous one, it
// means that we couldn't open this specific document.
if (doc == oldDoc)
doc = nullptr;
cof.document = doc;
if (doc) {
// Show all layers
if (cof.allLayers) {
for (doc::Layer* layer : doc->sprite()->allLayers())
layer->setVisible(true);
}
// Add document to exporter
if (m_exporter) {
FrameTag* frameTag = nullptr;
SelectedFrames selFrames;
if (cof.hasFrameTag()) {
frameTag = doc->sprite()->frameTags().getByName(cof.frameTag);
}
if (cof.hasFrameRange()) {
// --frame-range with --frame-tag
if (frameTag) {
selFrames.insert(
frameTag->fromFrame()+MID(0, cof.fromFrame, frameTag->frames()-1),
frameTag->fromFrame()+MID(0, cof.toFrame, frameTag->frames()-1));
}
// --frame-range without --frame-tag
else {
selFrames.insert(cof.fromFrame, cof.toFrame);
}
}
if (cof.hasLayersFilter()) {
SelectedLayers filteredLayers;
filter_layers(doc->sprite()->allLayers(), cof, filteredLayers);
if (cof.splitLayers) {
for (Layer* layer : filteredLayers) {
SelectedLayers oneLayer;
oneLayer.insert(layer);
m_exporter->addDocument(doc, frameTag, &oneLayer,
(!selFrames.empty() ? &selFrames: nullptr));
}
}
else {
m_exporter->addDocument(doc, frameTag, &filteredLayers,
(!selFrames.empty() ? &selFrames: nullptr));
}
}
else if (cof.splitLayers) {
for (auto layer : doc->sprite()->allVisibleLayers()) {
SelectedLayers oneLayer;
oneLayer.insert(layer);
m_exporter->addDocument(doc, frameTag, &oneLayer,
(!selFrames.empty() ? &selFrames: nullptr));
}
}
else {
m_exporter->addDocument(doc, frameTag, nullptr,
(!selFrames.empty() ? &selFrames: nullptr));
}
}
}
m_delegate->afterOpenFile(cof);
return (doc ? true: false);
}
void CliProcessor::saveFile(const CliOpenFile& cof)
{
UIContext* ctx = UIContext::instance();
ctx->setActiveDocument(cof.document);
Command* trimCommand = CommandsModule::instance()->getCommandByName(CommandId::AutocropSprite);
Command* cropCommand = CommandsModule::instance()->getCommandByName(CommandId::CropSprite);
Command* undoCommand = CommandsModule::instance()->getCommandByName(CommandId::Undo);
app::Document* doc = cof.document;
bool clearUndo = false;
if (!cof.crop.isEmpty()) {
Params cropParams;
cropParams.set("x", base::convert_to<std::string>(cof.crop.x).c_str());
cropParams.set("y", base::convert_to<std::string>(cof.crop.y).c_str());
cropParams.set("width", base::convert_to<std::string>(cof.crop.w).c_str());
cropParams.set("height", base::convert_to<std::string>(cof.crop.h).c_str());
ctx->executeCommand(cropCommand, cropParams);
}
std::string fn = cof.filename;
std::string filenameFormat = cof.filenameFormat;
if (filenameFormat.empty()) { // Default format
bool hasFrames = (cof.roi().frames() > 1);
filenameFormat = get_default_filename_format(
fn,
true, // With path
hasFrames, // Has frames
cof.splitLayers, // Has layer
cof.splitTags); // Has frame tag
}
SelectedLayers filteredLayers;
LayerList allLayers = doc->sprite()->allLayers();
LayerList layers;
// --save-as with --split-layers or --split-tags
if (cof.splitLayers) {
for (doc::Layer* layer : doc->sprite()->allVisibleLayers())
layers.push_back(layer);
}
else {
// Filter layers
if (cof.hasLayersFilter())
filter_layers(allLayers, cof, filteredLayers);
// All visible layers
layers.push_back(nullptr);
}
std::vector<doc::FrameTag*> frameTags;
if (cof.hasFrameTag()) {
frameTags.push_back(
doc->sprite()->frameTags().getByName(cof.frameTag));
}
else {
doc::FrameTags& origFrameTags = cof.document->sprite()->frameTags();
if (cof.splitTags && !origFrameTags.empty()) {
for (doc::FrameTag* frameTag : origFrameTags) {
// In case the tag is outside the given --frame-range
if (cof.hasFrameRange()) {
if (frameTag->toFrame() < cof.fromFrame ||
frameTag->fromFrame() > cof.toFrame)
continue;
}
frameTags.push_back(frameTag);
}
}
else
frameTags.push_back(nullptr);
}
bool layerInFormat = is_layer_in_filename_format(fn);
bool groupInFormat = is_group_in_filename_format(fn);
for (doc::FrameTag* frameTag : frameTags) {
// For each layer, hide other ones and save the sprite.
for (doc::Layer* layer : layers) {
RestoreVisibleLayers layersVisibility;
if (cof.splitLayers) {
ASSERT(layer);
// If the user doesn't want all layers and this one is hidden.
if (!layer->isVisible())
continue; // Just ignore this layer.
// Make this layer ("show") the only one visible.
layersVisibility.showLayer(layer);
}
else if (!filteredLayers.empty())
layersVisibility.showSelectedLayers(doc->sprite(), filteredLayers);
if (layer) {
if ((layerInFormat && layer->isGroup()) ||
(!layerInFormat && groupInFormat && !layer->isGroup())) {
continue;
}
}
// TODO --trim --save-as --split-layers doesn't make too much
// sense as we lost the trim rectangle information (e.g. we
// don't have sheet .json) Also, we should trim each frame
// individually (a process that can be done only in
// FileOp::operate()).
if (cof.trim)
ctx->executeCommand(trimCommand);
CliOpenFile itemCof = cof;
FilenameInfo fnInfo;
fnInfo.filename(fn);
if (layer) {
fnInfo.layerName(layer->name());
if (layer->isGroup())
fnInfo.groupName(layer->name());
else if (layer->parent() != layer->sprite()->root())
fnInfo.groupName(layer->parent()->name());
itemCof.includeLayers.push_back(layer->name());
}
if (frameTag) {
fnInfo
.innerTagName(frameTag->name())
.outerTagName(frameTag->name());
itemCof.frameTag = frameTag->name();
}
itemCof.filename = filename_formatter(filenameFormat, fnInfo);
itemCof.filenameFormat = filename_formatter(filenameFormat, fnInfo, false);
// Call delegate
m_delegate->saveFile(itemCof);
if (cof.trim) {
ctx->executeCommand(undoCommand);
clearUndo = true;
}
}
}
// Undo crop
if (!cof.crop.isEmpty()) {
ctx->executeCommand(undoCommand);
clearUndo = true;
}
if (clearUndo) {
// Just in case allow non-linear history is enabled
// we clear redo information
doc->undoHistory()->clearRedo();
}
}
} // namespace app

View File

@ -0,0 +1,40 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_APP_CLI_PROCESSOR_H_INCLUDED
#define APP_APP_CLI_PROCESSOR_H_INCLUDED
#pragma once
#include "app/cli/cli_delegate.h"
#include "app/cli/cli_open_file.h"
#include "base/unique_ptr.h"
#include <string>
#include <vector>
namespace app {
class AppOptions;
class DocumentExporter;
class CliProcessor {
public:
CliProcessor(CliDelegate* delegate,
const AppOptions& options);
void process();
private:
bool openFile(CliOpenFile& cof);
void saveFile(const CliOpenFile& cof);
CliDelegate* m_delegate;
const AppOptions& m_options;
base::UniquePtr<DocumentExporter> m_exporter;
};
} // namespace app
#endif

86
src/app/cli/cli_tests.cpp Normal file
View File

@ -0,0 +1,86 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#include "tests/test.h"
#include "app/cli/app_options.h"
#include "app/cli/cli_processor.h"
#include "app/document_exporter.h"
#include <initializer_list>
using namespace app;
class CliTestDelegate : public CliDelegate {
public:
CliTestDelegate() {
m_helpWasShown = false;
m_versionWasShown = false;
m_uiMode = false;
m_shellMode = false;
m_batchMode = false;
}
void showHelp(const AppOptions& options) override { m_helpWasShown = true; }
void showVersion() override { m_versionWasShown = true; }
void uiMode() override { m_uiMode = true; }
void shellMode() override { m_shellMode = true; }
void batchMode() override { m_batchMode = true; }
void beforeOpenFile(const CliOpenFile& cof) override { }
void afterOpenFile(const CliOpenFile& cof) override { }
void saveFile(const CliOpenFile& cof) override { }
void exportFiles(DocumentExporter& exporter) override { }
void execScript(const std::string& filename) override { }
bool helpWasShown() const { return m_helpWasShown; }
bool versionWasShown() const { return m_versionWasShown; }
private:
bool m_helpWasShown;
bool m_versionWasShown;
bool m_uiMode;
bool m_shellMode;
bool m_batchMode;
};
AppOptions args(std::initializer_list<const char*> l) {
int argc = l.size()+1;
const char** argv = new const char*[argc];
argv[0] = "aseprite.exe";
auto it = l.begin();
for (int i=1; i<argc; ++i, ++it) {
argv[i] = *it;
TRACE("argv[%d] = %s\n", i, argv[i]);
}
AppOptions opts(argc, argv);
delete[] argv;
return opts;
}
TEST(Cli, None)
{
CliTestDelegate d;
CliProcessor p(&d, args({ }));
p.process();
EXPECT_TRUE(!d.helpWasShown());
EXPECT_TRUE(!d.versionWasShown());
}
TEST(Cli, Help)
{
CliTestDelegate d;
CliProcessor p(&d, args({ "--help" }));
p.process();
EXPECT_TRUE(d.helpWasShown());
}
TEST(Cli, Version)
{
CliTestDelegate d;
CliProcessor p(&d, args({ "--version" }));
p.process();
EXPECT_TRUE(d.versionWasShown());
}

View File

@ -0,0 +1,127 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cli/default_cli_delegate.h"
#include "app/cli/app_options.h"
#include "app/cli/cli_open_file.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/console.h"
#include "app/document.h"
#include "app/document_exporter.h"
#include "app/file/palette_file.h"
#include "app/ui_context.h"
#include "base/convert_to.h"
#include "doc/frame_tag.h"
#include "doc/layer.h"
#include "doc/palette.h"
#include "doc/sprite.h"
#ifdef ENABLE_SCRIPTING
#include "app/script/app_scripting.h"
#include "script/engine_delegate.h"
#endif
#include <iostream>
namespace app {
void DefaultCliDelegate::showHelp(const AppOptions& options)
{
std::cout
<< PACKAGE << " v" << VERSION << " | A pixel art program\n" << COPYRIGHT
<< "\n\nUsage:\n"
<< " " << options.exeName() << " [OPTIONS] [FILES]...\n\n"
<< "Options:\n"
<< options.programOptions()
<< "\nFind more information in " << PACKAGE
<< " web site: " << WEBSITE << "\n\n";
}
void DefaultCliDelegate::showVersion()
{
std::cout << PACKAGE << ' ' << VERSION << '\n';
}
void DefaultCliDelegate::afterOpenFile(const CliOpenFile& cof)
{
if (!cof.document) // Do nothing
return;
if (cof.listLayers) {
for (doc::Layer* layer : cof.document->sprite()->allVisibleLayers())
std::cout << layer->name() << "\n";
}
if (cof.listTags) {
for (doc::FrameTag* tag : cof.document->sprite()->frameTags())
std::cout << tag->name() << "\n";
}
}
void DefaultCliDelegate::saveFile(const CliOpenFile& cof)
{
Context* ctx = UIContext::instance();
Command* saveAsCommand = CommandsModule::instance()->getCommandByName(CommandId::SaveFileCopyAs);
Params params;
params.set("filename", cof.filename.c_str());
params.set("filename-format", cof.filenameFormat.c_str());
if (cof.hasFrameTag()) {
params.set("frame-tag", cof.frameTag.c_str());
}
if (cof.hasFrameRange()) {
params.set("from-frame", base::convert_to<std::string>(cof.fromFrame).c_str());
params.set("to-frame", base::convert_to<std::string>(cof.toFrame).c_str());
}
ctx->executeCommand(saveAsCommand, params);
}
void DefaultCliDelegate::loadPalette(const CliOpenFile& cof,
const std::string& filename)
{
base::UniquePtr<doc::Palette> palette(load_palette(filename.c_str()));
if (palette) {
Context* ctx = UIContext::instance();
Command* loadPalCommand = CommandsModule::instance()->getCommandByName(CommandId::LoadPalette);
Params params;
params.set("filename", filename.c_str());
ctx->executeCommand(loadPalCommand, params);
}
else {
Console().printf("Error loading palette in --palette '%s'\n",
filename.c_str());
}
}
void DefaultCliDelegate::exportFiles(DocumentExporter& exporter)
{
LOG("APP: Exporting sheet...\n");
base::UniquePtr<app::Document> spriteSheet(exporter.exportSheet());
// Sprite sheet isn't used, we just delete it.
LOG("APP: Export sprite sheet: Done\n");
}
void DefaultCliDelegate::execScript(const std::string& filename)
{
#ifdef ENABLE_SCRIPTING
script::StdoutEngineDelegate delegate;
AppScripting engine(&delegate);
engine.evalFile(filename);
#endif
}
} // namespace app

View File

@ -0,0 +1,28 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CLI_DEFAULT_CLI_DELEGATE_H_INCLUDED
#define APP_CLI_DEFAULT_CLI_DELEGATE_H_INCLUDED
#pragma once
#include "app/cli/cli_delegate.h"
namespace app {
class DefaultCliDelegate : public CliDelegate {
public:
void showHelp(const AppOptions& programOptions) override;
void showVersion() override;
void afterOpenFile(const CliOpenFile& cof) override;
void saveFile(const CliOpenFile& cof) override;
void loadPalette(const CliOpenFile& cof, const std::string& filename) override;
void exportFiles(DocumentExporter& exporter) override;
void execScript(const std::string& filename) override;
};
} // namespace app
#endif

View File

@ -0,0 +1,218 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cli/preview_cli_delegate.h"
#include "app/cli/cli_open_file.h"
#include "app/document.h"
#include "app/document_exporter.h"
#include "app/file/file.h"
#include "app/ui_context.h"
#include "base/fs.h"
#include "base/unique_ptr.h"
#include "doc/sprite.h"
#include <iostream>
namespace app {
void PreviewCliDelegate::showHelp(const AppOptions& options)
{
std::cout << "- Show " PACKAGE " CLI usage\n";
}
void PreviewCliDelegate::showVersion()
{
std::cout << "- Show " PACKAGE " version\n";
}
void PreviewCliDelegate::uiMode()
{
std::cout << "- Run UI mode\n";
}
void PreviewCliDelegate::shellMode()
{
std::cout << "- Run shell mode\n";
}
void PreviewCliDelegate::batchMode()
{
std::cout << "- Exit\n";
}
void PreviewCliDelegate::beforeOpenFile(const CliOpenFile& cof)
{
std::cout << "- Open file '" << cof.filename << "'\n";
}
void PreviewCliDelegate::afterOpenFile(const CliOpenFile& cof)
{
if (!cof.document) {
std::cout << " - WARNING: File not found or error loading file\n";
return;
}
if (cof.listLayers)
std::cout << " - List layers\n";
if (cof.listTags)
std::cout << " - List tags\n";
if (cof.oneFrame)
std::cout << " - One frame\n";
if (cof.allLayers)
std::cout << " - Make all layers visible\n";
showLayersFilter(cof);
}
void PreviewCliDelegate::saveFile(const CliOpenFile& cof)
{
ASSERT(cof.document);
ASSERT(cof.document->sprite());
std::cout << "- Save file '" << cof.filename << "'\n"
<< " - Sprite: '" << cof.document->filename() << "'\n";
if (!cof.crop.isEmpty()) {
std::cout << " - Crop: "
<< cof.crop.x << ","
<< cof.crop.y << " "
<< cof.crop.w << "x"
<< cof.crop.h << "\n";
}
if (cof.trim) {
std::cout << " - Trim\n";
}
std::cout << " - Size: "
<< cof.document->sprite()->width() << "x"
<< cof.document->sprite()->height() << "\n";
showLayersFilter(cof);
if (cof.hasFrameTag()) {
std::cout << " - Frame tag: '" << cof.frameTag << "'\n";
}
if (cof.hasFrameRange()) {
const auto& selFrames = cof.roi().selectedFrames();
if (!selFrames.empty()) {
if (selFrames.ranges() == 1)
std::cout << " - Frame range from "
<< selFrames.firstFrame() << " to "
<< selFrames.lastFrame() << "\n";
else {
std::cout << " - Specific frames:";
for (auto frame : selFrames)
std::cout << ' ' << frame;
std::cout << "\n";
}
}
}
if (!cof.filenameFormat.empty())
std::cout << " - Filename format: '" << cof.filenameFormat << "'\n";
base::UniquePtr<FileOp> fop(
FileOp::createSaveDocumentOperation(
UIContext::instance(),
cof.roi(),
cof.filename.c_str(),
cof.filenameFormat.c_str()));
if (fop) {
std::vector<std::string> files;
fop->getFilenameList(files);
for (const auto& file : files) {
if (base::is_file(file))
std::cout << " - Overwrite file: '" << file << "'\n";
else
std::cout << " - Output file: '" << file << "'\n";
}
}
else
std::cout << " - No output\n";
}
void PreviewCliDelegate::loadPalette(const CliOpenFile& cof,
const std::string& filename)
{
ASSERT(cof.document);
ASSERT(cof.document->sprite());
std::cout << "- Load palette:\n"
<< " - Sprite: '" << cof.filename << "'\n"
<< " - Palette: '" << filename << "'\n";
}
void PreviewCliDelegate::exportFiles(DocumentExporter& exporter)
{
std::string type = "None";
switch (exporter.spriteSheetType()) {
case SpriteSheetType::Horizontal: type = "Horizontal"; break;
case SpriteSheetType::Vertical: type = "Vertical"; break;
case SpriteSheetType::Rows: type = "Rows"; break;
case SpriteSheetType::Columns: type = "Columns"; break;
case SpriteSheetType::Packed: type = "Packed"; break;
}
gfx::Size size = exporter.calculateSheetSize();
std::cout << "- Export sprite sheet:\n"
<< " - Type: " << type << "\n"
<< " - Size: " << size.w << "x" << size.h << "\n";
if (!exporter.textureFilename().empty()) {
std::cout << " - Save texture file: '"
<< exporter.textureFilename() << "'\n";
}
if (!exporter.dataFilename().empty()) {
std::string format = "Unknown";
switch (exporter.dataFormat()) {
case DocumentExporter::JsonHashDataFormat: format = "JSON Hash"; break;
case DocumentExporter::JsonArrayDataFormat: format = "JSON Array"; break;
}
std::cout << " - Save data file: '" << exporter.dataFilename() << "'\n"
<< " - Data format: " << format << "\n";
if (!exporter.filenameFormat().empty()) {
std::cout << " - Filename format for JSON items: '"
<< exporter.filenameFormat() << "'\n";
}
}
}
void PreviewCliDelegate::execScript(const std::string& filename)
{
std::cout << "- Run script: '" << filename << "'\n";
}
void PreviewCliDelegate::showLayersFilter(const CliOpenFile& cof)
{
if (!cof.includeLayers.empty()) {
std::cout << " - Include layers:";
for (const auto& filter : cof.includeLayers)
std::cout << ' ' << filter;
std::cout << "\n";
}
if (!cof.excludeLayers.empty()) {
std::cout << " - Exclude layers:";
for (const auto& filter : cof.excludeLayers)
std::cout << ' ' << filter;
std::cout << "\n";
}
}
} // namespace app

View File

@ -0,0 +1,36 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CLI_PREVIEW_CLI_DELEGATE_H_INCLUDED
#define APP_CLI_PREVIEW_CLI_DELEGATE_H_INCLUDED
#pragma once
#include "app/cli/cli_delegate.h"
namespace app {
class PreviewCliDelegate : public CliDelegate {
public:
void showHelp(const AppOptions& programOptions) override;
void showVersion() override;
void uiMode() override;
void shellMode() override;
void batchMode() override;
void beforeOpenFile(const CliOpenFile& cof) override;
void afterOpenFile(const CliOpenFile& cof) override;
void saveFile(const CliOpenFile& cof) override;
void loadPalette(const CliOpenFile& cof,
const std::string& filename) override;
void exportFiles(DocumentExporter& exporter) override;
void execScript(const std::string& filename) override;
private:
void showLayersFilter(const CliOpenFile& cof);
};
} // namespace app
#endif

View File

@ -12,6 +12,9 @@
#include <typeinfo>
// Uncomment this in case you want to check what Cmd is being executed on each step
//#define TRACE_CMD
namespace app {
Cmd::Cmd()
@ -27,7 +30,9 @@ Cmd::~Cmd()
void Cmd::execute(Context* ctx)
{
#ifdef TRACE_CMD
TRACE("CMD: Executing cmd '%s'\n", typeid(*this).name());
#endif
ASSERT(m_state == State::NotExecuted);
m_ctx = ctx;
@ -42,7 +47,9 @@ void Cmd::execute(Context* ctx)
void Cmd::undo()
{
#ifdef TRACE_CMD
TRACE("CMD: Undo cmd '%s'\n", typeid(*this).name());
#endif
ASSERT(m_state == State::Executed || m_state == State::Redone);
onUndo();
@ -55,7 +62,9 @@ void Cmd::undo()
void Cmd::redo()
{
#ifdef TRACE_CMD
TRACE("CMD: Redo cmd '%s'\n", typeid(*this).name());
#endif
ASSERT(m_state == State::Undone);
onRedo();

View File

@ -38,6 +38,8 @@ void AddCel::onExecute()
{
Layer* layer = this->layer();
Cel* cel = this->cel();
ASSERT(layer);
ASSERT(cel);
addCel(layer, cel);
}
@ -46,6 +48,8 @@ void AddCel::onUndo()
{
Layer* layer = this->layer();
Cel* cel = this->cel();
ASSERT(layer);
ASSERT(cel);
// Save the CelData only if the cel isn't linked
bool has_data = (cel->links() == 0);
@ -63,6 +67,7 @@ void AddCel::onUndo()
void AddCel::onRedo()
{
Layer* layer = this->layer();
ASSERT(layer);
SubObjectsFromSprite io(layer->sprite());
bool has_data = (read8(m_stream) != 0);
@ -74,6 +79,7 @@ void AddCel::onRedo()
io.addCelDataRef(celdata);
}
Cel* cel = read_cel(m_stream, &io);
ASSERT(cel);
addCel(layer, cel);

View File

@ -21,8 +21,8 @@ namespace cmd {
using namespace doc;
AddLayer::AddLayer(Layer* folder, Layer* newLayer, Layer* afterThis)
: m_folder(folder)
AddLayer::AddLayer(Layer* group, Layer* newLayer, Layer* afterThis)
: m_group(group)
, m_newLayer(newLayer)
, m_afterThis(afterThis)
, m_size(0)
@ -31,63 +31,62 @@ AddLayer::AddLayer(Layer* folder, Layer* newLayer, Layer* afterThis)
void AddLayer::onExecute()
{
Layer* folder = m_folder.layer();
Layer* group = m_group.layer();
Layer* newLayer = m_newLayer.layer();
Layer* afterThis = m_afterThis.layer();
addLayer(folder, newLayer, afterThis);
addLayer(group, newLayer, afterThis);
}
void AddLayer::onUndo()
{
Layer* folder = m_folder.layer();
Layer* group = m_group.layer();
Layer* layer = m_newLayer.layer();
write_layer(m_stream, layer);
m_size = size_t(m_stream.tellp());
removeLayer(folder, layer);
removeLayer(group, layer);
}
void AddLayer::onRedo()
{
Layer* folder = m_folder.layer();
SubObjectsFromSprite io(folder->sprite());
Layer* group = m_group.layer();
SubObjectsFromSprite io(group->sprite());
Layer* newLayer = read_layer(m_stream, &io);
Layer* afterThis = m_afterThis.layer();
addLayer(folder, newLayer, afterThis);
addLayer(group, newLayer, afterThis);
m_stream.str(std::string());
m_stream.clear();
m_size = 0;
}
void AddLayer::addLayer(Layer* folder, Layer* newLayer, Layer* afterThis)
void AddLayer::addLayer(Layer* group, Layer* newLayer, Layer* afterThis)
{
static_cast<LayerFolder*>(folder)->addLayer(newLayer);
static_cast<LayerFolder*>(folder)->stackLayer(newLayer, afterThis);
folder->incrementVersion();
folder->sprite()->incrementVersion();
static_cast<LayerGroup*>(group)->insertLayer(newLayer, afterThis);
group->incrementVersion();
group->sprite()->incrementVersion();
Document* doc = folder->sprite()->document();
Document* doc = group->sprite()->document();
DocumentEvent ev(doc);
ev.sprite(folder->sprite());
ev.sprite(group->sprite());
ev.layer(newLayer);
doc->notify_observers<DocumentEvent&>(&DocumentObserver::onAddLayer, ev);
}
void AddLayer::removeLayer(Layer* folder, Layer* layer)
void AddLayer::removeLayer(Layer* group, Layer* layer)
{
Document* doc = folder->sprite()->document();
Document* doc = group->sprite()->document();
DocumentEvent ev(doc);
ev.sprite(layer->sprite());
ev.layer(layer);
doc->notify_observers<DocumentEvent&>(&DocumentObserver::onBeforeRemoveLayer, ev);
static_cast<LayerFolder*>(folder)->removeLayer(layer);
folder->incrementVersion();
folder->sprite()->incrementVersion();
static_cast<LayerGroup*>(group)->removeLayer(layer);
group->incrementVersion();
group->sprite()->incrementVersion();
doc->notify_observers<DocumentEvent&>(&DocumentObserver::onAfterRemoveLayer, ev);

View File

@ -23,7 +23,7 @@ namespace cmd {
class AddLayer : public Cmd {
public:
AddLayer(Layer* folder, Layer* newLayer, Layer* afterThis);
AddLayer(Layer* group, Layer* newLayer, Layer* afterThis);
protected:
void onExecute() override;
@ -34,10 +34,10 @@ namespace cmd {
}
private:
void addLayer(Layer* folder, Layer* newLayer, Layer* afterThis);
void removeLayer(Layer* folder, Layer* layer);
void addLayer(Layer* group, Layer* newLayer, Layer* afterThis);
void removeLayer(Layer* group, Layer* layer);
WithLayer m_folder;
WithLayer m_group;
WithLayer m_newLayer;
WithLayer m_afterThis;
size_t m_size;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -33,6 +33,7 @@ BackgroundFromLayer::BackgroundFromLayer(Layer* layer)
ASSERT(layer);
ASSERT(layer->isVisible());
ASSERT(layer->isEditable());
ASSERT(!layer->isReference());
ASSERT(layer->sprite() != NULL);
ASSERT(layer->sprite()->backgroundLayer() == NULL);
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -14,6 +14,7 @@
#include "app/cmd/set_layer_flags.h"
#include "app/cmd/set_layer_name.h"
#include "app/cmd/set_layer_opacity.h"
#include "doc/sprite.h"
namespace app {
namespace cmd {
@ -32,7 +33,7 @@ ConfigureBackground::ConfigureBackground(Layer* layer)
add(new cmd::SetLayerOpacity(static_cast<LayerImage*>(layer), 255));
}
add(new cmd::MoveLayer(layer, nullptr));
add(new cmd::MoveLayer(layer, layer->sprite()->root(), nullptr));
}
} // namespace cmd

View File

@ -115,7 +115,7 @@ void CopyCel::onExecute()
dstCel->setFrame(m_dstFrame);
}
else
dstCel = create_cel_copy(srcCel, dstSprite, m_dstFrame);
dstCel = create_cel_copy(srcCel, dstSprite, dstLayer, m_dstFrame);
executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -40,8 +40,7 @@ void CopyFrame::onExecute()
if (fromFrame >= m_newFrame)
++fromFrame;
for (int i=0; i<sprite->countLayers(); ++i) {
Layer* layer = sprite->layer(i);
for (Layer* layer : sprite->allLayers()) {
if (layer->isImage()) {
executeAndAdd(new cmd::CopyCel(
static_cast<LayerImage*>(layer), fromFrame,

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -55,7 +55,7 @@ void FlattenLayers::onExecute()
// Create a new transparent layer to flatten everything onto.
flatLayer = new LayerImage(sprite);
ASSERT(flatLayer->isVisible());
executeAndAdd(new cmd::AddLayer(sprite->folder(), flatLayer, nullptr));
executeAndAdd(new cmd::AddLayer(sprite->root(), flatLayer, nullptr));
executeAndAdd(new cmd::SetLayerName(flatLayer, "Flattened"));
bgcolor = sprite->transparentColor();
}
@ -91,7 +91,7 @@ void FlattenLayers::onExecute()
}
// Delete old layers.
LayerList layers = sprite->folder()->getLayersList();
LayerList layers = sprite->root()->layers();
for (Layer* layer : layers)
if (layer != flatLayer)
executeAndAdd(new cmd::RemoveLayer(layer));

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -24,6 +24,7 @@ LayerFromBackground::LayerFromBackground(Layer* layer)
ASSERT(layer->isVisible());
ASSERT(layer->isEditable());
ASSERT(layer->isBackground());
ASSERT(!layer->isReference());
ASSERT(layer->sprite() != NULL);
ASSERT(layer->sprite()->backgroundLayer() != NULL);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -119,7 +119,7 @@ void MoveCel::onExecute()
executeAndAdd(new cmd::SetCelFrame(srcCel, m_dstFrame));
}
else {
dstCel = create_cel_copy(srcCel, dstSprite, m_dstFrame);
dstCel = create_cel_copy(srcCel, dstSprite, dstLayer, m_dstFrame);
executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
executeAndAdd(new cmd::ClearCel(srcCel));

View File

@ -20,29 +20,73 @@ namespace cmd {
using namespace doc;
MoveLayer::MoveLayer(Layer* layer, Layer* afterThis)
MoveLayer::MoveLayer(Layer* layer,
Layer* newParent,
Layer* afterThis)
: m_layer(layer)
, m_oldParent(layer->parent())
, m_oldAfterThis(layer->getPrevious())
, m_newAfterThis(afterThis)
, m_newParent(newParent)
, m_newAfterThis(afterThis == layer ? afterThis->getPrevious(): afterThis)
{
}
void MoveLayer::onExecute()
{
m_layer.layer()->parent()->stackLayer(
m_layer.layer(),
m_newAfterThis.layer());
Layer* layer = m_layer.layer();
Layer* afterThis = m_newAfterThis.layer();
LayerGroup* oldParent = static_cast<LayerGroup*>(m_oldParent.layer());
LayerGroup* newParent = static_cast<LayerGroup*>(m_newParent.layer());
ASSERT(layer);
ASSERT(oldParent);
ASSERT(newParent);
m_layer.layer()->parent()->incrementVersion();
#if _DEBUG // Check that we are not inserting a layer inside itself
{
Layer* p = newParent;
while (p) {
ASSERT(p != layer);
p = p->parent();
}
}
#endif
oldParent->removeLayer(layer);
newParent->insertLayer(layer, afterThis);
if (oldParent != newParent)
oldParent->incrementVersion();
newParent->incrementVersion();
layer->sprite()->incrementVersion();
}
void MoveLayer::onUndo()
{
m_layer.layer()->parent()->stackLayer(
m_layer.layer(),
m_oldAfterThis.layer());
Layer* layer = m_layer.layer();
Layer* afterThis = m_oldAfterThis.layer();
LayerGroup* oldParent = static_cast<LayerGroup*>(m_oldParent.layer());
LayerGroup* newParent = static_cast<LayerGroup*>(m_newParent.layer());
ASSERT(layer);
ASSERT(oldParent);
ASSERT(newParent);
m_layer.layer()->parent()->incrementVersion();
#if _DEBUG // Check that we are not inserting a layer inside itself
{
Layer* p = newParent;
while (p) {
ASSERT(p != layer);
p = p->parent();
}
}
#endif
newParent->removeLayer(layer);
oldParent->insertLayer(layer, afterThis);
if (oldParent != newParent)
oldParent->incrementVersion();
newParent->incrementVersion();
layer->sprite()->incrementVersion();
}
void MoveLayer::onFireNotifications()

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -17,7 +17,9 @@ namespace cmd {
class MoveLayer : public Cmd {
public:
MoveLayer(Layer* layer, Layer* afterThis);
MoveLayer(Layer* layer,
Layer* newParent,
Layer* afterThis);
protected:
void onExecute() override;
@ -29,8 +31,8 @@ namespace cmd {
private:
WithLayer m_layer;
WithLayer m_oldAfterThis;
WithLayer m_newAfterThis;
WithLayer m_oldParent, m_oldAfterThis;
WithLayer m_newParent, m_newAfterThis;
};
} // namespace cmd

View File

@ -0,0 +1,51 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/set_cel_bounds.h"
#include "app/document.h"
#include "doc/cel.h"
#include "doc/document_event.h"
namespace app {
namespace cmd {
using namespace doc;
SetCelBoundsF::SetCelBoundsF(Cel* cel, const gfx::RectF& bounds)
: WithCel(cel)
, m_oldBounds(cel->boundsF())
, m_newBounds(bounds)
{
}
void SetCelBoundsF::onExecute()
{
cel()->setBoundsF(m_newBounds);
cel()->incrementVersion();
}
void SetCelBoundsF::onUndo()
{
cel()->setBoundsF(m_oldBounds);
cel()->incrementVersion();
}
void SetCelBoundsF::onFireNotifications()
{
Cel* cel = this->cel();
DocumentEvent ev(cel->document());
ev.sprite(cel->sprite());
ev.cel(cel);
cel->document()->notify_observers<DocumentEvent&>(&DocumentObserver::onCelPositionChanged, ev);
}
} // namespace cmd
} // namespace app

View File

@ -0,0 +1,40 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_SET_CEL_BOUNDS_H_INCLUDED
#define APP_CMD_SET_CEL_BOUNDS_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_cel.h"
#include "gfx/rect.h"
namespace app {
namespace cmd {
using namespace doc;
class SetCelBoundsF : public Cmd
, public WithCel {
public:
SetCelBoundsF(Cel* cel, const gfx::RectF& bounds);
protected:
void onExecute() override;
void onUndo() override;
void onFireNotifications() override;
size_t onMemSize() const override {
return sizeof(*this);
}
private:
gfx::RectF m_oldBounds;
gfx::RectF m_newBounds;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -30,13 +30,13 @@ SetCelPosition::SetCelPosition(Cel* cel, int x, int y)
void SetCelPosition::onExecute()
{
cel()->data()->setPosition(m_newX, m_newY);
cel()->data()->setPosition(gfx::Point(m_newX, m_newY));
cel()->data()->incrementVersion();
}
void SetCelPosition::onUndo()
{
cel()->data()->setPosition(m_oldX, m_oldY);
cel()->data()->setPosition(gfx::Point(m_oldX, m_oldY));
cel()->data()->incrementVersion();
}

View File

@ -0,0 +1,53 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/set_pixel_ratio.h"
#include "doc/document.h"
#include "doc/document_event.h"
#include "doc/document_observer.h"
#include "doc/sprite.h"
namespace app {
namespace cmd {
using namespace doc;
SetPixelRatio::SetPixelRatio(Sprite* sprite, PixelRatio pixelRatio)
: WithSprite(sprite)
, m_oldRatio(sprite->pixelRatio())
, m_newRatio(pixelRatio)
{
}
void SetPixelRatio::onExecute()
{
Sprite* spr = sprite();
spr->setPixelRatio(m_newRatio);
spr->incrementVersion();
}
void SetPixelRatio::onUndo()
{
Sprite* spr = sprite();
spr->setPixelRatio(m_oldRatio);
spr->incrementVersion();
}
void SetPixelRatio::onFireNotifications()
{
Sprite* sprite = this->sprite();
DocumentEvent ev(sprite->document());
ev.sprite(sprite);
sprite->document()->notify_observers<DocumentEvent&>(&DocumentObserver::onSpritePixelRatioChanged, ev);
}
} // namespace cmd
} // namespace app

View File

@ -0,0 +1,46 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_SET_PIXEL_RATIO_H_INCLUDED
#define APP_CMD_SET_PIXEL_RATIO_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_sprite.h"
#include "doc/pixel_ratio.h"
namespace doc {
class Sprite;
}
namespace app {
namespace cmd {
using namespace doc;
class SetPixelRatio : public Cmd
, public WithSprite {
public:
SetPixelRatio(Sprite* sprite, PixelRatio pixelRatio);
protected:
void onExecute() override;
void onUndo() override;
void onFireNotifications() override;
size_t onMemSize() const override {
return sizeof(*this);
}
private:
void setRatio(PixelRatio ratio);
PixelRatio m_oldRatio;
PixelRatio m_newRatio;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -64,7 +64,7 @@ std::string CmdTransaction::onLabel() const
doc::SpritePosition CmdTransaction::calcSpritePosition()
{
doc::Site site = context()->activeSite();
return doc::SpritePosition(site.layerIndex(), site.frame());
return doc::SpritePosition(site.layer(), site.frame());
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -23,6 +23,37 @@
namespace app {
namespace {
bool get_cel_pixel(const Cel* cel,
const double x,
const double y,
const frame_t frame,
color_t& output)
{
gfx::RectF celBounds;
if (cel->layer()->isReference())
celBounds = cel->boundsF();
else
celBounds = cel->bounds();
const doc::Image* image = cel->image();
gfx::PointF pos(x, y);
if (!celBounds.contains(pos))
return false;
pos.x = (pos.x-celBounds.x)*image->width()/celBounds.w;
pos.y = (pos.y-celBounds.y)*image->height()/celBounds.h;
const gfx::Point ipos(pos);
if (!image->bounds().contains(ipos))
return false;
output = get_pixel(image, ipos.x, ipos.y);
return true;
}
}
ColorPicker::ColorPicker()
: m_alpha(0)
, m_layer(NULL)
@ -30,10 +61,12 @@ ColorPicker::ColorPicker()
}
void ColorPicker::pickColor(const doc::Site& site,
const gfx::Point& _pos, Mode mode)
const gfx::PointF& _pos,
const render::Projection& proj,
const Mode mode)
{
const doc::Sprite* sprite = site.sprite();
gfx::Point pos = _pos;
gfx::PointF pos = _pos;
m_alpha = 255;
m_color = app::Color::fromMask();
@ -44,42 +77,71 @@ void ColorPicker::pickColor(const doc::Site& site,
DocumentPreferences& docPref = Preferences::instance().document(doc);
if (int(docPref.tiled.mode()) & int(filters::TiledMode::X_AXIS))
pos.x = wrap_value(pos.x, site.sprite()->width());
pos.x = wrap_value<double>(pos.x, site.sprite()->width());
if (int(docPref.tiled.mode()) & int(filters::TiledMode::Y_AXIS))
pos.y = wrap_value(pos.y, site.sprite()->height());
pos.y = wrap_value<double>(pos.y, site.sprite()->height());
}
// Get the color from the image
if (mode == FromComposition) { // Pick from the composed image
m_color = app::Color::fromImage(
sprite->pixelFormat(),
render::get_sprite_pixel(sprite, pos.x, pos.y, site.frame()));
switch (mode) {
doc::CelList cels;
sprite->pickCels(pos.x, pos.y, site.frame(), 128, cels);
if (!cels.empty())
m_layer = cels.front()->layer();
}
else { // Pick from the current layer
int u, v;
doc::Image* image = site.image(&u, &v, NULL);
gfx::Point pt(pos.x-u, pos.y-v);
// Pick from the composed image
case FromComposition: {
m_color = app::Color::fromImage(
sprite->pixelFormat(),
render::get_sprite_pixel(sprite, pos.x, pos.y,
site.frame(), proj));
if (image && image->bounds().contains(pt)) {
doc::color_t imageColor = get_pixel(image, pt.x, pt.y);
doc::CelList cels;
sprite->pickCels(pos.x, pos.y, site.frame(), 128,
sprite->allVisibleLayers(), cels);
if (!cels.empty())
m_layer = cels.front()->layer();
break;
}
switch (image->pixelFormat()) {
case IMAGE_RGB:
m_alpha = doc::rgba_geta(imageColor);
break;
case IMAGE_GRAYSCALE:
m_alpha = doc::graya_geta(imageColor);
break;
// Pick from the current layer
case FromActiveLayer: {
const Cel* cel = site.cel();
if (cel) {
doc::color_t imageColor;
if (!get_cel_pixel(cel, pos.x, pos.y,
site.frame(), imageColor))
return;
const doc::Image* image = cel->image();
switch (image->pixelFormat()) {
case IMAGE_RGB:
m_alpha = doc::rgba_geta(imageColor);
break;
case IMAGE_GRAYSCALE:
m_alpha = doc::graya_geta(imageColor);
break;
}
m_color = app::Color::fromImage(image->pixelFormat(), imageColor);
m_layer = const_cast<Layer*>(site.layer());
}
break;
}
m_color = app::Color::fromImage(image->pixelFormat(), imageColor);
m_layer = const_cast<Layer*>(site.layer());
case FromFirstReferenceLayer: {
doc::CelList cels;
sprite->pickCels(pos.x, pos.y, site.frame(), 128,
sprite->allVisibleReferenceLayers(), cels);
for (const Cel* cel : cels) {
doc::color_t imageColor;
if (get_cel_pixel(cel, pos.x, pos.y,
site.frame(), imageColor)) {
m_color = app::Color::fromImage(
cel->image()->pixelFormat(), imageColor);
m_layer = cel->layer();
break;
}
}
break;
}
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -16,16 +16,26 @@ namespace doc {
class Site;
}
namespace render {
class Projection;
}
namespace app {
class ColorPicker {
public:
enum Mode { FromComposition, FromActiveLayer };
enum Mode {
FromComposition,
FromActiveLayer,
FromFirstReferenceLayer
};
ColorPicker();
void pickColor(const doc::Site& site,
const gfx::Point& pos, Mode mode);
const gfx::PointF& pos,
const render::Projection& proj,
const Mode mode);
app::Color color() const { return m_color; }
int alpha() const { return m_alpha; }

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -44,7 +44,9 @@ bool BackgroundFromLayerCommand::onEnabled(Context* context)
ContextFlags::ActiveLayerIsEditable |
ContextFlags::ActiveLayerIsImage) &&
// Doesn't have a background layer
!context->checkFlags(ContextFlags::HasBackgroundLayer);
!context->checkFlags(ContextFlags::HasBackgroundLayer) &&
// Isn't a reference layer
!context->checkFlags(ContextFlags::ActiveLayerIsReference);
}
void BackgroundFromLayerCommand::onExecute(Context* context)

View File

@ -107,9 +107,8 @@ private:
else if (m_range.enabled()) {
Sprite* sprite = m_document->sprite();
int count = 0;
for (Cel* cel : sprite->uniqueCels(m_range.frameBegin(),
m_range.frameEnd())) {
if (m_range.inRange(sprite->layerToIndex(cel->layer()))) {
for (Cel* cel : sprite->uniqueCels(m_range.selectedFrames())) {
if (m_range.contains(cel->layer())) {
if (backgroundCount && cel->layer()->isBackground())
++(*backgroundCount);
++count;
@ -171,37 +170,28 @@ private:
ContextWriter writer(UIContext::instance());
Transaction transaction(writer.context(), "Set Cel Properties");
if (count == 1 && m_cel) {
if (!m_cel->layer()->isBackground() &&
newOpacity != m_cel->opacity()) {
transaction.execute(new cmd::SetCelOpacity(writer.cel(), newOpacity));
}
if (m_userData != m_cel->data()->userData()) {
transaction.execute(new cmd::SetUserData(writer.cel()->data(), m_userData));
// Redraw timeline because the cel's user data/color
// might have changed.
App::instance()->timeline()->invalidate();
}
DocumentRange range;
if (m_range.enabled())
range = m_range;
else {
range.startRange(m_cel->layer(), m_cel->frame(), DocumentRange::kCels);
range.endRange(m_cel->layer(), m_cel->frame());
}
else if (m_range.enabled()) {
Sprite* sprite = m_document->sprite();
for (Cel* cel : sprite->uniqueCels(m_range.frameBegin(),
m_range.frameEnd())) {
if (m_range.inRange(sprite->layerToIndex(cel->layer()))) {
if (!cel->layer()->isBackground() && newOpacity != cel->opacity()) {
transaction.execute(new cmd::SetCelOpacity(cel, newOpacity));
}
if (m_newUserData &&
m_userData != cel->data()->userData()) {
transaction.execute(new cmd::SetUserData(cel->data(), m_userData));
Sprite* sprite = m_document->sprite();
for (Cel* cel : sprite->uniqueCels(range.selectedFrames())) {
if (range.contains(cel->layer())) {
if (!cel->layer()->isBackground() && newOpacity != cel->opacity()) {
transaction.execute(new cmd::SetCelOpacity(cel, newOpacity));
}
// Redraw timeline because the cel's user data/color
// might have changed.
App::instance()->timeline()->invalidate();
}
if (m_newUserData &&
m_userData != cel->data()->userData()) {
transaction.execute(new cmd::SetUserData(cel->data(), m_userData));
// Redraw timeline because the cel's user data/color
// might have changed.
App::instance()->timeline()->invalidate();
}
}
}

View File

@ -15,7 +15,6 @@
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
@ -52,33 +51,29 @@ void ClearCelCommand::onExecute(Context* context)
{
Transaction transaction(writer.context(), "Clear Cel");
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
Sprite* sprite = writer.sprite();
for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) {
Layer* layer = sprite->indexToLayer(layerIdx);
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty() &&
!site->selectedFrames().empty()) {
for (Layer* layer : site->selectedLayers()) {
if (!layer->isImage())
continue;
if (!layer->isEditableHierarchy()) {
nonEditableLayers = true;
continue;
}
LayerImage* layerImage = static_cast<LayerImage*>(layer);
for (frame_t frame = range.frameEnd(),
begin = range.frameBegin()-1;
frame != begin;
--frame) {
if (layerImage->cel(frame)) {
if (layerImage->isEditable())
document->getApi(transaction).clearCel(layerImage, frame);
else
nonEditableLayers = true;
}
for (frame_t frame : site->selectedFrames().reversed()) {
if (layerImage->cel(frame))
document->getApi(transaction).clearCel(layerImage, frame);
}
}
}
else if (writer.cel()) {
if (writer.layer()->isEditable())
if (writer.layer()->isEditableHierarchy())
document->getApi(transaction).clearCel(writer.cel());
else
nonEditableLayers = true;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -57,7 +57,9 @@ void DuplicateLayerCommand::onExecute(Context* context)
Transaction transaction(writer.context(), "Layer Duplication");
LayerImage* sourceLayer = static_cast<LayerImage*>(writer.layer());
DocumentApi api = document->getApi(transaction);
api.duplicateLayerAfter(sourceLayer, sourceLayer);
api.duplicateLayerAfter(sourceLayer,
sourceLayer->parent(),
sourceLayer);
transaction.commit();
}

View File

@ -8,7 +8,6 @@
#include "config.h"
#endif
#include "base/string.h"
#include "app/app.h"
#include "app/commands/command.h"
#include "app/context.h"
@ -19,12 +18,15 @@
#include "app/file_selector.h"
#include "app/modules/editors.h"
#include "app/pref/preferences.h"
#include "app/restore_visible_layers.h"
#include "app/ui/editor/editor.h"
#include "app/ui/layer_frame_comboboxes.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "base/bind.h"
#include "base/convert_to.h"
#include "base/fs.h"
#include "base/string.h"
#include "doc/frame_tag.h"
#include "doc/layer.h"
@ -39,11 +41,6 @@ using namespace ui;
namespace {
static const char* kAllLayers = "";
static const char* kAllFrames = "";
static const char* kSelectedLayers = "**selected-layers**";
static const char* kSelectedFrames = "**selected-frames**";
// Special key value used in default preferences to know if by default
// the user wants to generate texture and/or files.
static const char* kSpecifiedFilename = "**filename**";
@ -156,121 +153,13 @@ namespace {
return true;
}
class SelectedFrameTag {
public:
static frame_t From() {
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
return range.frameBegin();
}
else if (current_editor) {
return current_editor->frame();
}
else
return 0;
}
static frame_t To() {
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
return range.frameEnd();
}
else if (current_editor) {
return current_editor->frame();
}
else
return 0;
}
SelectedFrameTag() : m_frameTag(nullptr) {
}
~SelectedFrameTag() {
if (m_frameTag) {
m_frameTag->owner()->remove(m_frameTag);
delete m_frameTag;
}
}
FrameTag* create(Sprite* sprite) {
m_frameTag = new FrameTag(From(), To());
sprite->frameTags().add(m_frameTag);
return m_frameTag;
}
private:
FrameTag* m_frameTag;
};
class SelectedLayers {
public:
~SelectedLayers() {
for (auto item : m_restore)
item.first->setVisible(item.second);
}
void showSelectedLayers(Sprite* sprite) {
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (!range.enabled()) {
if (current_editor) {
ASSERT(current_editor->sprite() == sprite);
range.startRange(sprite->layerToIndex(current_editor->layer()),
current_editor->frame(), DocumentRange::kCels);
range.endRange(sprite->layerToIndex(current_editor->layer()),
current_editor->frame());
}
else
return;
}
std::vector<Layer*> layers;
sprite->getLayersList(layers);
for (int i=0; i<int(layers.size()); ++i) {
Layer* layer = layers[i];
bool selected = range.inRange(LayerIndex(i));
if (selected != layer->isVisible()) {
m_restore.push_back(std::make_pair(layer, layer->isVisible()));
layer->setVisible(selected);
}
}
}
private:
std::vector<std::pair<Layer*, bool> > m_restore;
};
}
class ExportSpriteSheetWindow : public app::gen::ExportSpriteSheet {
public:
class LayerItem : public ListItem {
public:
LayerItem(Layer* layer)
: ListItem("Layer: " + layer->name())
, m_layer(layer) {
}
Layer* layer() const { return m_layer; }
private:
Layer* m_layer;
};
class TagItem : public ListItem {
public:
TagItem(FrameTag* tag)
: ListItem("Tag: " + tag->name())
, m_tag(tag) {
}
FrameTag* tag() const { return m_tag; }
private:
FrameTag* m_tag;
};
ExportSpriteSheetWindow(Document* doc, Sprite* sprite,
DocumentPreferences& docPref)
: m_sprite(sprite)
ExportSpriteSheetWindow(Site& site, DocumentPreferences& docPref)
: m_site(site)
, m_sprite(site.sprite())
, m_docPref(docPref)
, m_filenameAskOverwrite(true)
, m_dataFilenameAskOverwrite(true)
@ -290,29 +179,11 @@ public:
if (m_docPref.spriteSheet.type() != app::SpriteSheetType::None)
sheetType()->setSelectedItemIndex((int)m_docPref.spriteSheet.type()-1);
layers()->addItem("Visible layers");
int i = layers()->addItem("Selected layers");
if (m_docPref.spriteSheet.layer() == kSelectedLayers)
layers()->setSelectedItemIndex(i);
{
std::vector<Layer*> layersList;
m_sprite->getLayersList(layersList);
for (Layer* layer : layersList) {
i = layers()->addItem(new LayerItem(layer));
if (m_docPref.spriteSheet.layer() == layer->name())
layers()->setSelectedItemIndex(i);
}
}
fill_layers_combobox(
m_sprite, layers(), m_docPref.spriteSheet.layer());
frames()->addItem("All frames");
i = frames()->addItem("Selected frames");
if (m_docPref.spriteSheet.frameTag() == kSelectedFrames)
frames()->setSelectedItemIndex(i);
for (FrameTag* tag : m_sprite->frameTags()) {
i = frames()->addItem(new TagItem(tag));
if (m_docPref.spriteSheet.frameTag() == tag->name())
frames()->setSelectedItemIndex(i);
}
fill_frames_combobox(
m_sprite, frames(), m_docPref.spriteSheet.frameTag());
openGenerated()->setSelected(m_docPref.spriteSheet.openGenerated());
@ -362,12 +233,12 @@ public:
listTags()->setSelected(m_docPref.spriteSheet.listFrameTags());
updateDataFields();
std::string base = doc->filename();
std::string base = site.document()->filename();
base = base::join_path(base::get_file_path(base), base::get_file_title(base));
if (m_filename.empty() ||
m_filename == kSpecifiedFilename) {
if (base::utf8_icmp(base::get_file_extension(doc->filename()), "png") == 0)
if (base::utf8_icmp(base::get_file_extension(site.document()->filename()), "png") == 0)
m_filename = base + "-sheet.png";
else
m_filename = base + ".png";
@ -487,21 +358,11 @@ public:
}
std::string layerValue() const {
if (LayerItem* item = dynamic_cast<LayerItem*>(layers()->getSelectedItem()))
return item->layer()->name();
else if (layers()->getSelectedItemIndex() == 1)
return kSelectedLayers;
else
return kAllLayers;
return layers()->getValue();
}
std::string frameTagValue() const {
if (TagItem* item = dynamic_cast<TagItem*>(frames()->getSelectedItem()))
return item->tag()->name();
else if (frames()->getSelectedItemIndex() == 1)
return kSelectedFrames;
else
return kAllFrames;
return frames()->getValue();
}
bool listLayersValue() const {
@ -653,16 +514,12 @@ private:
}
void updateSizeFields() {
int nframes = m_sprite->totalFrames();
std::string tagName = frameTagValue();
if (tagName == kSelectedFrames) {
nframes = SelectedFrameTag::To() - SelectedFrameTag::From() + 1;
}
else {
FrameTag* frameTag = m_sprite->frameTags().getByName(tagName);
if (frameTag)
nframes = frameTag->toFrame() - frameTag->fromFrame() + 1;
}
SelectedFrames selFrames;
calculate_selected_frames(m_site,
frameTagValue(),
selFrames);
frame_t nframes = selFrames.size();
Fit fit;
if (bestFit()->isSelected()) {
@ -691,6 +548,7 @@ private:
dataMeta()->setVisible(state);
}
Site& m_site;
Sprite* m_sprite;
DocumentPreferences& m_docPref;
std::string m_filename;
@ -745,13 +603,14 @@ bool ExportSpriteSheetCommand::onEnabled(Context* context)
void ExportSpriteSheetCommand::onExecute(Context* context)
{
Document* document(context->activeDocument());
Sprite* sprite = document->sprite();
Site site = context->activeSite();
Document* document = static_cast<Document*>(site.document());
Sprite* sprite = site.sprite();
DocumentPreferences& docPref(Preferences::instance().document(document));
bool askOverwrite = m_askOverwrite;
if (m_useUI && context->isUIAvailable()) {
ExportSpriteSheetWindow window(document, sprite, docPref);
ExportSpriteSheetWindow window(site, docPref);
window.openWindowInForeground();
if (!window.ok())
return;
@ -814,42 +673,29 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
return; // Do not overwrite
}
// If the user want to export selected frames, we can create a
// temporal frame tag for that.
FrameTag* frameTag;
bool isTemporalTag = false;
SelectedFrameTag selectedFrameTag;
if (frameTagName == kSelectedFrames) {
frameTag = selectedFrameTag.create(sprite);
isTemporalTag = true;
}
else if (frameTagName != kAllFrames)
frameTag = sprite->frameTags().getByName(frameTagName);
else
frameTag = nullptr;
SelectedFrames selFrames;
FrameTag* frameTag =
calculate_selected_frames(site, frameTagName, selFrames);
frame_t nframes = selFrames.size();
ASSERT(nframes > 0);
// If the user choose to render selected layers only, we can
// temporaly make them visible and hide the other ones.
Layer* layer = nullptr;
SelectedLayers layersVisibility;
if (layerName == kSelectedLayers) {
layersVisibility.showSelectedLayers(sprite);
}
else {
RestoreVisibleLayers layersVisibility;
calculate_visible_layers(site, layerName, layersVisibility);
SelectedLayers selLayers;
if (layerName != kSelectedLayers) {
// TODO add a getLayerByName
std::vector<Layer*> layers;
sprite->getLayersList(layers);
for (Layer* l : layers) {
if (l->name() == layerName) {
layer = l;
for (Layer* layer : sprite->allLayers()) {
if (layer->name() == layerName) {
selLayers.insert(layer);
break;
}
}
}
int nframes = (frameTag ? frameTag->toFrame() - frameTag->fromFrame() + 1:
sprite->totalFrames());
if (bestFit) {
Fit fit = best_fit(sprite, nframes, borderPadding, shapePadding, innerPadding);
columns = fit.columns;
@ -901,7 +747,9 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
exporter.setListLayers(true);
if (listFrameTags)
exporter.setListFrameTags(true);
exporter.addDocument(document, layer, frameTag, isTemporalTag);
exporter.addDocument(document, frameTag,
(!selLayers.empty() ? &selLayers: nullptr),
(!selFrames.empty() ? &selFrames: nullptr));
base::UniquePtr<Document> newDocument(exporter.exportSheet());
if (!newDocument)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -40,20 +40,27 @@ EyedropperCommand::EyedropperCommand()
}
void EyedropperCommand::pickSample(const doc::Site& site,
const gfx::Point& pixelPos,
const gfx::PointF& pixelPos,
const render::Projection& proj,
app::Color& color)
{
// Check if we've to grab alpha channel or the merged color.
Preferences& pref = Preferences::instance();
bool allLayers =
(pref.eyedropper.sample() == app::gen::EyedropperSample::ALL_LAYERS);
ColorPicker::Mode mode = ColorPicker::FromComposition;
switch (pref.eyedropper.sample()) {
case app::gen::EyedropperSample::ALL_LAYERS:
mode = ColorPicker::FromComposition;
break;
case app::gen::EyedropperSample::CURRENT_LAYER:
mode = ColorPicker::FromActiveLayer;
break;
case app::gen::EyedropperSample::FIRST_REFERENCE_LAYER:
mode = ColorPicker::FromFirstReferenceLayer;
break;
}
ColorPicker picker;
picker.pickColor(site,
pixelPos,
(allLayers ?
ColorPicker::FromComposition:
ColorPicker::FromActiveLayer));
picker.pickColor(site, pixelPos, proj, mode);
app::gen::EyedropperChannel channel =
pref.eyedropper.channel();
@ -171,7 +178,7 @@ void EyedropperCommand::onExecute(Context* context)
}
// Pixel position to get
gfx::Point pixelPos = editor->screenToEditor(ui::get_mouse_position());
gfx::PointF pixelPos = editor->screenToEditorF(ui::get_mouse_position());
// Start with fg/bg color
Preferences& pref = Preferences::instance();
@ -179,7 +186,10 @@ void EyedropperCommand::onExecute(Context* context)
m_background ? pref.colorBar.bgColor():
pref.colorBar.fgColor();
pickSample(editor->getSite(), pixelPos, color);
pickSample(editor->getSite(),
pixelPos,
editor->projection(),
color);
if (m_background)
pref.colorBar.bgColor(color);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -15,6 +15,10 @@ namespace doc {
class Site;
}
namespace render {
class Projection;
}
namespace app {
class EyedropperCommand : public Command {
@ -24,7 +28,8 @@ namespace app {
// Returns the color in the given sprite pos.
void pickSample(const doc::Site& site,
const gfx::Point& pixelPos,
const gfx::PointF& pixelPos,
const render::Projection& proj,
app::Color& color);
protected:

View File

@ -13,6 +13,7 @@
#include "app/app.h"
#include "app/cmd/flip_mask.h"
#include "app/cmd/flip_masked_cel.h"
#include "app/cmd/set_cel_bounds.h"
#include "app/cmd/set_mask_position.h"
#include "app/cmd/trim_cel.h"
#include "app/commands/params.h"
@ -94,6 +95,10 @@ void FlipCommand::onExecute(Context* context)
Site site = *writer.site();
for (Cel* cel : cels) {
// TODO add support to flip masked part of a reference layer
if (cel->layer()->isReference())
continue;
site.frame(cel->frame());
site.layer(cel->layer());
@ -150,14 +155,27 @@ void FlipCommand::onExecute(Context* context)
for (Cel* cel : cels) {
Image* image = cel->image();
api.setCelPosition
(sprite, cel,
(m_flipType == doc::algorithm::FlipHorizontal ?
// Flip reference layer cel
if (cel->layer()->isReference()) {
gfx::RectF bounds = cel->boundsF();
if (m_flipType == doc::algorithm::FlipHorizontal)
bounds.x = sprite->width() - bounds.w - bounds.x;
if (m_flipType == doc::algorithm::FlipVertical)
bounds.y = sprite->height() - bounds.h - bounds.y;
transaction.execute(new cmd::SetCelBoundsF(cel, bounds));
}
else {
api.setCelPosition
(sprite, cel,
(m_flipType == doc::algorithm::FlipHorizontal ?
sprite->width() - image->width() - cel->x():
cel->x()),
(m_flipType == doc::algorithm::FlipVertical ?
(m_flipType == doc::algorithm::FlipVertical ?
sprite->height() - image->height() - cel->y():
cel->y()));
}
api.flipImage(image, image->bounds(), m_flipType);
}

View File

@ -11,9 +11,10 @@
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/document_api.h"
#include "app/ui/timeline.h"
#include "app/pref/preferences.h"
#include "app/transaction.h"
#include "base/convert_to.h"
#include "doc/sprite.h"
@ -80,53 +81,60 @@ void FramePropertiesCommand::onExecute(Context* context)
const ContextReader reader(context);
const Sprite* sprite = reader.sprite();
auto& docPref = Preferences::instance().document(context->activeDocument());
app::gen::FrameProperties window;
frame_t firstFrame = 0;
frame_t lastFrame = 0;
int base = docPref.timeline.firstFrame();
app::gen::FrameProperties window;
SelectedFrames selFrames;
switch (m_target) {
case ALL_FRAMES:
lastFrame = sprite->lastFrame();
selFrames.insert(0, sprite->lastFrame());
break;
case CURRENT_RANGE: {
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
firstFrame = range.frameBegin();
lastFrame = range.frameEnd();
Site site = context->activeSite();
if (site.inTimeline()) {
selFrames = site.selectedFrames();
}
else {
firstFrame = lastFrame = reader.frame();
selFrames.insert(site.frame());
}
break;
}
case SPECIFIC_FRAME:
firstFrame = lastFrame = m_frame-base;
selFrames.insert(m_frame-base);
break;
}
if (firstFrame != lastFrame)
window.frame()->setTextf("[%d...%d]", (int)firstFrame+base, (int)lastFrame+base);
else
window.frame()->setTextf("%d", (int)firstFrame+base);
ASSERT(!selFrames.empty());
if (selFrames.empty())
return;
window.frlen()->setTextf("%d", sprite->frameDuration(firstFrame));
if (selFrames.size() == 1)
window.frame()->setTextf("%d", selFrames.firstFrame()+base);
else if (selFrames.ranges() == 1) {
window.frame()->setTextf("[%d...%d]",
selFrames.firstFrame()+base,
selFrames.lastFrame()+base);
}
else
window.frame()->setTextf("Multiple Frames");
window.frlen()->setTextf(
"%d", sprite->frameDuration(selFrames.firstFrame()));
window.openWindowInForeground();
if (window.closer() == window.ok()) {
int num = window.frlen()->textInt();
int newMsecs = window.frlen()->textInt();
ContextWriter writer(reader);
Transaction transaction(writer.context(), "Frame Duration");
DocumentApi api = writer.document()->getApi(transaction);
if (firstFrame != lastFrame)
api.setFrameRangeDuration(writer.sprite(), firstFrame, lastFrame, num);
else
api.setFrameDuration(writer.sprite(), firstFrame, num);
for (frame_t frame : selFrames)
api.setFrameDuration(writer.sprite(), frame, newMsecs);
transaction.commit();
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -50,7 +50,7 @@ public:
, m_doc(editor->document())
, m_sprite(editor->sprite())
, m_pal(m_sprite->palette(editor->frame()))
, m_zoom(editor->zoom())
, m_proj(editor->projection())
, m_index_bg_color(-1)
, m_doublebuf(Image::create(IMAGE_RGB, ui::display_w(), ui::display_h()))
, m_doublesur(she::instance()->createRgbaSurface(ui::display_w(), ui::display_h())) {
@ -173,11 +173,13 @@ protected:
virtual void onPaint(PaintEvent& ev) override {
Graphics* g = ev.graphics();
AppRender& render = m_editor->renderEngine();
render.setRefLayersVisiblity(false);
render.setProjection(render::Projection());
render.disableOnionskin();
render.setBgType(render::BgType::TRANSPARENT);
// Render sprite and leave the result in 'm_render' variable
if (m_render == NULL) {
if (m_render == nullptr) {
ImageBufferPtr buf = Editor::getRenderImageBuffer();
m_render.reset(Image::create(IMAGE_RGB,
m_sprite->width(), m_sprite->height(), buf));
@ -187,19 +189,20 @@ protected:
}
int x, y, w, h, u, v;
x = m_pos.x + m_zoom.apply(m_zoom.remove(m_delta.x));
y = m_pos.y + m_zoom.apply(m_zoom.remove(m_delta.y));
w = m_zoom.apply(m_sprite->width());
h = m_zoom.apply(m_sprite->height());
x = m_pos.x + m_proj.applyX(m_proj.removeX(m_delta.x));
y = m_pos.y + m_proj.applyY(m_proj.removeY(m_delta.y));
w = m_proj.applyX(m_sprite->width());
h = m_proj.applyY(m_sprite->height());
if (int(m_tiled) & int(TiledMode::X_AXIS)) x = SGN(x) * (ABS(x)%w);
if (int(m_tiled) & int(TiledMode::Y_AXIS)) y = SGN(y) * (ABS(y)%h);
render.setProjection(m_proj);
if (m_index_bg_color == -1) {
render.setupBackground(m_doc, m_doublebuf->pixelFormat());
render.renderBackground(m_doublebuf,
gfx::Clip(0, 0, -m_pos.x, -m_pos.y,
m_doublebuf->width(), m_doublebuf->height()), m_zoom);
m_doublebuf->width(), m_doublebuf->height()));
}
else {
doc::clear_image(m_doublebuf, m_pal->getEntry(m_index_bg_color));
@ -208,23 +211,23 @@ protected:
switch (m_tiled) {
case TiledMode::NONE:
render.renderImage(m_doublebuf, m_render, m_pal, x, y,
m_zoom, 255, BlendMode::NORMAL);
255, BlendMode::NORMAL);
break;
case TiledMode::X_AXIS:
for (u=x-w; u<ui::display_w()+w; u+=w)
render.renderImage(m_doublebuf, m_render, m_pal, u, y,
m_zoom, 255, BlendMode::NORMAL);
255, BlendMode::NORMAL);
break;
case TiledMode::Y_AXIS:
for (v=y-h; v<ui::display_h()+h; v+=h)
render.renderImage(m_doublebuf, m_render, m_pal, x, v,
m_zoom, 255, BlendMode::NORMAL);
255, BlendMode::NORMAL);
break;
case TiledMode::BOTH:
for (v=y-h; v<ui::display_h()+h; v+=h)
for (u=x-w; u<ui::display_w()+w; u+=w)
render.renderImage(m_doublebuf, m_render, m_pal, u, v,
m_zoom, 255, BlendMode::NORMAL);
255, BlendMode::NORMAL);
break;
}
@ -242,7 +245,7 @@ private:
gfx::Point m_pos;
gfx::Point m_oldMousePos;
gfx::Point m_delta;
render::Zoom m_zoom;
render::Projection m_proj;
int m_index_bg_color;
base::UniquePtr<Image> m_render;
base::UniquePtr<Image> m_doublebuf;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -15,104 +15,98 @@
#include "app/modules/gui.h"
#include "app/ui/editor/editor.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace app {
class GotoCommand : public Command {
class GotoLayerCommand : public Command {
public:
GotoCommand(const char* short_name, const char* friendly_name, CommandFlags flags)
: Command(short_name, friendly_name, flags) {
GotoLayerCommand(int offset,
const char* shortName,
const char* friendlyName,
CommandFlags flags)
: Command(shortName, friendlyName, flags),
m_offset(offset) {
}
protected:
bool onEnabled(Context* context) override {
return (current_editor &&
current_editor->document());
}
void onExecute(Context* context) override {
Site site = current_editor->getSite();
Layer* layer = site.layer();
if (!layer)
return;
if (m_offset > 0) {
int i = m_offset;
while (i-- > 0) {
layer = layer->getNextBrowsable();
if (!layer)
layer = site.sprite()->firstBrowsableLayer();
}
}
else if (m_offset < 0) {
int i = m_offset;
while (i++ < 0) {
layer = layer->getPreviousBrowsable();
if (!layer)
layer = site.sprite()->root()->lastLayer();
}
}
site.layer(layer);
// Flash the current layer
current_editor->setLayer(site.layer());
current_editor->flashCurrentLayer();
updateStatusBar(site);
}
void updateStatusBar(Site& site) {
if (site.layer() != NULL)
StatusBar::instance()
->setStatusText(1000, "Layer `%s' selected",
site.layer()->name().c_str());
StatusBar::instance()->setStatusText(
1000, "%s '%s' selected",
(site.layer()->isGroup() ? "Group": "Layer"),
site.layer()->name().c_str());
}
private:
int m_offset;
};
class GotoPreviousLayerCommand : public GotoLayerCommand {
public:
GotoPreviousLayerCommand()
: GotoLayerCommand(-1, "GotoPreviousLayer",
"Go to Previous Layer",
CmdUIOnlyFlag) {
}
Command* clone() const override {
return new GotoPreviousLayerCommand(*this);
}
};
class GotoPreviousLayerCommand : public GotoCommand {
class GotoNextLayerCommand : public GotoLayerCommand {
public:
GotoPreviousLayerCommand();
Command* clone() const override { return new GotoPreviousLayerCommand(*this); }
protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
GotoNextLayerCommand()
: GotoLayerCommand(+1, "GotoNextLayer",
"Go to Next Layer",
CmdUIOnlyFlag) {
}
Command* clone() const override {
return new GotoNextLayerCommand(*this);
}
};
GotoPreviousLayerCommand::GotoPreviousLayerCommand()
: GotoCommand("GotoPreviousLayer",
"Go to Previous Layer",
CmdUIOnlyFlag)
{
}
bool GotoPreviousLayerCommand::onEnabled(Context* context)
{
return (current_editor != nullptr &&
current_editor->document());
}
void GotoPreviousLayerCommand::onExecute(Context* context)
{
Site site = current_editor->getSite();
if (site.layerIndex() > 0)
site.layerIndex(site.layerIndex().previous());
else
site.layerIndex(LayerIndex(site.sprite()->countLayers()-1));
// Flash the current layer
current_editor->setLayer(site.layer());
current_editor->flashCurrentLayer();
updateStatusBar(site);
}
class GotoNextLayerCommand : public GotoCommand {
public:
GotoNextLayerCommand();
Command* clone() const override { return new GotoNextLayerCommand(*this); }
protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
};
GotoNextLayerCommand::GotoNextLayerCommand()
: GotoCommand("GotoNextLayer",
"Go to Next Layer",
CmdUIOnlyFlag)
{
}
bool GotoNextLayerCommand::onEnabled(Context* context)
{
return (current_editor != nullptr &&
current_editor->document());
}
void GotoNextLayerCommand::onExecute(Context* context)
{
Site site = current_editor->getSite();
if (site.layerIndex() < site.sprite()->countLayers()-1)
site.layerIndex(site.layerIndex().next());
else
site.layerIndex(LayerIndex(0));
// Flash the current layer
current_editor->setLayer(site.layer());
current_editor->flashCurrentLayer();
updateStatusBar(site);
}
Command* CommandFactory::createGotoPreviousLayerCommand()
{
return new GotoPreviousLayerCommand;

View File

@ -393,7 +393,7 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
DocumentApi api = document->getApi(transaction);
// Add the layer in the sprite.
LayerImage* resultLayer = api.newLayer(sprite, "Sprite Sheet");
LayerImage* resultLayer = api.newLayer(sprite->root(), "Sprite Sheet");
// Add all frames+cels to the new layer
for (size_t i=0; i<animation.size(); ++i) {
@ -406,12 +406,12 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
}
// Copy the list of layers (because we will modify it in the iteration).
LayerList layers = sprite->folder()->getLayersList();
LayerList layers = sprite->root()->layers();
// Remove all other layers
for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) {
if (*it != resultLayer)
api.removeLayer(*it);
for (Layer* child : layers) {
if (child != resultLayer)
api.removeLayer(child);
}
// Change the number of frames

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -44,7 +44,9 @@ bool LayerFromBackgroundCommand::onEnabled(Context* context)
ContextFlags::ActiveLayerIsVisible |
ContextFlags::ActiveLayerIsEditable |
ContextFlags::ActiveLayerIsImage |
ContextFlags::ActiveLayerIsBackground);
ContextFlags::ActiveLayerIsBackground) &&
// Isn't a reference layer
!context->checkFlags(ContextFlags::ActiveLayerIsReference);
}
void LayerFromBackgroundCommand::onExecute(Context* context)

View File

@ -71,20 +71,19 @@ void LayerOpacityCommand::onExecute(Context* context)
Transaction transaction(writer.context(), "Set Layer Opacity");
// TODO the range of selected frames should be in doc::Site.
SelectedLayers selLayers;
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) {
Layer* layer = writer.sprite()->indexToLayer(layerIdx);
if (!layer->isImage())
continue;
transaction.execute(
new cmd::SetLayerOpacity(static_cast<LayerImage*>(layer), m_opacity));
}
selLayers = range.selectedLayers();
}
else {
transaction.execute(
new cmd::SetLayerOpacity(static_cast<LayerImage*>(writer.layer()), m_opacity));
selLayers.insert(writer.layer());
}
for (auto layer : selLayers) {
if (layer->isImage())
transaction.execute(
new cmd::SetLayerOpacity(static_cast<LayerImage*>(layer), m_opacity));
}
transaction.commit();

View File

@ -95,14 +95,14 @@ public:
UIContext::instance()->remove_observer(this);
}
void setLayer(LayerImage* layer) {
void setLayer(Layer* layer) {
if (m_layer) {
document()->remove_observer(this);
m_layer = nullptr;
}
m_timer.stop();
m_layer = const_cast<LayerImage*>(layer);
m_layer = layer;
if (m_layer)
document()->add_observer(this);
@ -183,9 +183,10 @@ private:
BlendMode newBlendMode = blendModeValue();
if (newName != m_layer->name() ||
newOpacity != m_layer->opacity() ||
newBlendMode != m_layer->blendMode() ||
m_userData != m_layer->userData()) {
m_userData != m_layer->userData() ||
(m_layer->isImage() &&
(newOpacity != static_cast<LayerImage*>(m_layer)->opacity() ||
newBlendMode != static_cast<LayerImage*>(m_layer)->blendMode()))) {
try {
ContextWriter writer(UIContext::instance());
Transaction transaction(writer.context(), "Set Layer Properties");
@ -193,20 +194,21 @@ private:
if (newName != m_layer->name())
transaction.execute(new cmd::SetLayerName(writer.layer(), newName));
if (newOpacity != m_layer->opacity())
transaction.execute(new cmd::SetLayerOpacity(static_cast<LayerImage*>(writer.layer()), newOpacity));
if (newBlendMode != m_layer->blendMode())
transaction.execute(new cmd::SetLayerBlendMode(static_cast<LayerImage*>(writer.layer()), newBlendMode));
if (m_userData != m_layer->userData()) {
if (m_userData != m_layer->userData())
transaction.execute(new cmd::SetUserData(writer.layer(), m_userData));
// Redraw timeline because the layer's user data/color
// might have changed.
App::instance()->timeline()->invalidate();
if (m_layer->isImage()) {
if (newOpacity != static_cast<LayerImage*>(m_layer)->opacity())
transaction.execute(new cmd::SetLayerOpacity(static_cast<LayerImage*>(writer.layer()), newOpacity));
if (newBlendMode != static_cast<LayerImage*>(m_layer)->blendMode())
transaction.execute(new cmd::SetLayerBlendMode(static_cast<LayerImage*>(writer.layer()), newBlendMode));
}
// Redraw timeline because the layer's name/user data/color
// might have changed.
App::instance()->timeline()->invalidate();
transaction.commit();
}
catch (const std::exception& e) {
@ -220,7 +222,7 @@ private:
// ContextObserver impl
void onActiveSiteChange(const Site& site) override {
if (isVisible())
setLayer(dynamic_cast<LayerImage*>(const_cast<Layer*>(site.layer())));
setLayer(const_cast<Layer*>(site.layer()));
else if (m_layer)
setLayer(nullptr);
}
@ -261,10 +263,18 @@ private:
if (m_layer) {
name()->setText(m_layer->name().c_str());
name()->setEnabled(true);
mode()->setSelectedItemIndex((int)m_layer->blendMode());
mode()->setEnabled(!m_layer->isBackground());
opacity()->setValue(m_layer->opacity());
opacity()->setEnabled(!m_layer->isBackground());
if (m_layer->isImage()) {
mode()->setSelectedItemIndex((int)static_cast<LayerImage*>(m_layer)->blendMode());
mode()->setEnabled(!m_layer->isBackground());
opacity()->setValue(static_cast<LayerImage*>(m_layer)->opacity());
opacity()->setEnabled(!m_layer->isBackground());
}
else {
mode()->setEnabled(false);
opacity()->setEnabled(false);
}
m_userData = m_layer->userData();
}
else {
@ -277,7 +287,7 @@ private:
}
Timer m_timer;
LayerImage* m_layer;
Layer* m_layer;
bool m_selfUpdate;
UserData m_userData;
};

View File

@ -15,7 +15,6 @@
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
@ -42,9 +41,9 @@ LinkCelsCommand::LinkCelsCommand()
bool LinkCelsCommand::onEnabled(Context* context)
{
if (context->checkFlags(ContextFlags::ActiveDocumentIsWritable)) {
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
return (range.enabled() && range.frames() > 1);
auto site = context->activeSite();
return (site.inTimeline() &&
site.selectedFrames().size() > 1);
}
else
return false;
@ -56,36 +55,33 @@ void LinkCelsCommand::onExecute(Context* context)
Document* document(writer.document());
bool nonEditableLayers = false;
{
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (!range.enabled())
auto site = context->activeSite();
if (!site.inTimeline())
return;
Transaction transaction(writer.context(), friendlyName());
Sprite* sprite = writer.sprite();
frame_t begin = range.frameBegin();
frame_t end = range.frameEnd();
for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) {
Layer* layer = sprite->indexToLayer(layerIdx);
for (Layer* layer : site.selectedLayers()) {
if (!layer->isImage())
continue;
if (!layer->isEditable()) {
if (!layer->isEditableHierarchy()) {
nonEditableLayers = true;
continue;
}
LayerImage* layerImage = static_cast<LayerImage*>(layer);
for (frame_t frame=begin; frame < end+1; ++frame) {
for (auto it=site.selectedFrames().begin(), end=site.selectedFrames().end();
it != end; ++it) {
frame_t frame = *it;
Cel* cel = layerImage->cel(frame);
if (cel) {
for (frame = cel->frame()+1;
frame < end+1; ++frame) {
for (++it; it != end; ++it) {
transaction.execute(
new cmd::CopyCel(
layerImage, cel->frame(),
layerImage, frame,
layerImage, *it,
true)); // true = force links
}
break;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -35,6 +35,7 @@ protected:
private:
std::string m_preset;
std::string m_filename;
};
LoadPaletteCommand::LoadPaletteCommand()
@ -47,6 +48,7 @@ LoadPaletteCommand::LoadPaletteCommand()
void LoadPaletteCommand::onLoadParams(const Params& params)
{
m_preset = params.get("preset");
m_filename = params.get("filename");
}
void LoadPaletteCommand::onExecute(Context* context)
@ -58,24 +60,30 @@ void LoadPaletteCommand::onExecute(Context* context)
if (!base::is_file(filename))
filename = get_preset_palette_filename(m_preset, ".gpl");
}
else if (!m_filename.empty()) {
filename = m_filename;
}
else {
std::string exts = get_readable_palette_extensions();
filename = app::show_file_selector("Load Palette", "", exts,
FileSelectorType::Open);
}
if (!filename.empty()) {
base::UniquePtr<doc::Palette> palette(load_palette(filename.c_str()));
if (!palette) {
// Do nothing
if (filename.empty())
return;
base::UniquePtr<doc::Palette> palette(load_palette(filename.c_str()));
if (!palette) {
if (context->isUIAvailable())
Alert::show("Error<<Loading palette file||&Close");
}
else {
SetPaletteCommand* cmd = static_cast<SetPaletteCommand*>(
CommandsModule::instance()->getCommandByName(CommandId::SetPalette));
cmd->setPalette(palette);
context->executeCommand(cmd);
}
return;
}
SetPaletteCommand* cmd = static_cast<SetPaletteCommand*>(
CommandsModule::instance()->getCommandByName(CommandId::SetPalette));
cmd->setPalette(palette);
context->executeCommand(cmd);
}
Command* CommandFactory::createLoadPaletteCommand()

View File

@ -14,9 +14,11 @@
#include "app/color_utils.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/tools/tool_box.h"
#include "app/transaction.h"
#include "app/ui/editor/editor.h"
#include "app/ui/toolbar.h"
#include "doc/algorithm/shrink_bounds.h"
#include "doc/cel.h"
@ -64,7 +66,10 @@ void MaskContentCommand::onExecute(Context* context)
gfx::Color color;
if (writer.layer()->isBackground()) {
ColorPicker picker;
picker.pickColor(*writer.site(), gfx::Point(0, 0), ColorPicker::FromComposition);
picker.pickColor(*writer.site(),
gfx::PointF(0.0, 0.0),
current_editor->projection(),
ColorPicker::FromComposition);
color = color_utils::color_for_layer(picker.color(), writer.layer());
}
else

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -21,6 +21,8 @@
#include "app/ui/workspace.h"
#include "app/ui_context.h"
#include "app/util/clipboard.h"
#include "app/util/pixel_ratio.h"
#include "base/bind.h"
#include "base/unique_ptr.h"
#include "doc/cel.h"
#include "doc/image.h"
@ -101,6 +103,26 @@ void NewFileCommand::onExecute(Context* context)
// Select background color
window.bgColor()->setSelectedItem(bg);
// Advance options
bool advanced = pref.newFile.advanced();
window.advancedCheck()->setSelected(advanced);
window.advancedCheck()->Click.connect(
base::Bind<void>(
[&]{
gfx::Rect bounds = window.bounds();
window.advanced()->setVisible(window.advancedCheck()->isSelected());
window.setBounds(gfx::Rect(window.bounds().origin(),
window.sizeHint()));
window.layout();
window.manager()->invalidateRect(bounds);
}));
window.advanced()->setVisible(advanced);
if (advanced)
window.pixelRatio()->setValue(pref.newFile.pixelRatio());
else
window.pixelRatio()->setValue("1:1");
// Open the window
window.openWindowInForeground();
@ -135,6 +157,8 @@ void NewFileCommand::onExecute(Context* context)
pref.newFile.height(h);
pref.newFile.colorMode(format);
pref.newFile.backgroundColor(bg);
pref.newFile.advanced(window.advancedCheck()->isSelected());
pref.newFile.pixelRatio(window.pixelRatio()->getValue());
// Create the new sprite
ASSERT(format == IMAGE_RGB || format == IMAGE_GRAYSCALE || format == IMAGE_INDEXED);
@ -142,13 +166,18 @@ void NewFileCommand::onExecute(Context* context)
base::UniquePtr<Sprite> sprite(Sprite::createBasicSprite(format, w, h, ncolors));
if (window.advancedCheck()->isSelected()) {
sprite->setPixelRatio(
base::convert_to<PixelRatio>(window.pixelRatio()->getValue()));
}
if (sprite->pixelFormat() != IMAGE_GRAYSCALE)
get_default_palette()->copyColorsTo(sprite->palette(frame_t(0)));
// If the background color isn't transparent, we have to
// convert the `Layer 1' in a `Background'
if (color.getType() != app::Color::MaskType) {
Layer* layer = sprite->folder()->getFirstLayer();
Layer* layer = sprite->root()->firstLayer();
if (layer && layer->isImage()) {
LayerImage* layerImage = static_cast<LayerImage*>(layer);

View File

@ -105,34 +105,37 @@ void NewFrameCommand::onExecute(Context* context)
case Content::DUPLICATE_CELS:
case Content::DUPLICATE_CELS_BLOCK: {
// TODO the range of selected frames should be in doc::Site.
Timeline* timeline = App::instance()->timeline();
Timeline::Range range = timeline->range();
if (range.enabled()) {
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty() &&
!site->selectedFrames().empty()) {
std::map<CelData*, Cel*> relatedCels;
auto timeline = App::instance()->timeline();
timeline->prepareToMoveRange();
DocumentRange range = timeline->range();
LayerIndex layerBegin = range.layerBegin();
LayerIndex layerEnd = range.layerEnd();
SelectedLayers selLayers;
if (site->inFrames())
selLayers.selectAllLayers(writer.sprite()->root());
else
selLayers = site->selectedLayers();
if (range.type() == DocumentRange::kFrames) {
layerBegin = writer.sprite()->firstLayer();
layerEnd = writer.sprite()->lastLayer();
}
frame_t frameRange =
(site->selectedFrames().lastFrame() -
site->selectedFrames().firstFrame() + 1);
for (LayerIndex layer = layerBegin; layer <= layerEnd; ++layer) {
Layer* layerPtr = writer.sprite()->indexToLayer(layer);
if (layerPtr->isImage()) {
for (frame_t frame = range.frameEnd(); frame >= range.frameBegin(); --frame) {
frame_t srcFrame = frame;
frame_t dstFrame = frame+range.frames();
for (Layer* layer : selLayers) {
if (layer->isImage()) {
for (frame_t srcFrame : site->selectedFrames().reversed()) {
frame_t dstFrame = srcFrame+frameRange;
bool continuous;
CelData* srcCelData = nullptr;
if (m_content == Content::DUPLICATE_CELS_BLOCK) {
continuous = false;
Cel* srcCel = static_cast<LayerImage*>(layerPtr)->cel(srcFrame);
Cel* srcCel = static_cast<LayerImage*>(layer)->cel(srcFrame);
if (srcCel) {
srcCelData = srcCel->data();
@ -144,19 +147,19 @@ void NewFrameCommand::onExecute(Context* context)
}
}
else
continuous = layerPtr->isContinuous();
continuous = layer->isContinuous();
api.copyCel(
static_cast<LayerImage*>(layerPtr), srcFrame,
static_cast<LayerImage*>(layerPtr), dstFrame, continuous);
static_cast<LayerImage*>(layer), srcFrame,
static_cast<LayerImage*>(layer), dstFrame, continuous);
if (srcCelData && !relatedCels[srcCelData])
relatedCels[srcCelData] = layerPtr->cel(dstFrame);
relatedCels[srcCelData] = layer->cel(dstFrame);
}
}
}
range.displace(0, range.frames());
range.displace(0, frameRange);
timeline->moveRange(range);
}
else {

View File

@ -58,8 +58,8 @@ void NewFrameTagCommand::onExecute(Context* context)
if (range.enabled() &&
(range.type() == DocumentRange::kFrames ||
range.type() == DocumentRange::kCels)) {
from = range.frameBegin();
to = range.frameEnd();
from = range.selectedFrames().firstFrame();
to = range.selectedFrames().lastFrame();
}
base::UniquePtr<FrameTag> frameTag(new FrameTag(from, to));

View File

@ -9,7 +9,9 @@
#endif
#include "app/app.h"
#include "app/cmd/move_layer.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/context_access.h"
#include "app/document_api.h"
@ -19,10 +21,16 @@
#include "app/transaction.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "doc/layer.h"
#include "doc/primitives.h"
#include "doc/sprite.h"
#include "render/quantization.h"
#include "render/render.h"
#include "ui/ui.h"
#include "new_layer.xml.h"
#include <cstdio>
#include <cstring>
@ -32,6 +40,9 @@ using namespace ui;
class NewLayerCommand : public Command {
public:
enum class Type { Layer, Group, ReferenceLayer };
enum class Place { AfterActiveLayer, Top };
NewLayerCommand();
Command* clone() const override { return new NewLayerCommand(*this); }
@ -41,29 +52,42 @@ protected:
void onExecute(Context* context) override;
private:
bool m_ask;
bool m_top;
std::string m_name;
};
std::string getUniqueLayerName(const Sprite* sprite) const;
int getMaxLayerNum(const Layer* layer) const;
const char* layerPrefix() const;
static std::string get_unique_layer_name(Sprite* sprite);
static int get_max_layer_num(Layer* layer);
std::string m_name;
Type m_type;
Place m_place;
bool m_ask;
bool m_fromFile;
};
NewLayerCommand::NewLayerCommand()
: Command("NewLayer",
"New Layer",
CmdRecordableFlag)
{
m_ask = false;
m_top = false;
m_name = "";
m_type = Type::Layer;
m_place = Place::AfterActiveLayer;
m_ask = false;
}
void NewLayerCommand::onLoadParams(const Params& params)
{
m_ask = (params.get("ask") == "true");
m_top = (params.get("top") == "true");
m_name = params.get("name");
m_type = Type::Layer;
if (params.get("group") == "true")
m_type = Type::Group;
else if (params.get("reference") == "true")
m_type = Type::ReferenceLayer;
m_ask = (params.get("ask") == "true");
m_fromFile = (params.get("from-file") == "true");
m_place = Place::AfterActiveLayer;
if (params.get("top") == "true")
m_place = Place::Top;
}
bool NewLayerCommand::onEnabled(Context* context)
@ -72,6 +96,16 @@ bool NewLayerCommand::onEnabled(Context* context)
ContextFlags::HasActiveSprite);
}
namespace {
class Scoped { // TODO move this to base library
public:
Scoped(const std::function<void()>& func) : m_func(func) { }
~Scoped() { m_func(); }
private:
std::function<void()> m_func;
};
}
void NewLayerCommand::onExecute(Context* context)
{
ContextWriter writer(context);
@ -79,70 +113,234 @@ void NewLayerCommand::onExecute(Context* context)
Sprite* sprite(writer.sprite());
std::string name;
app::Document* pasteDoc = nullptr;
Scoped destroyPasteDoc(
[&pasteDoc, context]{
if (pasteDoc) {
context->documents().remove(pasteDoc);
delete pasteDoc;
}
});
// Default name (m_name is a name specified in params)
if (!m_name.empty())
name = m_name;
else
name = get_unique_layer_name(sprite);
name = getUniqueLayerName(sprite);
// Select a file to copy its content
if (m_fromFile) {
Document* oldActiveDocument = context->activeDocument();
Command* openFile = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
Params params;
params.set("filename", "");
context->executeCommand(openFile, params);
// The user have selected another document.
if (oldActiveDocument != context->activeDocument()) {
pasteDoc = context->activeDocument();
static_cast<UIContext*>(context)
->setActiveDocument(oldActiveDocument);
}
}
// If params specify to ask the user about the name...
if (m_ask) {
// We open the window to ask the name
base::UniquePtr<Window> window(app::load_widget<Window>("new_layer.xml", "new_layer"));
Widget* name_widget = app::find_widget<Widget>(window, "name");
name_widget->setText(name.c_str());
name_widget->setMinSize(gfx::Size(128, 0));
window->openWindowInForeground();
if (window->closer() != window->findChild("ok"))
app::gen::NewLayer window;
window.name()->setText(name.c_str());
window.name()->setMinSize(gfx::Size(128, 0));
window.openWindowInForeground();
if (window.closer() != window.ok())
return;
name = window->findChild("name")->text();
name = window.name()->text();
}
LayerGroup* parent = sprite->root();
Layer* activeLayer = writer.layer();
SelectedLayers selLayers = writer.site()->selectedLayers();
if (activeLayer) {
if (activeLayer->isGroup() &&
activeLayer->isExpanded() &&
m_type != Type::Group) {
parent = static_cast<LayerGroup*>(activeLayer);
activeLayer = nullptr;
}
else {
parent = activeLayer->parent();
}
}
Layer* layer;
{
Transaction transaction(writer.context(), "New Layer");
Transaction transaction(
writer.context(),
std::string("New ") + layerPrefix());
DocumentApi api = document->getApi(transaction);
layer = api.newLayer(sprite, name);
bool afterBackground = false;
// If "top" parameter is false, create the layer above the active
// one.
if (activeLayer && !m_top)
api.restackLayerAfter(layer, activeLayer);
switch (m_type) {
case Type::Layer:
layer = api.newLayer(parent, name);
break;
case Type::Group:
layer = api.newGroup(parent, name);
break;
case Type::ReferenceLayer:
layer = api.newLayer(parent, name);
if (layer)
layer->setReference(true);
afterBackground = true;
break;
}
ASSERT(layer);
if (!layer)
return;
ASSERT(layer->parent());
// Put new layer as an overlay of the background or in the first
// layer in case the sprite is transparent.
if (afterBackground) {
Layer* first = sprite->root()->firstLayer();
if (first) {
if (first->isBackground())
api.restackLayerAfter(layer, sprite->root(), first);
else
api.restackLayerBefore(layer, sprite->root(), first);
}
}
// Move the layer above the active one.
else if (activeLayer && m_place == Place::AfterActiveLayer) {
api.restackLayerAfter(layer,
activeLayer->parent(),
activeLayer);
}
// Put all selected layers inside the group
if (m_type == Type::Group && writer.site()->inTimeline()) {
LayerGroup* commonParent = nullptr;
layer_t sameParents = 0;
for (Layer* l : selLayers) {
if (!commonParent ||
commonParent == l->parent()) {
commonParent = l->parent();
++sameParents;
}
}
if (sameParents == selLayers.size()) {
for (Layer* newChild : selLayers.toLayerList()) {
transaction.execute(
new cmd::MoveLayer(newChild, layer,
static_cast<LayerGroup*>(layer)->lastLayer()));
}
}
}
// Paste sprite content
if (pasteDoc && layer->isImage()) {
Sprite* pasteSpr = pasteDoc->sprite();
render::Render render;
render.setBgType(render::BgType::NONE);
// Add more frames at the end
if (writer.frame()+pasteSpr->lastFrame() > sprite->lastFrame())
api.addEmptyFramesTo(sprite, writer.frame()+pasteSpr->lastFrame());
// Paste the given sprite as flatten
for (frame_t fr=0; fr<=pasteSpr->lastFrame(); ++fr) {
ImageRef pasteImage(
Image::create(
pasteSpr->pixelFormat(),
pasteSpr->width(),
pasteSpr->height()));
clear_image(pasteImage.get(),
pasteSpr->transparentColor());
render.renderSprite(pasteImage.get(), pasteSpr, fr);
frame_t dstFrame = writer.frame()+fr;
if (sprite->pixelFormat() != pasteSpr->pixelFormat() ||
sprite->pixelFormat() == IMAGE_INDEXED) {
ImageRef pasteImageConv(
render::convert_pixel_format(
pasteImage.get(),
nullptr,
sprite->pixelFormat(),
DitheringMethod::NONE,
sprite->rgbMap(dstFrame),
pasteSpr->palette(fr),
(pasteSpr->backgroundLayer() ? true: false),
sprite->transparentColor()));
if (pasteImageConv)
pasteImage = pasteImageConv;
}
Cel* cel = layer->cel(dstFrame);
if (cel) {
api.replaceImage(sprite, cel->imageRef(), pasteImage);
}
else {
cel = api.addCel(static_cast<LayerImage*>(layer),
dstFrame, pasteImage);
}
if (cel) {
if (layer->isReference()) {
gfx::RectF bounds(0, 0, pasteSpr->width(), pasteSpr->height());
double scale = MIN(double(sprite->width()) / bounds.w,
double(sprite->height()) / bounds.h);
bounds.w *= scale;
bounds.h *= scale;
bounds.x = sprite->width()/2 - bounds.w/2;
bounds.y = sprite->height()/2 - bounds.h/2;
cel->setBoundsF(bounds);
}
else {
cel->setPosition(sprite->width()/2 - pasteSpr->width()/2,
sprite->height()/2 - pasteSpr->height()/2);
}
}
}
}
transaction.commit();
}
update_screen_for_document(document);
StatusBar::instance()->invalidate();
StatusBar::instance()->showTip(1000, "Layer `%s' created", name.c_str());
StatusBar::instance()->showTip(
1000, "%s '%s' created",
layerPrefix(),
name.c_str());
App::instance()->mainWindow()->popTimeline();
}
static std::string get_unique_layer_name(Sprite* sprite)
std::string NewLayerCommand::getUniqueLayerName(const Sprite* sprite) const
{
char buf[1024];
std::sprintf(buf, "Layer %d", get_max_layer_num(sprite->folder())+1);
std::sprintf(buf, "%s %d",
layerPrefix(),
getMaxLayerNum(sprite->root())+1);
return buf;
}
static int get_max_layer_num(Layer* layer)
int NewLayerCommand::getMaxLayerNum(const Layer* layer) const
{
std::string prefix = layerPrefix();
prefix += " ";
int max = 0;
if (std::strncmp(layer->name().c_str(), prefix.c_str(), prefix.size()) == 0)
max = std::strtol(layer->name().c_str()+prefix.size(), NULL, 10);
if (std::strncmp(layer->name().c_str(), "Layer ", 6) == 0)
max = std::strtol(layer->name().c_str()+6, NULL, 10);
if (layer->isFolder()) {
LayerIterator it = static_cast<LayerFolder*>(layer)->getLayerBegin();
LayerIterator end = static_cast<LayerFolder*>(layer)->getLayerEnd();
for (; it != end; ++it) {
int tmp = get_max_layer_num(*it);
if (layer->isGroup()) {
for (const Layer* child : static_cast<const LayerGroup*>(layer)->layers()) {
int tmp = getMaxLayerNum(child);
max = MAX(tmp, max);
}
}
@ -150,6 +348,16 @@ static int get_max_layer_num(Layer* layer)
return max;
}
const char* NewLayerCommand::layerPrefix() const
{
switch (m_type) {
case Type::Layer: return "Layer";
case Type::Group: return "Group";
case Type::ReferenceLayer: return "Reference Layer";
}
return "Unknown";
}
Command* CommandFactory::createNewLayerCommand()
{
return new NewLayerCommand;

View File

@ -1,86 +0,0 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/app.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/document_api.h"
#include "app/document_api.h"
#include "app/find_widget.h"
#include "app/load_widget.h"
#include "app/modules/gui.h"
#include "app/ui/status_bar.h"
#include "app/transaction.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include "ui/ui.h"
namespace app {
using namespace ui;
class NewLayerSetCommand : public Command {
public:
NewLayerSetCommand();
Command* clone() const override { return new NewLayerSetCommand(*this); }
protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
};
NewLayerSetCommand::NewLayerSetCommand()
: Command("NewLayerSet",
"New Layer Set",
CmdRecordableFlag)
{
}
bool NewLayerSetCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveSprite);
}
void NewLayerSetCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Document* document(writer.document());
Sprite* sprite(writer.sprite());
// load the window widget
base::UniquePtr<Window> window(app::load_widget<Window>("new_layer.xml", "new_layer_set"));
window->openWindowInForeground();
if (window->closer() != window->findChild("ok"))
return;
std::string name = window->findChild("name")->text();
Layer* layer;
{
Transaction transaction(writer.context(), "New Layer");
layer = document->getApi(transaction).newLayerFolder(sprite);
transaction.commit();
}
layer->setName(name);
update_screen_for_document(document);
StatusBar::instance()->invalidate();
StatusBar::instance()->showTip(1000, "Layer `%s' created", name.c_str());
}
Command* CommandFactory::createNewLayerSetCommand()
{
return new NewLayerSetCommand;
}
} // namespace app

View File

@ -71,7 +71,7 @@ void NewSpriteFromSelectionCommand::onExecute(Context* context)
palette->copyColorsTo(dstSprite->palette(frame_t(0)));
LayerImage* dstLayer = static_cast<LayerImage*>(dstSprite->folder()->getFirstLayer());
LayerImage* dstLayer = static_cast<LayerImage*>(dstSprite->root()->firstLayer());
if (site.layer()->isBackground())
dstLayer->configureAsBackground(); // Configure layer name as background
dstLayer->setFlags(site.layer()->flags()); // Copy all flags

View File

@ -79,6 +79,7 @@ OpenFileCommand::OpenFileCommand()
"Open Sprite",
CmdRecordableFlag)
, m_repeatCheckbox(false)
, m_oneFrame(false)
, m_seqDecision(SequenceDecision::Ask)
{
}
@ -88,6 +89,15 @@ void OpenFileCommand::onLoadParams(const Params& params)
m_filename = params.get("filename");
m_folder = params.get("folder"); // Initial folder
m_repeatCheckbox = (params.get("repeat_checkbox") == "true");
m_oneFrame = (params.get("oneframe") == "true");
std::string sequence = params.get("sequence");
if (m_oneFrame || sequence == "skip")
m_seqDecision = SequenceDecision::Skip;
else if (sequence == "agree")
m_seqDecision = SequenceDecision::Agree;
else
m_seqDecision = SequenceDecision::Ask;
}
void OpenFileCommand::onExecute(Context* context)
@ -106,81 +116,86 @@ void OpenFileCommand::onExecute(Context* context)
m_folder.push_back(base::path_separator);
m_filename = app::show_file_selector("Open", m_folder, exts,
FileSelectorType::Open);
FileSelectorType::Open);
}
if (!m_filename.empty()) {
int flags = (m_repeatCheckbox ? FILE_LOAD_SEQUENCE_ASK_CHECKBOX: 0);
// The user cancelled the operation through UI or isn't a filename
// specified in params.
if (m_filename.empty())
return;
switch (m_seqDecision) {
case SequenceDecision::Ask:
flags |= FILE_LOAD_SEQUENCE_ASK;
break;
case SequenceDecision::Agree:
flags |= FILE_LOAD_SEQUENCE_YES;
break;
case SequenceDecision::Skip:
flags |= FILE_LOAD_SEQUENCE_NONE;
break;
}
int flags = (m_repeatCheckbox ? FILE_LOAD_SEQUENCE_ASK_CHECKBOX: 0);
base::UniquePtr<FileOp> fop(
FileOp::createLoadDocumentOperation(
context, m_filename.c_str(), flags));
bool unrecent = false;
switch (m_seqDecision) {
case SequenceDecision::Ask:
flags |= FILE_LOAD_SEQUENCE_ASK;
break;
case SequenceDecision::Agree:
flags |= FILE_LOAD_SEQUENCE_YES;
break;
case SequenceDecision::Skip:
flags |= FILE_LOAD_SEQUENCE_NONE;
break;
}
if (fop) {
if (fop->hasError()) {
console.printf(fop->error().c_str());
unrecent = true;
if (m_oneFrame)
flags |= FILE_LOAD_ONE_FRAME;
base::UniquePtr<FileOp> fop(
FileOp::createLoadDocumentOperation(
context, m_filename.c_str(), flags));
bool unrecent = false;
// Do nothing (the user cancelled or something like that)
if (!fop)
return;
if (fop->hasError()) {
console.printf(fop->error().c_str());
unrecent = true;
}
else {
if (fop->isSequence()) {
if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_YES) {
m_seqDecision = SequenceDecision::Agree;
}
else {
if (fop->isSequence()) {
if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_YES) {
m_seqDecision = SequenceDecision::Agree;
}
else if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_NONE) {
m_seqDecision = SequenceDecision::Skip;
}
m_usedFiles = fop->filenames();
}
else {
m_usedFiles.push_back(fop->filename());
}
OpenFileJob task(fop);
task.showProgressWindow();
// Post-load processing, it is called from the GUI because may require user intervention.
fop->postLoad();
// Show any error
if (fop->hasError() && !fop->isStop())
console.printf(fop->error().c_str());
Document* document = fop->document();
if (document) {
if (context->isUIAvailable())
App::instance()->recentFiles()->addRecentFile(fop->filename().c_str());
document->setContext(context);
}
else if (!fop->isStop())
unrecent = true;
else if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_NONE) {
m_seqDecision = SequenceDecision::Skip;
}
// The file was not found or was loaded loaded with errors,
// so we can remove it from the recent-file list
if (unrecent) {
if (context->isUIAvailable())
App::instance()->recentFiles()->removeRecentFile(m_filename.c_str());
}
m_usedFiles = fop->filenames();
}
else {
// Do nothing (the user cancelled or something like that)
m_usedFiles.push_back(fop->filename());
}
OpenFileJob task(fop);
task.showProgressWindow();
// Post-load processing, it is called from the GUI because may require user intervention.
fop->postLoad();
// Show any error
if (fop->hasError() && !fop->isStop())
console.printf(fop->error().c_str());
Document* document = fop->document();
if (document) {
if (context->isUIAvailable())
App::instance()->recentFiles()->addRecentFile(fop->filename().c_str());
document->setContext(context);
}
else if (!fop->isStop())
unrecent = true;
}
// The file was not found or was loaded loaded with errors,
// so we can remove it from the recent-file list
if (unrecent) {
if (context->isUIAvailable())
App::instance()->recentFiles()->removeRecentFile(m_filename.c_str());
}
}

View File

@ -24,14 +24,6 @@ namespace app {
OpenFileCommand();
Command* clone() const override { return new OpenFileCommand(*this); }
SequenceDecision sequenceDecision() const {
return m_seqDecision;
}
void setSequenceDecision(SequenceDecision seqDecision) {
m_seqDecision = seqDecision;
}
const std::vector<std::string>& usedFiles() const {
return m_usedFiles;
}
@ -44,6 +36,7 @@ namespace app {
std::string m_filename;
std::string m_folder;
bool m_repeatCheckbox;
bool m_oneFrame;
std::vector<std::string> m_usedFiles;
SequenceDecision m_seqDecision;
};

View File

@ -0,0 +1,71 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/modules/gui.h"
#include "doc/layer.h"
namespace app {
using namespace ui;
class OpenGroupCommand : public Command {
public:
OpenGroupCommand();
Command* clone() const override { return new OpenGroupCommand(*this); }
protected:
bool onEnabled(Context* context) override;
bool onChecked(Context* context) override;
void onExecute(Context* context) override;
// TODO onGetFriendlyName() needs the Context so we can return "Open
// Group" or "Close Group" depending on the context
//std::string onGetFriendlyName() const override;
};
OpenGroupCommand::OpenGroupCommand()
: Command("OpenGroup",
"Open/Close Group",
CmdRecordableFlag)
{
}
bool OpenGroupCommand::onEnabled(Context* context)
{
const ContextReader reader(context);
const Layer* layer = reader.layer();
return (layer && layer->isGroup());
}
bool OpenGroupCommand::onChecked(Context* context)
{
const ContextReader reader(context);
const Layer* layer = reader.layer();
return (layer && layer->isExpanded());
}
void OpenGroupCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Layer* layer = writer.layer();
layer->setCollapsed(layer->isExpanded());
update_screen_for_document(writer.document());
}
Command* CommandFactory::createOpenGroupCommand()
{
return new OpenGroupCommand;
}
} // namespace app

View File

@ -158,6 +158,8 @@ public:
if (m_pref.experimental.flashLayer())
flashLayer()->setSelected(true);
nonactiveLayersOpacity()->setValue(m_pref.experimental.nonactiveLayersOpacity());
if (m_pref.editor.showScrollbars())
showScrollbars()->setSelected(true);
@ -332,6 +334,7 @@ public:
// Experimental features
m_pref.experimental.useNativeFileDialog(nativeFileDialog()->isSelected());
m_pref.experimental.flashLayer(flashLayer()->isSelected());
m_pref.experimental.nonactiveLayersOpacity(nonactiveLayersOpacity()->getValue());
ui::set_use_native_cursors(m_pref.cursor.useNativeCursor());
ui::set_mouse_cursor_scale(m_pref.cursor.cursorScale());

View File

@ -13,7 +13,6 @@
#include "app/context_access.h"
#include "app/document_api.h"
#include "app/modules/gui.h"
#include "app/ui/timeline.h"
#include "app/transaction.h"
#include "doc/sprite.h"
#include "ui/ui.h"
@ -54,14 +53,10 @@ void RemoveFrameCommand::onExecute(Context* context)
{
Transaction transaction(writer.context(), "Remove Frame");
DocumentApi api = document->getApi(transaction);
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
for (frame_t frame = range.frameEnd(),
begin = range.frameBegin()-1;
frame != begin;
--frame) {
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedFrames().empty()) {
for (frame_t frame : site->selectedFrames().reversed()) {
api.removeFrame(sprite, frame);
}
}

View File

@ -14,7 +14,6 @@
#include "app/document_api.h"
#include "app/modules/gui.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "app/transaction.h"
#include "doc/layer.h"
#include "doc/sprite.h"
@ -51,34 +50,43 @@ bool RemoveLayerCommand::onEnabled(Context* context)
void RemoveLayerCommand::onExecute(Context* context)
{
std::string layer_name;
std::string layerName;
ContextWriter writer(context);
Document* document(writer.document());
Sprite* sprite(writer.sprite());
Layer* layer(writer.layer());
{
Transaction transaction(writer.context(), "Remove Layer");
DocumentApi api = document->getApi(transaction);
// TODO the range of selected layer should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
if (range.layers() == sprite->countLayers()) {
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty()) {
SelectedLayers selLayers = site->selectedLayers();
selLayers.removeChildrenIfParentIsSelected();
layer_t deletedTopLevelLayers = 0;
for (Layer* layer : selLayers) {
if (layer->parent() == sprite->root())
++deletedTopLevelLayers;
}
if (deletedTopLevelLayers == sprite->root()->layersCount()) {
ui::Alert::show("Error<<You cannot delete all layers.||&OK");
return;
}
for (LayerIndex layer = range.layerEnd(); layer >= range.layerBegin(); --layer) {
api.removeLayer(sprite->indexToLayer(layer));
for (Layer* layer : selLayers) {
api.removeLayer(layer);
}
}
else {
if (sprite->countLayers() == 1) {
if (sprite->allLayersCount() == 1) {
ui::Alert::show("Error<<You cannot delete the last layer.||&OK");
return;
}
layer_name = layer->name();
Layer* layer = writer.layer();
layerName = layer->name();
api.removeLayer(layer);
}
@ -87,8 +95,8 @@ void RemoveLayerCommand::onExecute(Context* context)
update_screen_for_document(document);
StatusBar::instance()->invalidate();
if (!layer_name.empty())
StatusBar::instance()->showTip(1000, "Layer `%s' removed", layer_name.c_str());
if (!layerName.empty())
StatusBar::instance()->showTip(1000, "Layer '%s' removed", layerName.c_str());
else
StatusBar::instance()->showTip(1000, "Layers removed");
}

View File

@ -9,6 +9,7 @@
#endif
#include "app/app.h"
#include "app/cmd/set_cel_bounds.h"
#include "app/commands/cmd_rotate.h"
#include "app/commands/params.h"
#include "app/context_access.h"
@ -57,6 +58,29 @@ public:
protected:
template<typename T>
void rotate_rect(gfx::RectT<T>& newBounds) {
const gfx::RectT<T> bounds = newBounds;
switch (m_angle) {
case 180:
newBounds.x = m_sprite->width() - bounds.x - bounds.w;
newBounds.y = m_sprite->height() - bounds.y - bounds.h;
break;
case 90:
newBounds.x = m_sprite->height() - bounds.y - bounds.h;
newBounds.y = bounds.x;
newBounds.w = bounds.h;
newBounds.h = bounds.w;
break;
case -90:
newBounds.x = bounds.y;
newBounds.y = m_sprite->width() - bounds.x - bounds.w;
newBounds.w = bounds.h;
newBounds.h = bounds.w;
break;
}
}
// [working thread]
virtual void onJob()
{
@ -69,22 +93,17 @@ protected:
if (!image)
continue;
switch (m_angle) {
case 180:
api.setCelPosition(m_sprite, cel,
m_sprite->width() - cel->x() - image->width(),
m_sprite->height() - cel->y() - image->height());
break;
case 90:
api.setCelPosition(m_sprite, cel,
m_sprite->height() - cel->y() - image->height(),
cel->x());
break;
case -90:
api.setCelPosition(m_sprite, cel,
cel->y(),
m_sprite->width() - cel->x() - image->width());
break;
if (cel->layer()->isReference()) {
gfx::RectF bounds = cel->boundsF();
rotate_rect(bounds);
if (cel->boundsF() != bounds)
transaction.execute(new cmd::SetCelBoundsF(cel, bounds));
}
else {
gfx::Rect bounds = cel->bounds();
rotate_rect(bounds);
if (bounds.origin() != cel->bounds().origin())
api.setCelPosition(m_sprite, cel, bounds.x, bounds.y);
}
}

View File

@ -22,12 +22,15 @@
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/recent_files.h"
#include "app/restore_visible_layers.h"
#include "app/ui/layer_frame_comboboxes.h"
#include "app/ui/status_bar.h"
#include "base/bind.h"
#include "base/convert_to.h"
#include "base/fs.h"
#include "base/thread.h"
#include "base/unique_ptr.h"
#include "doc/frame_tag.h"
#include "doc/sprite.h"
#include "ui/ui.h"
@ -35,8 +38,16 @@ namespace app {
class SaveAsCopyDelegate : public FileSelectorDelegate {
public:
SaveAsCopyDelegate(double scale)
: m_resizeScale(scale) { }
SaveAsCopyDelegate(const Sprite* sprite,
const double scale,
const std::string& layer,
const std::string& frame,
const bool applyPixelRatio)
: m_sprite(sprite),
m_resizeScale(scale),
m_layer(layer),
m_frame(frame),
m_applyPixelRatio(applyPixelRatio) { }
bool hasResizeCombobox() override {
return true;
@ -50,8 +61,43 @@ public:
m_resizeScale = scale;
}
void fillLayersComboBox(ui::ComboBox* layers) override {
fill_layers_combobox(m_sprite, layers, m_layer);
}
void fillFramesComboBox(ui::ComboBox* frames) override {
fill_frames_combobox(m_sprite, frames, m_frame);
}
std::string getLayers() override { return m_layer; }
std::string getFrames() override { return m_frame; }
void setLayers(const std::string& layer) override {
m_layer = layer;
}
void setFrames(const std::string& frame) override {
m_frame = frame;
}
void setApplyPixelRatio(bool applyPixelRatio) override {
m_applyPixelRatio = applyPixelRatio;
}
bool applyPixelRatio() const override {
return m_applyPixelRatio;
}
doc::PixelRatio pixelRatio() override {
return m_sprite->pixelRatio();
}
private:
const Sprite* m_sprite;
double m_resizeScale;
std::string m_layer;
std::string m_frame;
bool m_applyPixelRatio;
};
class SaveFileJob : public Job, public IFileOpProgress {
@ -92,43 +138,6 @@ private:
FileOp* m_fop;
};
static void save_document_in_background(const Context* context,
const Document* document, bool mark_as_saved,
const std::string& fn_format)
{
base::UniquePtr<FileOp> fop(
FileOp::createSaveDocumentOperation(
context, document,
document->filename().c_str(), fn_format.c_str()));
if (!fop)
return;
SaveFileJob job(fop);
job.showProgressWindow();
if (fop->hasError()) {
Console console;
console.printf(fop->error().c_str());
// We don't know if the file was saved correctly or not. So mark
// it as it should be saved again.
const_cast<Document*>(document)->impossibleToBackToSavedState();
}
// If the job was cancelled, mark the document as modified.
else if (fop->isStop()) {
const_cast<Document*>(document)->impossibleToBackToSavedState();
}
else if (context->isUIAvailable()) {
App::instance()->recentFiles()->addRecentFile(document->filename().c_str());
if (mark_as_saved)
const_cast<Document*>(document)->markAsSaved();
StatusBar::instance()
->setStatusText(2000, "File %s, saved.",
document->name().c_str());
}
}
//////////////////////////////////////////////////////////////////////
SaveFileBaseCommand::SaveFileBaseCommand(const char* short_name, const char* friendly_name, CommandFlags flags)
@ -140,6 +149,19 @@ void SaveFileBaseCommand::onLoadParams(const Params& params)
{
m_filename = params.get("filename");
m_filenameFormat = params.get("filename-format");
m_frameTag = params.get("frame-tag");
if (params.has_param("from-frame") ||
params.has_param("to-frame")) {
doc::frame_t fromFrame = params.get_as<doc::frame_t>("from-frame");
doc::frame_t toFrame = params.get_as<doc::frame_t>("to-frame");
m_selFrames.insert(fromFrame, toFrame);
m_adjustFramesByFrameTag = true;
}
else {
m_selFrames.clear();
m_adjustFramesByFrameTag = false;
}
}
// Returns true if there is a current sprite to save.
@ -160,7 +182,8 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
// have to mark the file as saved.
bool saveCopyAs = (delegate != nullptr);
bool markAsSaved = (!saveCopyAs);
double scale = 1.0;
double xscale = 1.0;
double yscale = 1.0;
if (!m_filename.empty()) {
filename = m_filename;
@ -180,7 +203,7 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
filename = newfilename;
if (delegate &&
delegate->hasResizeCombobox()) {
scale = delegate->getResizeScale();
xscale = yscale = delegate->getResizeScale();
}
}
@ -195,16 +218,24 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
m_selectedFilename = filename;
}
// Pixel ratio
if (delegate &&
delegate->applyPixelRatio()) {
doc::PixelRatio pr = delegate->pixelRatio();
xscale *= pr.w;
yscale *= pr.h;
}
// Apply scale
bool undoResize = false;
if (scale != 1.0) {
if (xscale != 1.0 || yscale != 1.0) {
Command* resizeCmd = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
ASSERT(resizeCmd);
if (resizeCmd) {
int width = document->sprite()->width();
int height = document->sprite()->height();
int newWidth = int(double(width) * scale);
int newHeight = int(double(height) * scale);
int newWidth = int(double(width) * xscale);
int newHeight = int(double(height) * yscale);
if (newWidth < 1) newWidth = 1;
if (newHeight < 1) newHeight = 1;
if (width != newWidth || height != newHeight) {
@ -219,10 +250,29 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
}
}
// Save the document
save_document_in_background(
context, const_cast<Document*>(document),
markAsSaved, m_filenameFormat);
{
RestoreVisibleLayers layersVisibility;
if (delegate) {
Site site = context->activeSite();
// Selected layers to export
calculate_visible_layers(site,
delegate->getLayers(),
layersVisibility);
// Selected frames to export
SelectedFrames selFrames;
FrameTag* frameTag = calculate_selected_frames(
site, delegate->getFrames(), selFrames);
if (frameTag)
m_frameTag = frameTag->name();
m_selFrames = selFrames;
m_adjustFramesByFrameTag = false;
}
// Save the document
saveDocumentInBackground(context, const_cast<Document*>(document), markAsSaved);
}
// Undo resize
if (undoResize) {
@ -244,6 +294,46 @@ bool SaveFileBaseCommand::saveAsDialog(Context* context,
return true;
}
void SaveFileBaseCommand::saveDocumentInBackground(const Context* context,
const app::Document* document,
bool markAsSaved) const
{
base::UniquePtr<FileOp> fop(
FileOp::createSaveDocumentOperation(
context,
FileOpROI(document, m_frameTag,
m_selFrames, m_adjustFramesByFrameTag),
document->filename().c_str(),
m_filenameFormat.c_str()));
if (!fop)
return;
SaveFileJob job(fop);
job.showProgressWindow();
if (fop->hasError()) {
Console console;
console.printf(fop->error().c_str());
// We don't know if the file was saved correctly or not. So mark
// it as it should be saved again.
const_cast<Document*>(document)->impossibleToBackToSavedState();
}
// If the job was cancelled, mark the document as modified.
else if (fop->isStop()) {
const_cast<Document*>(document)->impossibleToBackToSavedState();
}
else if (context->isUIAvailable()) {
App::instance()->recentFiles()->addRecentFile(document->filename().c_str());
if (markAsSaved)
const_cast<Document*>(document)->markAsSaved();
StatusBar::instance()
->setStatusText(2000, "File %s, saved.",
document->name().c_str());
}
}
//////////////////////////////////////////////////////////////////////
class SaveFileCommand : public SaveFileBaseCommand {
@ -272,9 +362,7 @@ void SaveFileCommand::onExecute(Context* context)
ContextWriter writer(context);
Document* documentWriter = writer.document();
save_document_in_background(
context, documentWriter, true,
m_filenameFormat.c_str());
saveDocumentInBackground(context, documentWriter, true);
}
// If the document isn't associated to a file, we must to show the
// save-as dialog to the user to select for first time the file-name
@ -324,7 +412,17 @@ void SaveFileCopyAsCommand::onExecute(Context* context)
// show "Save As" dialog
DocumentPreferences& docPref = Preferences::instance().document(document);
SaveAsCopyDelegate delegate(docPref.saveCopy.resizeScale());
base::UniquePtr<SaveAsCopyDelegate> delegate;
if (context->isUIAvailable()) {
delegate.reset(
new SaveAsCopyDelegate(
document->sprite(),
docPref.saveCopy.resizeScale(),
docPref.saveCopy.layer(),
docPref.saveCopy.frameTag(),
docPref.saveCopy.applyPixelRatio()));
}
// Is a default output filename in the preferences?
if (!docPref.saveCopy.filename().empty()) {
@ -333,9 +431,14 @@ void SaveFileCopyAsCommand::onExecute(Context* context)
docPref.saveCopy.filename());
}
if (saveAsDialog(context, "Save Copy As", &delegate)) {
if (saveAsDialog(context, "Save Copy As", delegate)) {
docPref.saveCopy.filename(document->filename());
docPref.saveCopy.resizeScale(delegate.getResizeScale());
if (delegate) {
docPref.saveCopy.resizeScale(delegate->getResizeScale());
docPref.saveCopy.layer(delegate->getLayers());
docPref.saveCopy.frameTag(delegate->getFrames());
docPref.saveCopy.applyPixelRatio(delegate->applyPixelRatio());
}
}
// Restore the file name.

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -9,10 +9,12 @@
#pragma once
#include "app/commands/command.h"
#include "doc/selected_frames.h"
#include <string>
namespace app {
class Document;
class FileSelectorDelegate;
class SaveFileBaseCommand : public Command {
@ -29,10 +31,16 @@ namespace app {
bool saveAsDialog(Context* context, const char* dlgTitle,
FileSelectorDelegate* delegate = nullptr);
void saveDocumentInBackground(const Context* context,
const app::Document* document,
bool markAsSaved) const;
std::string m_filename;
std::string m_filenameFormat;
std::string m_selectedFilename;
std::string m_frameTag;
doc::SelectedFrames m_selFrames;
bool m_adjustFramesByFrameTag;
};
} // namespace app

View File

@ -84,8 +84,8 @@ void SetLoopSectionCommand::onExecute(Context* ctx)
case Action::Auto: {
auto range = App::instance()->timeline()->range();
if (range.enabled() && (range.frames() > 1)) {
begin = range.frameBegin();
end = range.frameEnd();
begin = range.selectedFrames().firstFrame();
end = range.selectedFrames().lastFrame();
on = true;
}
else {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -48,7 +48,8 @@ void SetPaletteCommand::onExecute(Context* context)
set_current_palette(m_palette, false);
// Redraw the entire screen
ui::Manager::getDefault()->invalidate();
if (context->isUIAvailable())
ui::Manager::getDefault()->invalidate();
}
Command* CommandFactory::createSetPaletteCommand()

View File

@ -8,13 +8,15 @@
#include "config.h"
#endif
#include "app/cmd/set_pixel_ratio.h"
#include "app/color.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/document_api.h"
#include "app/modules/gui.h"
#include "app/ui/color_button.h"
#include "app/transaction.h"
#include "app/ui/color_button.h"
#include "app/util/pixel_ratio.h"
#include "base/bind.h"
#include "base/mem_utils.h"
#include "doc/image.h"
@ -112,6 +114,10 @@ void SpritePropertiesCommand::onExecute(Context* context)
else {
window.transparentColorPlaceholder()->addChild(new Label("(only for indexed images)"));
}
// Pixel ratio
window.pixelRatio()->setValue(
base::convert_to<std::string>(sprite->pixelRatio()));
}
window.remapWindow();
@ -122,21 +128,28 @@ void SpritePropertiesCommand::onExecute(Context* context)
window.openWindowInForeground();
if (window.closer() == window.ok()) {
if (color_button) {
ContextWriter writer(context);
Sprite* sprite(writer.sprite());
ContextWriter writer(context);
Sprite* sprite(writer.sprite());
// If the transparent color index has changed, we update the
// property in the sprite.
int index = color_button->getColor().getIndex();
if (color_t(index) != sprite->transparentColor()) {
Transaction transaction(writer.context(), "Set Transparent Color");
DocumentApi api = writer.document()->getApi(transaction);
color_t index = (color_button ? color_button->getColor().getIndex():
sprite->transparentColor());
PixelRatio pixelRatio =
base::convert_to<PixelRatio>(window.pixelRatio()->getValue());
if (index != sprite->transparentColor() ||
pixelRatio != sprite->pixelRatio()) {
Transaction transaction(writer.context(), "Change Sprite Properties");
DocumentApi api = writer.document()->getApi(transaction);
if (index != sprite->transparentColor())
api.setSpriteTransparentColor(sprite, index);
transaction.commit();
update_screen_for_document(writer.document());
}
if (pixelRatio != sprite->pixelRatio())
transaction.execute(new cmd::SetPixelRatio(sprite, pixelRatio));
transaction.commit();
update_screen_for_document(writer.document());
}
}

View File

@ -8,6 +8,7 @@
#include "config.h"
#endif
#include "app/cmd/set_cel_bounds.h"
#include "app/commands/cmd_sprite_size.h"
#include "app/commands/command.h"
#include "app/commands/params.h"
@ -48,8 +49,11 @@ class SpriteSizeJob : public Job {
int m_new_height;
ResizeMethod m_resize_method;
int scale_x(int x) const { return x * m_new_width / m_sprite->width(); }
int scale_y(int y) const { return y * m_new_height / m_sprite->height(); }
template<typename T>
T scale_x(T x) const { return x * T(m_new_width) / T(m_sprite->width()); }
template<typename T>
T scale_y(T y) const { return y * T(m_new_height) / T(m_sprite->height()); }
public:
@ -83,33 +87,44 @@ protected:
// For each cel...
int progress = 0;
for (Cel* cel : m_sprite->uniqueCels()) {
// Change its location
api.setCelPosition(m_sprite, cel, scale_x(cel->x()), scale_y(cel->y()));
// Get cel's image
Image* image = cel->image();
if (image && !cel->link()) {
// Resize the image
int w = scale_x(image->width());
int h = scale_y(image->height());
ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)));
new_image->setMaskColor(image->maskColor());
// Resize the cel bounds only if it's from a reference layer
if (cel->layer()->isReference()) {
gfx::RectF newBounds = cel->boundsF();
newBounds.x = scale_x(newBounds.x);
newBounds.y = scale_y(newBounds.y);
newBounds.w = scale_x(newBounds.w);
newBounds.h = scale_y(newBounds.h);
transaction.execute(new cmd::SetCelBoundsF(cel, newBounds));
}
else {
// Change its location
api.setCelPosition(m_sprite, cel, scale_x(cel->x()), scale_y(cel->y()));
doc::algorithm::fixup_image_transparent_colors(image);
doc::algorithm::resize_image(
image, new_image.get(),
m_resize_method,
m_sprite->palette(cel->frame()),
m_sprite->rgbMap(cel->frame()),
(cel->layer()->isBackground() ? -1: m_sprite->transparentColor()));
// Resize the image
int w = scale_x(image->width());
int h = scale_y(image->height());
ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)));
new_image->setMaskColor(image->maskColor());
api.replaceImage(m_sprite, cel->imageRef(), new_image);
doc::algorithm::fixup_image_transparent_colors(image);
doc::algorithm::resize_image(
image, new_image.get(),
m_resize_method,
m_sprite->palette(cel->frame()),
m_sprite->rgbMap(cel->frame()),
(cel->layer()->isBackground() ? -1: m_sprite->transparentColor()));
api.replaceImage(m_sprite, cel->imageRef(), new_image);
}
}
jobProgress((float)progress / cels_count);
++progress;
// cancel all the operation?
// Cancel all the operation?
if (isCanceled())
return; // Transaction destructor will undo all operations
}

View File

@ -0,0 +1,49 @@
// Aseprite
// Copyright (C) 2016 Carlo Caputo
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/app.h"
#include "app/commands/command.h"
#include "app/context.h"
#include "app/document.h"
#include "app/pref/preferences.h"
namespace app {
using namespace gfx;
class ToggleTimelineThumbnailsCommand : public Command {
public:
ToggleTimelineThumbnailsCommand()
: Command("ToggleTimelineThumbnails",
"Toggle Timeline Thumbnails",
CmdUIOnlyFlag)
{
}
Command* clone() const override { return new ToggleTimelineThumbnailsCommand(*this); }
protected:
bool onChecked(Context* context) override {
DocumentPreferences& docPref = Preferences::instance().document(context->activeDocument());
return docPref.thumbnails.enabled();
}
void onExecute(Context* context) override {
DocumentPreferences& docPref = Preferences::instance().document(context->activeDocument());
docPref.thumbnails.enabled(!docPref.thumbnails.enabled());
}
};
Command* CommandFactory::createToggleTimelineThumbnailsCommand()
{
return new ToggleTimelineThumbnailsCommand;
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -70,7 +70,7 @@ void UndoCommand::onExecute(Context* context)
Preferences::instance().undo.gotoModified();
if (gotoModified) {
SpritePosition currentPosition(writer.site()->layerIndex(),
SpritePosition currentPosition(writer.site()->layer(),
writer.site()->frame());
if (m_type == Undo)
@ -79,7 +79,9 @@ void UndoCommand::onExecute(Context* context)
spritePosition = undo->nextRedoSpritePosition();
if (spritePosition != currentPosition) {
current_editor->setLayer(sprite->indexToLayer(spritePosition.layerIndex()));
Layer* selectLayer = spritePosition.layer();
if (selectLayer)
current_editor->setLayer(selectLayer);
current_editor->setFrame(spritePosition.frame());
// Draw the current layer/frame (which is not undone yet) so the
@ -111,11 +113,13 @@ void UndoCommand::onExecute(Context* context)
// weren't able to reach before the undo).
if (gotoModified) {
SpritePosition currentPosition(
writer.site()->layerIndex(),
writer.site()->layer(),
writer.site()->frame());
if (spritePosition != currentPosition) {
current_editor->setLayer(sprite->indexToLayer(spritePosition.layerIndex()));
Layer* selectLayer = spritePosition.layer();
if (selectLayer)
current_editor->setLayer(selectLayer);
current_editor->setFrame(spritePosition.frame());
}
}

View File

@ -15,7 +15,6 @@
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
@ -52,36 +51,31 @@ void UnlinkCelCommand::onExecute(Context* context)
{
Transaction transaction(writer.context(), "Unlink Cel");
// TODO the range of selected frames should be in doc::Site.
auto range = App::instance()->timeline()->range();
if (range.enabled()) {
Sprite* sprite = writer.sprite();
for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) {
Layer* layer = sprite->indexToLayer(layerIdx);
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty()) {
for (Layer* layer : site->selectedLayers()) {
if (!layer->isImage())
continue;
if (!layer->isEditableHierarchy()) {
nonEditableLayers = true;
continue;
}
LayerImage* layerImage = static_cast<LayerImage*>(layer);
for (frame_t frame = range.frameEnd(),
begin = range.frameBegin()-1;
frame != begin;
--frame) {
for (frame_t frame : site->selectedFrames().reversed()) {
Cel* cel = layerImage->cel(frame);
if (cel && cel->links()) {
if (layerImage->isEditable())
transaction.execute(new cmd::UnlinkCel(cel));
else
nonEditableLayers = true;
}
if (cel && cel->links())
transaction.execute(new cmd::UnlinkCel(cel));
}
}
}
else {
Cel* cel = writer.cel();
if (cel && cel->links()) {
if (cel->layer()->isEditable())
if (cel->layer()->isEditableHierarchy())
transaction.execute(new cmd::UnlinkCel(writer.cel()));
else
nonEditableLayers = true;

View File

@ -80,10 +80,10 @@ FOR_EACH_COMMAND(NewFile)
FOR_EACH_COMMAND(NewFrame)
FOR_EACH_COMMAND(NewFrameTag)
FOR_EACH_COMMAND(NewLayer)
FOR_EACH_COMMAND(NewLayerSet)
FOR_EACH_COMMAND(NewSpriteFromSelection)
FOR_EACH_COMMAND(OpenBrowser)
FOR_EACH_COMMAND(OpenFile)
FOR_EACH_COMMAND(OpenGroup)
FOR_EACH_COMMAND(OpenInFolder)
FOR_EACH_COMMAND(OpenWithApp)
FOR_EACH_COMMAND(Options)
@ -133,6 +133,7 @@ FOR_EACH_COMMAND(SymmetryMode)
FOR_EACH_COMMAND(TiledMode)
FOR_EACH_COMMAND(Timeline)
FOR_EACH_COMMAND(TogglePreview)
FOR_EACH_COMMAND(ToggleTimelineThumbnails)
FOR_EACH_COMMAND(Undo)
FOR_EACH_COMMAND(UndoHistory)
FOR_EACH_COMMAND(UnlinkCel)

View File

@ -214,7 +214,7 @@ void FilterManagerImpl::applyToTarget()
bool cancelled = false;
ImagesCollector images((m_target & TARGET_ALL_LAYERS ?
m_site.sprite()->folder():
m_site.sprite()->root():
m_site.layer()),
m_site.frame(),
(m_target & TARGET_ALL_FRAMES) == TARGET_ALL_FRAMES,
@ -265,9 +265,9 @@ void FilterManagerImpl::flush()
m_bounds.x,
m_bounds.y+m_row-1)),
gfx::Size(
editor->zoom().apply(m_bounds.w),
(editor->zoom().scale() >= 1 ? editor->zoom().apply(1):
editor->zoom().remove(1))));
editor->projection().applyX(m_bounds.w),
(editor->projection().scaleY() >= 1 ? editor->projection().applyY(1):
editor->projection().removeY(1))));
gfx::Region reg1(rect);
gfx::Region reg2;

View File

@ -87,12 +87,15 @@ void ContextFlags::updateFlagsFromSite(const Site& site)
if (layer->isBackground())
m_flags |= ActiveLayerIsBackground;
if (layer->isVisible())
if (layer->isVisibleHierarchy())
m_flags |= ActiveLayerIsVisible;
if (layer->isEditable())
if (layer->isEditableHierarchy())
m_flags |= ActiveLayerIsEditable;
if (layer->isReference())
m_flags |= ActiveLayerIsReference;
if (layer->isImage()) {
m_flags |= ActiveLayerIsImage;

View File

@ -34,6 +34,7 @@ namespace app {
ActiveLayerIsBackground = 1 << 10,
ActiveLayerIsVisible = 1 << 11,
ActiveLayerIsEditable = 1 << 12,
ActiveLayerIsReference = 1 << 13,
};
ContextFlags();

View File

@ -38,7 +38,10 @@ DataRecovery::DataRecovery(doc::Context* ctx)
SessionPtr session(new Session(itempath));
if (!session->isRunning()) {
if (!session->isEmpty()) {
if (session->version() != VERSION) {
TRACE("cannot be loaded (incompatible version)\n");
}
else if (!session->isEmpty()) {
TRACE("to be loaded\n");
m_sessions.push_back(session);
}

View File

@ -238,11 +238,26 @@ private:
// Read layers
int nlayers = read32(s);
if (nlayers >= 1 && nlayers < 0xfffff) {
std::map<ObjectId, LayerGroup*> layersMap;
layersMap[0] = spr->root(); // parentId = 0 is the root level
for (int i = 0; i < nlayers; ++i) {
ObjectId layId = read32(s);
ObjectId parentId = read32(s);
if (!layersMap[parentId]) {
Console().printf("Inexistent parent #%d for layer #%d", parentId, layId);
// Put this layer at the root level
parentId = 0;
}
Layer* lay = loadObject<Layer*>("lay", layId, &Reader::readLayer);
if (lay)
spr->folder()->addLayer(lay);
if (lay) {
if (lay->isGroup())
layersMap[layId] = static_cast<LayerGroup*>(lay);
layersMap[parentId]->addLayer(lay);
}
}
}
else {
@ -277,7 +292,8 @@ private:
Layer* readLayer(std::ifstream& s) {
LayerFlags flags = (LayerFlags)read32(s);
ObjectType type = (ObjectType)read16(s);
ASSERT(type == ObjectType::LayerImage);
ASSERT(type == ObjectType::LayerImage ||
type == ObjectType::LayerGroup);
std::string name = read_string(s);
@ -301,6 +317,12 @@ private:
}
return lay.release();
}
else if (type == ObjectType::LayerGroup) {
base::UniquePtr<LayerGroup> lay(new LayerGroup(m_sprite));
lay->setName(name);
lay->setFlags(flags);
return lay.release();
}
else {
Console().printf("Unable to load layer named '%s', type #%d\n",
name.c_str(), (int)type);
@ -394,7 +416,7 @@ app::Document* read_document_with_raw_images(const std::string& dir,
// Load each image as a new frame
auto lay = new LayerImage(spr);
spr->folder()->addLayer(lay);
spr->root()->addLayer(lay);
frame_t frame = 0;
for (const auto& fn : base::list_files(dir)) {
@ -419,7 +441,7 @@ app::Document* read_document_with_raw_images(const std::string& dir,
break;
case RawImagesAs::kLayers:
lay = new LayerImage(spr);
spr->folder()->addLayer(lay);
spr->root()->addLayer(lay);
break;
}
}

View File

@ -72,6 +72,19 @@ std::string Session::name() const
return name;
}
std::string Session::version()
{
if (m_version.empty()) {
std::string verfile = verFilename();
if (base::is_file(verfile)) {
std::ifstream pf(FSTREAM_PATH(verfile));
if (pf)
pf >> m_version;
}
}
return m_version;
}
const Session::Backups& Session::backups()
{
if (m_backups.empty()) {

View File

@ -41,6 +41,7 @@ namespace crash {
~Session();
std::string name() const;
std::string version();
const Backups& backups();
bool isRunning();
@ -66,8 +67,7 @@ namespace crash {
base::pid m_pid;
std::string m_path;
std::fstream m_log;
std::fstream m_pidFile;
std::string m_version;
Backups m_backups;
DISABLE_COPYING(Session);

View File

@ -72,20 +72,37 @@ public:
if (!saveObject("frtag", frtag, &Writer::writeFrameTag))
return false;
for (Cel* cel : spr->uniqueCels()) {
if (!saveObject("img", cel->image(), &Writer::writeImage))
return false;
// Get all layers (visible, hidden, subchildren, etc.)
LayerList layers = spr->allLayers();
if (!saveObject("celdata", cel->data(), &Writer::writeCelData))
return false;
// Save original cel data (skip links)
for (Layer* lay : layers) {
CelList cels;
lay->getCels(cels);
for (Cel* cel : cels) {
if (cel->link()) // Skip link
continue;
if (!saveObject("img", cel->image(), &Writer::writeImage))
return false;
if (!saveObject("celdata", cel->data(), &Writer::writeCelData))
return false;
}
}
for (Cel* cel : spr->cels())
if (!saveObject("cel", cel, &Writer::writeCel))
return false;
// Save all cels (original and links)
for (Layer* lay : layers) {
CelList cels;
lay->getCels(cels);
std::vector<Layer*> layers;
spr->getLayersList(layers);
for (Cel* cel : cels)
if (!saveObject("cel", cel, &Writer::writeCel))
return false;
}
// Save all layers (top level, groups, children, etc.)
for (Layer* lay : layers)
if (!saveObject("lay", lay, &Writer::writeLayerStructure))
return false;
@ -125,11 +142,8 @@ private:
write32(s, spr->frameDuration(fr));
// IDs of all main layers
std::vector<Layer*> layers;
spr->getLayersList(layers);
write32(s, layers.size());
for (Layer* lay : layers)
write32(s, lay->id());
write32(s, spr->allLayersCount());
writeAllLayersID(s, 0, spr->root());
// IDs of all palettes
write32(s, spr->getPalettes().size());
@ -144,21 +158,40 @@ private:
return true;
}
void writeAllLayersID(std::ofstream& s, ObjectId parentId, const LayerGroup* group) {
for (const Layer* lay : group->layers()) {
write32(s, lay->id());
write32(s, parentId);
if (lay->isGroup())
writeAllLayersID(s, lay->id(), static_cast<const LayerGroup*>(lay));
}
}
bool writeLayerStructure(std::ofstream& s, Layer* lay) {
write32(s, static_cast<int>(lay->flags())); // Flags
write16(s, static_cast<int>(lay->type())); // Type
write_string(s, lay->name());
if (lay->type() == ObjectType::LayerImage) {
CelConstIterator it, begin = static_cast<const LayerImage*>(lay)->getCelBegin();
CelConstIterator end = static_cast<const LayerImage*>(lay)->getCelEnd();
switch (lay->type()) {
// Cels
write32(s, static_cast<const LayerImage*>(lay)->getCelsCount());
for (it=begin; it != end; ++it) {
const Cel* cel = *it;
write32(s, cel->id());
case ObjectType::LayerImage: {
CelConstIterator it, begin = static_cast<const LayerImage*>(lay)->getCelBegin();
CelConstIterator end = static_cast<const LayerImage*>(lay)->getCelEnd();
// Cels
write32(s, static_cast<const LayerImage*>(lay)->getCelsCount());
for (it=begin; it != end; ++it) {
const Cel* cel = *it;
write32(s, cel->id());
}
break;
}
case ObjectType::LayerGroup:
// Do nothing (the layer parent/children structure is saved in
// writeSprite/writeAllLayersID() functions)
break;
}
return true;
}

View File

@ -310,6 +310,7 @@ void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, La
else {
newCel.reset(create_cel_copy(sourceCel,
destLayer->sprite(),
destLayer,
sourceCel->frame()));
linked.insert(std::make_pair(sourceCel->data()->id(), newCel.get()));
}
@ -318,23 +319,19 @@ void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, La
newCel.release();
}
}
else if (sourceLayer0->isFolder() && destLayer0->isFolder()) {
const LayerFolder* sourceLayer = static_cast<const LayerFolder*>(sourceLayer0);
LayerFolder* destLayer = static_cast<LayerFolder*>(destLayer0);
else if (sourceLayer0->isGroup() && destLayer0->isGroup()) {
const LayerGroup* sourceLayer = static_cast<const LayerGroup*>(sourceLayer0);
LayerGroup* destLayer = static_cast<LayerGroup*>(destLayer0);
LayerConstIterator it = sourceLayer->getLayerBegin();
LayerConstIterator end = sourceLayer->getLayerEnd();
for (; it != end; ++it) {
Layer* sourceChild = *it;
for (Layer* sourceChild : sourceLayer->layers()) {
base::UniquePtr<Layer> destChild(NULL);
if (sourceChild->isImage()) {
destChild.reset(new LayerImage(destLayer->sprite()));
copyLayerContent(sourceChild, destDoc, destChild);
}
else if (sourceChild->isFolder()) {
destChild.reset(new LayerFolder(destLayer->sprite()));
else if (sourceChild->isGroup()) {
destChild.reset(new LayerGroup(destLayer->sprite()));
copyLayerContent(sourceChild, destDoc, destChild);
}
else {
@ -346,7 +343,7 @@ void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, La
// Add the new layer in the sprite.
Layer* newLayer = destChild.release();
Layer* afterThis = destLayer->getLastLayer();
Layer* afterThis = destLayer->lastLayer();
destLayer->addLayer(newLayer);
destChild.release();
@ -394,8 +391,8 @@ Document* Document::duplicate(DuplicateType type) const
switch (type) {
case DuplicateExactCopy:
// Copy the layer folder
copyLayerContent(sourceSprite->folder(), documentCopy, spriteCopy->folder());
// Copy the layer group
copyLayerContent(sourceSprite->root(), documentCopy, spriteCopy->root());
ASSERT((spriteCopy->backgroundLayer() && sourceSprite->backgroundLayer()) ||
(!spriteCopy->backgroundLayer() && !sourceSprite->backgroundLayer()));
@ -404,16 +401,16 @@ Document* Document::duplicate(DuplicateType type) const
case DuplicateWithFlattenLayers:
{
// Flatten layers
ASSERT(sourceSprite->folder() != NULL);
ASSERT(sourceSprite->root() != NULL);
LayerImage* flatLayer = create_flatten_layer_copy
(spriteCopy,
sourceSprite->folder(),
sourceSprite->root(),
gfx::Rect(0, 0, sourceSprite->width(), sourceSprite->height()),
frame_t(0), sourceSprite->lastFrame());
// Add and select the new flat layer
spriteCopy->folder()->addLayer(flatLayer);
spriteCopy->root()->addLayer(flatLayer);
// Configure the layer as background only if the original
// sprite has a background layer.

View File

@ -28,6 +28,7 @@
#include "app/cmd/remove_frame_tag.h"
#include "app/cmd/remove_layer.h"
#include "app/cmd/replace_image.h"
#include "app/cmd/set_cel_bounds.h"
#include "app/cmd/set_cel_frame.h"
#include "app/cmd/set_cel_opacity.h"
#include "app/cmd/set_cel_position.h"
@ -81,8 +82,7 @@ void DocumentApi::cropSprite(Sprite* sprite, const gfx::Rect& bounds)
setSpriteSize(sprite, bounds.w, bounds.h);
app::Document* doc = static_cast<app::Document*>(sprite->document());
std::vector<Layer*> layers;
sprite->getLayersList(layers);
LayerList layers = sprite->allLayers();
for (Layer* layer : layers) {
if (!layer->isImage())
continue;
@ -113,6 +113,13 @@ void DocumentApi::cropSprite(Sprite* sprite, const gfx::Rect& bounds)
replaceImage(sprite, cel->imageRef(), new_image);
}
}
else if (layer->isReference()) {
// Update the ref cel's bounds
gfx::RectF newBounds = cel->boundsF();
newBounds.x -= bounds.x;
newBounds.y -= bounds.y;
m_transaction.execute(new cmd::SetCelBoundsF(cel, newBounds));
}
else {
// Update the cel's position
setCelPosition(sprite, cel,
@ -237,7 +244,7 @@ void DocumentApi::moveFrame(Sprite* sprite, frame_t frame, frame_t beforeFrame)
adjustFrameTags(sprite, beforeFrame, +1, true);
// Change cel positions.
moveFrameLayer(sprite->folder(), frame, beforeFrame);
moveFrameLayer(sprite->root(), frame, beforeFrame);
}
}
@ -288,12 +295,9 @@ void DocumentApi::moveFrameLayer(Layer* layer, frame_t frame, frame_t beforeFram
break;
}
case ObjectType::LayerFolder: {
LayerIterator it = static_cast<LayerFolder*>(layer)->getLayerBegin();
LayerIterator end = static_cast<LayerFolder*>(layer)->getLayerEnd();
for (; it != end; ++it)
moveFrameLayer(*it, frame, beforeFrame);
case ObjectType::LayerGroup: {
for (Layer* child : static_cast<LayerGroup*>(layer)->layers())
moveFrameLayer(child, frame, beforeFrame);
break;
}
@ -395,30 +399,27 @@ void DocumentApi::swapCel(
if (cel2) setCelFramePosition(cel2, frame1);
}
LayerImage* DocumentApi::newLayer(Sprite* sprite, const std::string& name)
LayerImage* DocumentApi::newLayer(LayerGroup* parent, const std::string& name)
{
LayerImage* layer = new LayerImage(sprite);
LayerImage* layer = new LayerImage(parent->sprite());
layer->setName(name);
addLayer(sprite->folder(), layer,
sprite->folder()->getLastLayer());
addLayer(parent, layer, parent->lastLayer());
return layer;
}
LayerFolder* DocumentApi::newLayerFolder(Sprite* sprite)
LayerGroup* DocumentApi::newGroup(LayerGroup* parent, const std::string& name)
{
LayerFolder* layer = new LayerFolder(sprite);
addLayer(sprite->folder(), layer,
sprite->folder()->getLastLayer());
LayerGroup* layer = new LayerGroup(parent->sprite());
layer->setName(name);
addLayer(parent, layer, parent->lastLayer());
return layer;
}
void DocumentApi::addLayer(LayerFolder* folder, Layer* newLayer, Layer* afterThis)
void DocumentApi::addLayer(LayerGroup* parent, Layer* newLayer, Layer* afterThis)
{
m_transaction.execute(new cmd::AddLayer(folder, newLayer, afterThis));
m_transaction.execute(new cmd::AddLayer(parent, newLayer, afterThis));
}
void DocumentApi::removeLayer(Layer* layer)
@ -428,17 +429,30 @@ void DocumentApi::removeLayer(Layer* layer)
m_transaction.execute(new cmd::RemoveLayer(layer));
}
void DocumentApi::restackLayerAfter(Layer* layer, Layer* afterThis)
void DocumentApi::restackLayerAfter(Layer* layer, LayerGroup* parent, Layer* afterThis)
{
m_transaction.execute(new cmd::MoveLayer(layer, afterThis));
ASSERT(parent);
if (layer == afterThis)
return;
m_transaction.execute(new cmd::MoveLayer(layer, parent, afterThis));
}
void DocumentApi::restackLayerBefore(Layer* layer, Layer* beforeThis)
void DocumentApi::restackLayerBefore(Layer* layer, LayerGroup* parent, Layer* beforeThis)
{
LayerIndex beforeThisIdx = layer->sprite()->layerToIndex(beforeThis);
LayerIndex afterThisIdx = beforeThisIdx.previous();
ASSERT(parent);
restackLayerAfter(layer, layer->sprite()->indexToLayer(afterThisIdx));
if (layer == beforeThis)
return;
Layer* afterThis;
if (beforeThis)
afterThis = beforeThis->getPrevious();
else
afterThis = parent->lastLayer();
restackLayerAfter(layer, parent, afterThis);
}
void DocumentApi::backgroundFromLayer(Layer* layer)
@ -456,26 +470,36 @@ void DocumentApi::flattenLayers(Sprite* sprite)
m_transaction.execute(new cmd::FlattenLayers(sprite));
}
void DocumentApi::duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer)
Layer* DocumentApi::duplicateLayerAfter(Layer* sourceLayer, LayerGroup* parent, Layer* afterLayer)
{
base::UniquePtr<LayerImage> newLayerPtr(new LayerImage(sourceLayer->sprite()));
ASSERT(parent);
base::UniquePtr<Layer> newLayerPtr;
if (sourceLayer->isImage())
newLayerPtr.reset(new LayerImage(sourceLayer->sprite()));
else if (sourceLayer->isGroup())
newLayerPtr.reset(new LayerGroup(sourceLayer->sprite()));
else
throw std::runtime_error("Invalid layer type");
m_document->copyLayerContent(sourceLayer, m_document, newLayerPtr);
newLayerPtr->setName(newLayerPtr->name() + " Copy");
addLayer(sourceLayer->parent(), newLayerPtr, afterLayer);
addLayer(parent, newLayerPtr, afterLayer);
// Release the pointer as it is owned by the sprite now.
newLayerPtr.release();
return newLayerPtr.release();
}
void DocumentApi::duplicateLayerBefore(Layer* sourceLayer, Layer* beforeLayer)
Layer* DocumentApi::duplicateLayerBefore(Layer* sourceLayer, LayerGroup* parent, Layer* beforeLayer)
{
LayerIndex beforeThisIdx = sourceLayer->sprite()->layerToIndex(beforeLayer);
LayerIndex afterThisIdx = beforeThisIdx.previous();
duplicateLayerAfter(sourceLayer, sourceLayer->sprite()->indexToLayer(afterThisIdx));
ASSERT(parent);
Layer* afterThis = (beforeLayer ? beforeLayer->getPreviousBrowsable(): nullptr);
Layer* newLayer = duplicateLayerAfter(sourceLayer, parent, afterThis);
if (newLayer)
restackLayerBefore(newLayer, parent, beforeLayer);
return newLayer;
}
Cel* DocumentApi::addCel(LayerImage* layer, frame_t frameNumber, const ImageRef& image)
@ -485,9 +509,7 @@ Cel* DocumentApi::addCel(LayerImage* layer, frame_t frameNumber, const ImageRef&
base::UniquePtr<Cel> cel(new Cel(frameNumber, image));
addCel(layer, cel);
cel.release();
return cel;
return cel.release();
}
void DocumentApi::replaceImage(Sprite* sprite, const ImageRef& oldImage, const ImageRef& newImage)

Some files were not shown because too many files have changed in this diff Show More