mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-06 03:39:51 +00:00
Merge branch 'beta' into master
The next official release will be v1.2.
This commit is contained in:
commit
79f82cfbee
19
data/gui.xml
19
data/gui.xml
@ -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="&Layer">
|
||||
<item command="LayerProperties" text="&Properties..." />
|
||||
<item command="LayerVisibility" text="&Visible" />
|
||||
<item command="OpenGroup" text="&Open Group" />
|
||||
<separator />
|
||||
<item command="NewLayer" text="&New Layer" />
|
||||
<item command="NewLayer" text="New &Group">
|
||||
<param name="group" value="true" />
|
||||
</item>
|
||||
<item command="RemoveLayer" text="&Remove Layer" />
|
||||
<item command="BackgroundFromLayer" text="&Background from Layer" />
|
||||
<item command="LayerFromBackground" text="&Layer from Background" />
|
||||
@ -685,6 +694,11 @@
|
||||
<item command="DuplicateLayer" text="&Duplicate" />
|
||||
<item command="MergeDownLayer" text="&Merge Down" />
|
||||
<item command="FlattenLayers" text="&Flatten" />
|
||||
<separator />
|
||||
<item command="NewLayer" text="Add R&eference Layer" >
|
||||
<param name="reference" value="true" />
|
||||
<param name="from-file" value="true" />
|
||||
</item>
|
||||
</menu>
|
||||
<menu text="F&rame">
|
||||
<item command="FrameProperties" text="Frame &Properties...">
|
||||
@ -845,6 +859,9 @@
|
||||
<item command="LayerProperties" text="&Properties..." />
|
||||
<separator />
|
||||
<item command="NewLayer" text="&New" />
|
||||
<item command="NewLayer" text="New &Group" >
|
||||
<param name="group" value="true" />
|
||||
</item>
|
||||
<item command="RemoveLayer" text="&Remove" />
|
||||
<item command="BackgroundFromLayer" text="&Background from Layer" />
|
||||
<item command="LayerFromBackground" text="&Layer from Background" />
|
||||
|
@ -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 |
@ -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" />
|
||||
|
@ -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="&OK" closewindow="true" id="ok" magnet="true" width="60" />
|
||||
|
31
data/widgets/file_selector_extras.xml
Normal file
31
data/widgets/file_selector_extras.xml
Normal 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>
|
@ -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="&OK" closewindow="true" id="ok" magnet="true" />
|
||||
<button text="&Cancel" closewindow="true" />
|
||||
</box>
|
||||
</box>
|
||||
<box horizontal="true" homogeneous="true">
|
||||
<button text="&OK" closewindow="true" id="ok" magnet="true" />
|
||||
<button text="&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="&OK" closewindow="true" id="ok" magnet="true" />
|
||||
<button text="&Cancel" closewindow="true" />
|
||||
</box>
|
||||
</box>
|
||||
</window>
|
||||
</window>
|
||||
</gui>
|
||||
|
@ -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="&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">
|
||||
|
@ -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>
|
||||
|
@ -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 transparent color in each 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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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)
|
||||
|
||||
```
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
471
src/app/app.cpp
471
src/app/app.cpp
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
37
src/app/cli/cli_delegate.h
Normal file
37
src/app/cli/cli_delegate.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is 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
|
46
src/app/cli/cli_open_file.cpp
Normal file
46
src/app/cli/cli_open_file.cpp
Normal 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
|
60
src/app/cli/cli_open_file.h
Normal file
60
src/app/cli/cli_open_file.h
Normal 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
|
653
src/app/cli/cli_processor.cpp
Normal file
653
src/app/cli/cli_processor.cpp
Normal 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
|
40
src/app/cli/cli_processor.h
Normal file
40
src/app/cli/cli_processor.h
Normal 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
86
src/app/cli/cli_tests.cpp
Normal 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());
|
||||
}
|
127
src/app/cli/default_cli_delegate.cpp
Normal file
127
src/app/cli/default_cli_delegate.cpp
Normal 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
|
28
src/app/cli/default_cli_delegate.h
Normal file
28
src/app/cli/default_cli_delegate.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is 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
|
218
src/app/cli/preview_cli_delegate.cpp
Normal file
218
src/app/cli/preview_cli_delegate.cpp
Normal 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
|
36
src/app/cli/preview_cli_delegate.h
Normal file
36
src/app/cli/preview_cli_delegate.h
Normal 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
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
51
src/app/cmd/set_cel_bounds.cpp
Normal file
51
src/app/cmd/set_cel_bounds.cpp
Normal 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
|
40
src/app/cmd/set_cel_bounds.h
Normal file
40
src/app/cmd/set_cel_bounds.h
Normal 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
|
@ -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();
|
||||
}
|
||||
|
||||
|
53
src/app/cmd/set_pixel_ratio.cpp
Normal file
53
src/app/cmd/set_pixel_ratio.cpp
Normal 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
|
46
src/app/cmd/set_pixel_ratio.h
Normal file
46
src/app/cmd/set_pixel_ratio.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
71
src/app/commands/cmd_open_group.cpp
Normal file
71
src/app/commands/cmd_open_group.cpp
Normal 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
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
49
src/app/commands/cmd_toggle_timeline_thumbnails.cpp
Normal file
49
src/app/commands/cmd_toggle_timeline_thumbnails.cpp
Normal 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
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -34,6 +34,7 @@ namespace app {
|
||||
ActiveLayerIsBackground = 1 << 10,
|
||||
ActiveLayerIsVisible = 1 << 11,
|
||||
ActiveLayerIsEditable = 1 << 12,
|
||||
ActiveLayerIsReference = 1 << 13,
|
||||
};
|
||||
|
||||
ContextFlags();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user