mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-30 04:20:23 +00:00
Add basic implementation of slice tool (#721) to modify theme parts
It still need support to export to .aseprite-data as <slice> XML elements, and animation support.
This commit is contained in:
parent
fa1b15a3f5
commit
643cad5c97
14
data/gui.xml
14
data/gui.xml
@ -445,8 +445,8 @@
|
||||
<key tool="eyedropper" shortcut="I" />
|
||||
<key tool="hand" shortcut="H" />
|
||||
<key tool="move" shortcut="V" />
|
||||
<key tool="slice" shortcut="C" />
|
||||
<key tool="zoom" shortcut="Z" />
|
||||
<!-- key tool="slice" shortcut="K" /-->
|
||||
|
||||
<key tool="paint_bucket" shortcut="G" />
|
||||
|
||||
@ -769,6 +769,7 @@
|
||||
<item command="ShowLayerEdges" text="&Layer Edges" />
|
||||
<item command="ShowSelectionEdges" text="&Selection Edges" />
|
||||
<item command="ShowGrid" text="&Grid" />
|
||||
<item command="ShowSlices" text="Sl&ices" />
|
||||
<item command="ShowPixelGrid" text="&Pixel Grid" />
|
||||
<separator />
|
||||
<item command="ShowBrushPreview" text="&Brush Preview" />
|
||||
@ -915,6 +916,11 @@
|
||||
<item command="RemoveFrameTag" text="&Remove Tag" />
|
||||
</menu>
|
||||
|
||||
<menu id="slice_popup">
|
||||
<item command="SliceProperties" text="Slice &Properties..." />
|
||||
<item command="RemoveSlice" text="&Remove Slice" />
|
||||
</menu>
|
||||
|
||||
<menu id="palette_popup">
|
||||
<item command="PaletteEditor" text="&Palette Editor">
|
||||
<param name="switch" value="true" />
|
||||
@ -1118,15 +1124,15 @@
|
||||
ink="move"
|
||||
controller="freehand"
|
||||
/>
|
||||
<!-- tool id="slice"
|
||||
<tool id="slice"
|
||||
text="Slice Tool"
|
||||
fill="always"
|
||||
fill="none"
|
||||
ink="slice"
|
||||
controller="two_points"
|
||||
pointshape="pixel"
|
||||
intertwine="as_rectangles"
|
||||
tracepolicy="last"
|
||||
/-->
|
||||
/>
|
||||
</group>
|
||||
|
||||
<group id="paint_bucket" text="Paint Bucket Tool">
|
||||
|
@ -349,6 +349,7 @@
|
||||
<option id="grid" type="bool" default="false" migrate="grid.visible" />
|
||||
<option id="pixel_grid" type="bool" default="false" migrate="pixel_grid.visible" />
|
||||
<option id="brush_preview" type="bool" default="true" />
|
||||
<option id="slices" type="bool" default="true" />
|
||||
</section>
|
||||
</document>
|
||||
|
||||
|
@ -438,6 +438,17 @@ open_dmp_file = Open the following file to debug your compilation:
|
||||
do_it_later = Do it later
|
||||
delete_file = Delete file, I've already sent it
|
||||
|
||||
[slice_properties]
|
||||
title = Slice Properties
|
||||
name = Slice Name:
|
||||
user_data_tooltip = User Data
|
||||
bounds = Bounds:
|
||||
center = 9-Slices
|
||||
x = X
|
||||
y = Y
|
||||
width = Width
|
||||
height = Height
|
||||
|
||||
[sprite_properties]
|
||||
title = Sprite Properties
|
||||
filename = File name:
|
||||
|
4
data/themes/default/sheet.aseprite-data
Normal file
4
data/themes/default/sheet.aseprite-data
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<sprite>
|
||||
<slices theme="theme.xml" />
|
||||
</sprite>
|
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
@ -282,12 +282,6 @@
|
||||
<part id="target_frames" x="176" y="224" w="32" h="16" />
|
||||
<part id="target_layers" x="208" y="224" w="32" h="16" />
|
||||
<part id="target_frames_layers" x="240" y="224" w="32" h="16" />
|
||||
<part id="brush_circle" x="144" y="144" w="7" h="8" />
|
||||
<part id="brush_circle_selected" x="144" y="152" w="7" h="8" />
|
||||
<part id="brush_square" x="160" y="144" w="7" h="8" />
|
||||
<part id="brush_square_selected" x="160" y="152" w="7" h="8" />
|
||||
<part id="brush_line" x="176" y="144" w="6" h="6" />
|
||||
<part id="brush_line_selected" x="176" y="152" w="6" h="6" />
|
||||
<part id="selection_replace" x="176" y="160" w="7" h="7" />
|
||||
<part id="selection_replace_selected" x="176" y="168" w="7" h="7" />
|
||||
<part id="selection_add" x="192" y="160" w="7" h="7" />
|
||||
@ -412,6 +406,7 @@
|
||||
<part id="icon_grid" x="224" y="264" w="8" h="8" />
|
||||
<part id="icon_save" x="232" y="264" w="8" h="8" />
|
||||
<part id="icon_save_small" x="240" y="264" w="8" h="8" />
|
||||
<part id="icon_slice" x="248" y="264" w="8" h="8" />
|
||||
</parts>
|
||||
|
||||
<stylesheet>
|
||||
|
50
data/widgets/slice_properties.xml
Normal file
50
data/widgets/slice_properties.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2017 by David Capello -->
|
||||
<gui>
|
||||
<window id="slice_properties" text="@.title">
|
||||
<grid columns="2">
|
||||
<label text="@.name" />
|
||||
<hbox>
|
||||
<entry maxsize="256" id="name" magnet="true" expansive="true" />
|
||||
<button id="user_data" icon="icon_user_data" tooltip="@.user_data_tooltip" />
|
||||
</hbox>
|
||||
|
||||
<separator horizontal="true" cell_hspan="2" />
|
||||
|
||||
<box />
|
||||
<hbox homogeneous="true">
|
||||
<label text="@.x" />
|
||||
<label text="@.y" />
|
||||
<label text="@.width" />
|
||||
<label text="@.height" />
|
||||
</hbox>
|
||||
|
||||
<label text="@.bounds" />
|
||||
<hbox homogeneous="true">
|
||||
<entry maxsize="8" id="bounds_x" />
|
||||
<entry maxsize="8" id="bounds_y" />
|
||||
<entry maxsize="8" id="bounds_w" />
|
||||
<entry maxsize="8" id="bounds_h" />
|
||||
</hbox>
|
||||
|
||||
<check text="@.center" id="center" />
|
||||
<hbox homogeneous="true">
|
||||
<entry maxsize="8" id="center_x" />
|
||||
<entry maxsize="8" id="center_y" />
|
||||
<entry maxsize="8" id="center_w" />
|
||||
<entry maxsize="8" id="center_h" />
|
||||
</hbox>
|
||||
|
||||
<separator horizontal="true" cell_hspan="2" />
|
||||
|
||||
<hbox cell_hspan="2">
|
||||
<boxfiller />
|
||||
<hbox homogeneous="true">
|
||||
<button text="@general.ok" closewindow="true" id="ok" magnet="true" minwidth="60" />
|
||||
<button text="@general.cancel" closewindow="true" />
|
||||
</hbox>
|
||||
</hbox>
|
||||
</grid>
|
||||
</window>
|
||||
</gui>
|
@ -291,15 +291,41 @@ belongs to that cel, etc.
|
||||
DWORD Flags
|
||||
1 = Has text
|
||||
2 = Has color
|
||||
+ If flags has bit 1:
|
||||
+ If flags have bit 1:
|
||||
STRING Text
|
||||
+ If flags has bit 2:
|
||||
+ If flags have bit 2:
|
||||
BYTE Color Red (0-255)
|
||||
BYTE Color Green (0-255)
|
||||
BYTE Color Blue (0-255)
|
||||
BYTE Color Alpha (0-255)
|
||||
```
|
||||
|
||||
### Slices Chunk (0x2021)
|
||||
|
||||
Field | Details |
|
||||
----------- | -------------------------------- |
|
||||
DWORD | Number of slices
|
||||
BYTE[8] | Reserved
|
||||
For each slice... |
|
||||
DWORD | Number of "slice keys"
|
||||
DWORD | Flags
|
||||
| 1 - It's a 9-patches slice
|
||||
DWORD | Reserved
|
||||
STRING | Name
|
||||
For each slice key ... |
|
||||
DWORD | Frame number (this slice is valid from
|
||||
| this frame to the end of the animation)
|
||||
SIGNED WORD | Slice X position
|
||||
SIGNED WORD | Slice Y position
|
||||
WORD | Slice Width (can be 0 if this slice hidden in the
|
||||
| animation from the given frame)
|
||||
WORD | Slice Height
|
||||
If flags have bit 1 |
|
||||
SIGNED WORD | Center X position (relative to slice bounds)
|
||||
SIGNED WORD | Center Y position
|
||||
WORD | Center Width
|
||||
WORD | Center Height
|
||||
|
||||
### Notes
|
||||
|
||||
#### NOTE.1
|
||||
|
@ -162,6 +162,7 @@ add_library(app-lib
|
||||
cmd/add_frame_tag.cpp
|
||||
cmd/add_layer.cpp
|
||||
cmd/add_palette.cpp
|
||||
cmd/add_slice.cpp
|
||||
cmd/background_from_layer.cpp
|
||||
cmd/clear_cel.cpp
|
||||
cmd/clear_image.cpp
|
||||
@ -188,6 +189,7 @@ add_library(app-lib
|
||||
cmd/remove_frame_tag.cpp
|
||||
cmd/remove_layer.cpp
|
||||
cmd/remove_palette.cpp
|
||||
cmd/remove_slice.cpp
|
||||
cmd/replace_image.cpp
|
||||
cmd/reselect_mask.cpp
|
||||
cmd/set_cel_bounds.cpp
|
||||
@ -209,6 +211,8 @@ add_library(app-lib
|
||||
cmd/set_palette.cpp
|
||||
cmd/set_pixel_format.cpp
|
||||
cmd/set_pixel_ratio.cpp
|
||||
cmd/set_slice_key.cpp
|
||||
cmd/set_slice_name.cpp
|
||||
cmd/set_sprite_size.cpp
|
||||
cmd/set_total_frames.cpp
|
||||
cmd/set_transparent_color.cpp
|
||||
@ -221,6 +225,7 @@ add_library(app-lib
|
||||
cmd/with_frame_tag.cpp
|
||||
cmd/with_image.cpp
|
||||
cmd/with_layer.cpp
|
||||
cmd/with_slice.cpp
|
||||
cmd/with_sprite.cpp
|
||||
cmd_sequence.cpp
|
||||
cmd_transaction.cpp
|
||||
@ -307,6 +312,7 @@ add_library(app-lib
|
||||
commands/cmd_remove_frame.cpp
|
||||
commands/cmd_remove_frame_tag.cpp
|
||||
commands/cmd_remove_layer.cpp
|
||||
commands/cmd_remove_slice.cpp
|
||||
commands/cmd_repeat_last_export.cpp
|
||||
commands/cmd_reselect_mask.cpp
|
||||
commands/cmd_reverse_frames.cpp
|
||||
@ -324,6 +330,7 @@ add_library(app-lib
|
||||
commands/cmd_set_palette_entry_size.cpp
|
||||
commands/cmd_set_same_ink.cpp
|
||||
commands/cmd_show.cpp
|
||||
commands/cmd_slice_properties.cpp
|
||||
commands/cmd_sprite_properties.cpp
|
||||
commands/cmd_sprite_size.cpp
|
||||
commands/cmd_switch_colors.cpp
|
||||
@ -433,6 +440,7 @@ add_library(app-lib
|
||||
ui/editor/editor_view.cpp
|
||||
ui/editor/moving_cel_state.cpp
|
||||
ui/editor/moving_pixels_state.cpp
|
||||
ui/editor/moving_slice_state.cpp
|
||||
ui/editor/moving_symmetry_state.cpp
|
||||
ui/editor/navigate_state.cpp
|
||||
ui/editor/pivot_helpers.cpp
|
||||
@ -478,6 +486,7 @@ add_library(app-lib
|
||||
ui/skin/skin_theme.cpp
|
||||
ui/skin/style.cpp
|
||||
ui/skin/style_sheet.cpp
|
||||
ui/slice_window.cpp
|
||||
ui/status_bar.cpp
|
||||
ui/styled_button.cpp
|
||||
ui/tabs.cpp
|
||||
|
@ -89,6 +89,7 @@ void AppMenus::reload()
|
||||
m_celPopupMenu.reset(loadMenuById(handle, "cel_popup"));
|
||||
m_celMovementPopupMenu.reset(loadMenuById(handle, "cel_movement_popup"));
|
||||
m_frameTagPopupMenu.reset(loadMenuById(handle, "frame_tag_popup"));
|
||||
m_slicePopupMenu.reset(loadMenuById(handle, "slice_popup"));
|
||||
m_palettePopupMenu.reset(loadMenuById(handle, "palette_popup"));
|
||||
m_inkPopupMenu.reset(loadMenuById(handle, "ink_popup"));
|
||||
|
||||
@ -284,6 +285,7 @@ void AppMenus::applyShortcutToMenuitemsWithCommand(Command* command, const Param
|
||||
m_celPopupMenu,
|
||||
m_celMovementPopupMenu,
|
||||
m_frameTagPopupMenu,
|
||||
m_slicePopupMenu,
|
||||
m_palettePopupMenu,
|
||||
m_inkPopupMenu
|
||||
};
|
||||
|
@ -46,6 +46,7 @@ namespace app {
|
||||
Menu* getCelPopupMenu() { return m_celPopupMenu; }
|
||||
Menu* getCelMovementPopupMenu() { return m_celMovementPopupMenu; }
|
||||
Menu* getFrameTagPopupMenu() { return m_frameTagPopupMenu; }
|
||||
Menu* getSlicePopupMenu() { return m_slicePopupMenu; }
|
||||
Menu* getPalettePopupMenu() { return m_palettePopupMenu; }
|
||||
Menu* getInkPopupMenu() { return m_inkPopupMenu; }
|
||||
|
||||
@ -67,6 +68,7 @@ namespace app {
|
||||
base::UniquePtr<Menu> m_celPopupMenu;
|
||||
base::UniquePtr<Menu> m_celMovementPopupMenu;
|
||||
base::UniquePtr<Menu> m_frameTagPopupMenu;
|
||||
base::UniquePtr<Menu> m_slicePopupMenu;
|
||||
base::UniquePtr<Menu> m_palettePopupMenu;
|
||||
base::UniquePtr<Menu> m_inkPopupMenu;
|
||||
obs::scoped_connection m_recentFilesConn;
|
||||
|
64
src/app/cmd/add_slice.cpp
Normal file
64
src/app/cmd/add_slice.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
// 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/cmd/add_slice.h"
|
||||
|
||||
#include "doc/slice.h"
|
||||
#include "doc/slice_io.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
AddSlice::AddSlice(Sprite* sprite, Slice* slice)
|
||||
: WithSprite(sprite)
|
||||
, WithSlice(slice)
|
||||
, m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
void AddSlice::onExecute()
|
||||
{
|
||||
Sprite* sprite = this->sprite();
|
||||
Slice* slice = this->slice();
|
||||
|
||||
sprite->slices().add(slice);
|
||||
sprite->incrementVersion();
|
||||
}
|
||||
|
||||
void AddSlice::onUndo()
|
||||
{
|
||||
Sprite* sprite = this->sprite();
|
||||
Slice* slice = this->slice();
|
||||
write_slice(m_stream, slice);
|
||||
m_size = size_t(m_stream.tellp());
|
||||
|
||||
sprite->slices().remove(slice);
|
||||
sprite->incrementVersion();
|
||||
delete slice;
|
||||
}
|
||||
|
||||
void AddSlice::onRedo()
|
||||
{
|
||||
Sprite* sprite = this->sprite();
|
||||
Slice* slice = read_slice(m_stream);
|
||||
|
||||
sprite->slices().add(slice);
|
||||
sprite->incrementVersion();
|
||||
|
||||
m_stream.str(std::string());
|
||||
m_stream.clear();
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
43
src/app/cmd/add_slice.h
Normal file
43
src/app/cmd/add_slice.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_ADD_SLICE_H_INCLUDED
|
||||
#define APP_CMD_ADD_SLICE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_slice.h"
|
||||
#include "app/cmd/with_sprite.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class AddSlice : public Cmd
|
||||
, public WithSprite
|
||||
, public WithSlice {
|
||||
public:
|
||||
AddSlice(Sprite* sprite, Slice* slice);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this) + m_size;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_size;
|
||||
std::stringstream m_stream;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
39
src/app/cmd/remove_slice.cpp
Normal file
39
src/app/cmd/remove_slice.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
// 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/cmd/remove_slice.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
RemoveSlice::RemoveSlice(Sprite* sprite, Slice* slice)
|
||||
: AddSlice(sprite, slice)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveSlice::onExecute()
|
||||
{
|
||||
AddSlice::onUndo();
|
||||
}
|
||||
|
||||
void RemoveSlice::onUndo()
|
||||
{
|
||||
AddSlice::onRedo();
|
||||
}
|
||||
|
||||
void RemoveSlice::onRedo()
|
||||
{
|
||||
AddSlice::onUndo();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
30
src/app/cmd/remove_slice.h
Normal file
30
src/app/cmd/remove_slice.h
Normal file
@ -0,0 +1,30 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_REMOVE_SLICE_H_INCLUDED
|
||||
#define APP_CMD_REMOVE_SLICE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd/add_slice.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class RemoveSlice : public AddSlice {
|
||||
public:
|
||||
RemoveSlice(Sprite* sprite, Slice* slice);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
55
src/app/cmd/set_slice_key.cpp
Normal file
55
src/app/cmd/set_slice_key.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
// 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/cmd/set_slice_key.h"
|
||||
|
||||
#include "doc/slice.h"
|
||||
#include "doc/slices.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
SetSliceKey::SetSliceKey(Slice* slice,
|
||||
const doc::frame_t frame,
|
||||
const doc::SliceKey& sliceKey)
|
||||
: WithSlice(slice)
|
||||
, m_frame(frame)
|
||||
, m_newSliceKey(sliceKey)
|
||||
{
|
||||
auto it = slice->getIteratorByFrame(frame);
|
||||
if (it != slice->end() && it->frame() == frame)
|
||||
m_oldSliceKey = *it->value();
|
||||
}
|
||||
|
||||
void SetSliceKey::onExecute()
|
||||
{
|
||||
if (!m_newSliceKey.isEmpty())
|
||||
slice()->insert(m_frame, m_newSliceKey);
|
||||
else
|
||||
slice()->remove(m_frame);
|
||||
|
||||
slice()->incrementVersion();
|
||||
slice()->owner()->sprite()->incrementVersion();
|
||||
}
|
||||
|
||||
void SetSliceKey::onUndo()
|
||||
{
|
||||
if (!m_oldSliceKey.isEmpty())
|
||||
slice()->insert(m_frame, m_oldSliceKey);
|
||||
else
|
||||
slice()->remove(m_frame);
|
||||
|
||||
slice()->incrementVersion();
|
||||
slice()->owner()->sprite()->incrementVersion();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
43
src/app/cmd/set_slice_key.h
Normal file
43
src/app/cmd/set_slice_key.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_SET_SLICE_KEY_H_INCLUDED
|
||||
#define APP_CMD_SET_SLICE_KEY_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_slice.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/slice.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class SetSliceKey : public Cmd
|
||||
, public WithSlice {
|
||||
public:
|
||||
SetSliceKey(Slice* slice,
|
||||
const doc::frame_t frame,
|
||||
const doc::SliceKey& sliceKey);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
doc::frame_t m_frame;
|
||||
doc::SliceKey m_oldSliceKey;
|
||||
doc::SliceKey m_newSliceKey;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
41
src/app/cmd/set_slice_name.cpp
Normal file
41
src/app/cmd/set_slice_name.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// 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/cmd/set_slice_name.h"
|
||||
|
||||
#include "doc/document.h"
|
||||
#include "doc/document_event.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
SetSliceName::SetSliceName(Slice* slice, const std::string& name)
|
||||
: WithSlice(slice)
|
||||
, m_oldName(slice->name())
|
||||
, m_newName(name)
|
||||
{
|
||||
}
|
||||
|
||||
void SetSliceName::onExecute()
|
||||
{
|
||||
slice()->setName(m_newName);
|
||||
slice()->incrementVersion();
|
||||
}
|
||||
|
||||
void SetSliceName::onUndo()
|
||||
{
|
||||
slice()->setName(m_oldName);
|
||||
slice()->incrementVersion();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
40
src/app/cmd/set_slice_name.h
Normal file
40
src/app/cmd/set_slice_name.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_SET_SLICE_NAME_H_INCLUDED
|
||||
#define APP_CMD_SET_SLICE_NAME_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_slice.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class SetSliceName : public Cmd
|
||||
, public WithSlice {
|
||||
public:
|
||||
SetSliceName(Slice* slice, const std::string& name);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_oldName;
|
||||
std::string m_newName;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
31
src/app/cmd/with_slice.cpp
Normal file
31
src/app/cmd/with_slice.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// 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/cmd/with_slice.h"
|
||||
|
||||
#include "doc/slice.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
WithSlice::WithSlice(Slice* slice)
|
||||
: m_sliceId(slice->id())
|
||||
{
|
||||
}
|
||||
|
||||
Slice* WithSlice::slice()
|
||||
{
|
||||
return get<Slice>(m_sliceId);
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
33
src/app/cmd/with_slice.h
Normal file
33
src/app/cmd/with_slice.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_WITH_SLICE_H_INCLUDED
|
||||
#define APP_CMD_WITH_SLICE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/object_id.h"
|
||||
|
||||
namespace doc {
|
||||
class Slice;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class WithSlice {
|
||||
public:
|
||||
WithSlice(Slice* slice);
|
||||
Slice* slice();
|
||||
|
||||
private:
|
||||
ObjectId m_sliceId;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -124,7 +124,9 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
if (m_filename.empty())
|
||||
return;
|
||||
|
||||
int flags = (m_repeatCheckbox ? FILE_LOAD_SEQUENCE_ASK_CHECKBOX: 0);
|
||||
int flags =
|
||||
FILE_LOAD_DATA_FILE |
|
||||
(m_repeatCheckbox ? FILE_LOAD_SEQUENCE_ASK_CHECKBOX: 0);
|
||||
|
||||
switch (m_seqDecision) {
|
||||
case SequenceDecision::Ask:
|
||||
|
114
src/app/commands/cmd_remove_slice.cpp
Normal file
114
src/app/commands/cmd_remove_slice.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
// 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/app.h"
|
||||
#include "app/cmd/remove_slice.h"
|
||||
#include "app/cmd/set_slice_key.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/document_api.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/transaction.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "ui/alert.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class RemoveSliceCommand : public Command {
|
||||
public:
|
||||
RemoveSliceCommand();
|
||||
Command* clone() const override { return new RemoveSliceCommand(*this); }
|
||||
|
||||
protected:
|
||||
void onLoadParams(const Params& params) override;
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
|
||||
private:
|
||||
std::string m_sliceName;
|
||||
ObjectId m_sliceId;
|
||||
};
|
||||
|
||||
RemoveSliceCommand::RemoveSliceCommand()
|
||||
: Command("RemoveSlice",
|
||||
"Remove Slice",
|
||||
CmdRecordableFlag)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveSliceCommand::onLoadParams(const Params& params)
|
||||
{
|
||||
m_sliceName = params.get("name");
|
||||
|
||||
std::string id = params.get("id");
|
||||
if (!id.empty())
|
||||
m_sliceId = ObjectId(base::convert_to<doc::ObjectId>(id));
|
||||
else
|
||||
m_sliceId = NullId;
|
||||
}
|
||||
|
||||
bool RemoveSliceCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
||||
ContextFlags::HasActiveSprite |
|
||||
ContextFlags::HasActiveLayer);
|
||||
}
|
||||
|
||||
void RemoveSliceCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
const Sprite* sprite = reader.sprite();
|
||||
frame_t frame = reader.frame();
|
||||
const Slice* foundSlice = nullptr;
|
||||
|
||||
if (!m_sliceName.empty())
|
||||
foundSlice = sprite->slices().getByName(m_sliceName);
|
||||
else if (m_sliceId != NullId)
|
||||
foundSlice = sprite->slices().getById(m_sliceId);
|
||||
|
||||
if (!foundSlice)
|
||||
return;
|
||||
|
||||
std::string sliceName = foundSlice->name();
|
||||
|
||||
{
|
||||
ContextWriter writer(reader, 500);
|
||||
Document* document(writer.document());
|
||||
Sprite* sprite(writer.sprite());
|
||||
Transaction transaction(writer.context(), "Remove Slice");
|
||||
Slice* slice = const_cast<Slice*>(foundSlice);
|
||||
|
||||
if (slice->size() > 1) {
|
||||
transaction.execute(new cmd::SetSliceKey(slice, frame, SliceKey()));
|
||||
}
|
||||
else {
|
||||
transaction.execute(new cmd::RemoveSlice(sprite, slice));
|
||||
}
|
||||
transaction.commit();
|
||||
document->notifyGeneralUpdate();
|
||||
}
|
||||
|
||||
StatusBar::instance()->invalidate();
|
||||
if (!sliceName.empty())
|
||||
StatusBar::instance()->showTip(1000, "Slice '%s' removed", sliceName.c_str());
|
||||
else
|
||||
StatusBar::instance()->showTip(1000, "Slice removed");
|
||||
}
|
||||
|
||||
Command* CommandFactory::createRemoveSliceCommand()
|
||||
{
|
||||
return new RemoveSliceCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -171,6 +171,28 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
class ShowSlicesCommand : public Command {
|
||||
public:
|
||||
ShowSlicesCommand()
|
||||
: Command("ShowSlices",
|
||||
"Show Slices",
|
||||
CmdUIOnlyFlag) {
|
||||
}
|
||||
|
||||
Command* clone() const override { return new ShowSlicesCommand(*this); }
|
||||
|
||||
protected:
|
||||
bool onChecked(Context* ctx) override {
|
||||
DocumentPreferences& docPref = Preferences::instance().document(ctx->activeDocument());
|
||||
return docPref.show.slices();
|
||||
}
|
||||
|
||||
void onExecute(Context* ctx) override {
|
||||
DocumentPreferences& docPref = Preferences::instance().document(ctx->activeDocument());
|
||||
docPref.show.slices(!docPref.show.slices());
|
||||
}
|
||||
};
|
||||
|
||||
Command* CommandFactory::createShowExtrasCommand()
|
||||
{
|
||||
return new ShowExtrasCommand;
|
||||
@ -201,4 +223,9 @@ Command* CommandFactory::createShowBrushPreviewCommand()
|
||||
return new ShowBrushPreviewCommand;
|
||||
}
|
||||
|
||||
Command* CommandFactory::createShowSlicesCommand()
|
||||
{
|
||||
return new ShowSlicesCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
119
src/app/commands/cmd_slice_properties.cpp
Normal file
119
src/app/commands/cmd_slice_properties.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
// 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/cmd/set_slice_key.h"
|
||||
#include "app/cmd/set_slice_name.h"
|
||||
#include "app/cmd/set_user_data.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/slice_window.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
class SlicePropertiesCommand : public Command {
|
||||
public:
|
||||
SlicePropertiesCommand();
|
||||
Command* clone() const override { return new SlicePropertiesCommand(*this); }
|
||||
|
||||
protected:
|
||||
void onLoadParams(const Params& params) override;
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
|
||||
private:
|
||||
std::string m_sliceName;
|
||||
ObjectId m_sliceId;
|
||||
};
|
||||
|
||||
SlicePropertiesCommand::SlicePropertiesCommand()
|
||||
: Command("SliceProperties",
|
||||
"Slice Properties",
|
||||
CmdUIOnlyFlag)
|
||||
, m_sliceId(NullId)
|
||||
{
|
||||
}
|
||||
|
||||
void SlicePropertiesCommand::onLoadParams(const Params& params)
|
||||
{
|
||||
m_sliceName = params.get("name");
|
||||
|
||||
std::string id = params.get("id");
|
||||
if (!id.empty())
|
||||
m_sliceId = ObjectId(base::convert_to<doc::ObjectId>(id));
|
||||
else
|
||||
m_sliceId = NullId;
|
||||
}
|
||||
|
||||
bool SlicePropertiesCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
|
||||
}
|
||||
|
||||
void SlicePropertiesCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
const Sprite* sprite = reader.sprite();
|
||||
frame_t frame = reader.frame();
|
||||
const Slice* foundSlice = nullptr;
|
||||
|
||||
if (!m_sliceName.empty())
|
||||
foundSlice = sprite->slices().getByName(m_sliceName);
|
||||
else if (m_sliceId != NullId)
|
||||
foundSlice = sprite->slices().getById(m_sliceId);
|
||||
|
||||
if (!foundSlice)
|
||||
return;
|
||||
|
||||
const doc::SliceKey* key = foundSlice->getByFrame(frame);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
SliceWindow window(sprite, foundSlice, frame);
|
||||
if (!window.show())
|
||||
return;
|
||||
|
||||
{
|
||||
ContextWriter writer(reader, 500);
|
||||
Transaction transaction(writer.context(), "Slice Properties");
|
||||
Slice* slice = const_cast<Slice*>(foundSlice);
|
||||
|
||||
std::string name = window.nameValue();
|
||||
|
||||
if (slice->name() != name)
|
||||
transaction.execute(new cmd::SetSliceName(slice, name));
|
||||
|
||||
if (slice->userData() != window.userDataValue())
|
||||
transaction.execute(new cmd::SetUserData(slice, window.userDataValue()));
|
||||
|
||||
if (key->bounds() != window.boundsValue() ||
|
||||
key->center() != window.centerValue()) {
|
||||
SliceKey newKey = *key;
|
||||
newKey.setBounds(window.boundsValue());
|
||||
newKey.setCenter(window.centerValue());
|
||||
transaction.execute(new cmd::SetSliceKey(slice, frame, newKey));
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
writer.document()->notifyGeneralUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
Command* CommandFactory::createSlicePropertiesCommand()
|
||||
{
|
||||
return new SlicePropertiesCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -98,6 +98,7 @@ FOR_EACH_COMMAND(Refresh)
|
||||
FOR_EACH_COMMAND(RemoveFrame)
|
||||
FOR_EACH_COMMAND(RemoveFrameTag)
|
||||
FOR_EACH_COMMAND(RemoveLayer)
|
||||
FOR_EACH_COMMAND(RemoveSlice)
|
||||
FOR_EACH_COMMAND(RepeatLastExport)
|
||||
FOR_EACH_COMMAND(ReplaceColor)
|
||||
FOR_EACH_COMMAND(ReselectMask)
|
||||
@ -125,6 +126,8 @@ FOR_EACH_COMMAND(ShowLayerEdges)
|
||||
FOR_EACH_COMMAND(ShowOnionSkin)
|
||||
FOR_EACH_COMMAND(ShowPixelGrid)
|
||||
FOR_EACH_COMMAND(ShowSelectionEdges)
|
||||
FOR_EACH_COMMAND(ShowSlices)
|
||||
FOR_EACH_COMMAND(SliceProperties)
|
||||
FOR_EACH_COMMAND(SnapToGrid)
|
||||
FOR_EACH_COMMAND(SpriteProperties)
|
||||
FOR_EACH_COMMAND(SpriteSize)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -31,6 +31,8 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/slice_io.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/string_io.h"
|
||||
#include "doc/subobjects_io.h"
|
||||
@ -286,6 +288,17 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Read slices
|
||||
int nslices = read32(s);
|
||||
if (nslices >= 1 && nslices < 0xffffff) {
|
||||
for (int i = 0; i < nslices; ++i) {
|
||||
ObjectId sliceId = read32(s);
|
||||
Slice* slice = loadObject<Slice*>("slice", sliceId, &Reader::readSlice);
|
||||
if (slice)
|
||||
spr->slices().add(slice);
|
||||
}
|
||||
}
|
||||
|
||||
return spr.release();
|
||||
}
|
||||
|
||||
@ -350,6 +363,10 @@ private:
|
||||
return read_frame_tag(s, false);
|
||||
}
|
||||
|
||||
Slice* readSlice(std::ifstream& s) {
|
||||
return read_slice(s, false);
|
||||
}
|
||||
|
||||
// Fix issues that the restoration process could produce.
|
||||
void fixUndetectedDocumentIssues(app::Document* doc) {
|
||||
Sprite* spr = doc->sprite();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -30,6 +30,8 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/slice_io.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/string_io.h"
|
||||
|
||||
@ -72,6 +74,10 @@ public:
|
||||
if (!saveObject("frtag", frtag, &Writer::writeFrameTag))
|
||||
return false;
|
||||
|
||||
for (Slice* slice : spr->slices())
|
||||
if (!saveObject("slice", slice, &Writer::writeSlice))
|
||||
return false;
|
||||
|
||||
// Get all layers (visible, hidden, subchildren, etc.)
|
||||
LayerList layers = spr->allLayers();
|
||||
|
||||
@ -155,6 +161,11 @@ private:
|
||||
for (FrameTag* frtag : spr->frameTags())
|
||||
write32(s, frtag->id());
|
||||
|
||||
// IDs of all slices
|
||||
write32(s, spr->slices().size());
|
||||
for (const Slice* slice : spr->slices())
|
||||
write32(s, slice->id());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -220,6 +231,11 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeSlice(std::ofstream& s, Slice* slice) {
|
||||
write_slice(s, slice);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool saveObject(const char* prefix, T* obj, bool (Writer::*writeMember)(std::ofstream&, T*)) {
|
||||
if (isCanceled())
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -39,6 +39,7 @@
|
||||
#define ASE_FILE_CHUNK_FRAME_TAGS 0x2018
|
||||
#define ASE_FILE_CHUNK_PALETTE 0x2019
|
||||
#define ASE_FILE_CHUNK_USER_DATA 0x2020
|
||||
#define ASE_FILE_CHUNK_SLICES 0x2021
|
||||
|
||||
#define ASE_FILE_LAYER_IMAGE 0
|
||||
#define ASE_FILE_LAYER_GROUP 1
|
||||
@ -54,6 +55,8 @@
|
||||
|
||||
#define ASE_CEL_EXTRA_FLAG_PRECISE_BOUNDS 1
|
||||
|
||||
#define ASE_SLICE_FLAG_HAS_CENTER_BOUNDS 1
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace base;
|
||||
@ -139,6 +142,9 @@ static void ase_file_write_mask_chunk(FILE* f, ASE_FrameHeader* frame_header, Ma
|
||||
static void ase_file_read_frame_tags_chunk(FILE* f, FrameTags* frameTags);
|
||||
static void ase_file_write_frame_tags_chunk(FILE* f, ASE_FrameHeader* frame_header, const FrameTags* frameTags,
|
||||
const frame_t fromFrame, const frame_t toFrame);
|
||||
static void ase_file_read_slices_chunk(FILE* f, Slices* slices);
|
||||
static void ase_file_write_slices_chunk(FILE* f, ASE_FrameHeader* frame_header, const Slices* slices,
|
||||
const frame_t fromFrame, const frame_t toFrame);
|
||||
static void ase_file_read_user_data_chunk(FILE* f, UserData* userData);
|
||||
static void ase_file_write_user_data_chunk(FILE* f, ASE_FrameHeader* frame_header, const UserData* userData);
|
||||
static bool ase_has_groups(LayerGroup* group);
|
||||
@ -325,6 +331,10 @@ bool AseFormat::onLoad(FileOp* fop)
|
||||
ase_file_read_frame_tags_chunk(f, &sprite->frameTags());
|
||||
break;
|
||||
|
||||
case ASE_FILE_CHUNK_SLICES:
|
||||
ase_file_read_slices_chunk(f, &sprite->slices());
|
||||
break;
|
||||
|
||||
case ASE_FILE_CHUNK_USER_DATA: {
|
||||
UserData userData;
|
||||
ase_file_read_user_data_chunk(f, &userData);
|
||||
@ -455,6 +465,13 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
ase_file_write_frame_tags_chunk(f, &frame_header, &sprite->frameTags(),
|
||||
fop->roi().fromFrame(),
|
||||
fop->roi().toFrame());
|
||||
|
||||
// Writer slice chunks
|
||||
if (sprite->slices().size() > 0)
|
||||
ase_file_write_slices_chunk(f, &frame_header,
|
||||
&sprite->slices(),
|
||||
fop->roi().fromFrame(),
|
||||
fop->roi().toFrame());
|
||||
}
|
||||
|
||||
// Write cel chunks
|
||||
@ -1593,7 +1610,7 @@ static void ase_file_write_frame_tags_chunk(FILE* f, ASE_FrameHeader* frame_head
|
||||
|
||||
int tags = 0;
|
||||
for (const FrameTag* tag : *frameTags) {
|
||||
// Skip tags that are outside the given ROI
|
||||
// Skip tags that are outside of the given ROI
|
||||
if (tag->fromFrame() > toFrame ||
|
||||
tag->toFrame() < fromFrame)
|
||||
continue;
|
||||
@ -1668,6 +1685,120 @@ static void ase_file_write_user_data_chunk(FILE* f, ASE_FrameHeader* frame_heade
|
||||
}
|
||||
}
|
||||
|
||||
static void ase_file_read_slices_chunk(FILE* f, Slices* slices)
|
||||
{
|
||||
size_t nslices = fgetl(f); // Number of slices
|
||||
fgetl(f); // 8 bytes reserved
|
||||
fgetl(f);
|
||||
|
||||
for (size_t i=0; i<nslices; ++i) {
|
||||
size_t nkeys = fgetl(f); // Number of keys
|
||||
int flags = fgetl(f); // Flags
|
||||
fgetl(f); // 4 bytes reserved
|
||||
std::string name = ase_file_read_string(f); // Name
|
||||
|
||||
base::UniquePtr<Slice> slice(new Slice);
|
||||
slice->setName(name);
|
||||
|
||||
// For each key
|
||||
for (size_t j=0; j<nkeys; ++j) {
|
||||
frame_t frame = fgetl(f);
|
||||
gfx::Rect bounds, center;
|
||||
bounds.x = fgetl(f);
|
||||
bounds.y = fgetl(f);
|
||||
bounds.w = fgetl(f);
|
||||
bounds.h = fgetl(f);
|
||||
|
||||
if (flags & ASE_SLICE_FLAG_HAS_CENTER_BOUNDS) {
|
||||
center.x = fgetl(f);
|
||||
center.y = fgetl(f);
|
||||
center.w = fgetl(f);
|
||||
center.h = fgetl(f);
|
||||
}
|
||||
|
||||
slice->insert(frame, SliceKey(bounds, center));
|
||||
}
|
||||
|
||||
slices->add(slice);
|
||||
slice.release();
|
||||
}
|
||||
}
|
||||
|
||||
static void ase_file_write_slices_chunk(FILE* f, ASE_FrameHeader* frame_header,
|
||||
const Slices* slices,
|
||||
const frame_t fromFrame,
|
||||
const frame_t toFrame)
|
||||
{
|
||||
ChunkWriter chunk(f, frame_header, ASE_FILE_CHUNK_SLICES);
|
||||
|
||||
size_t nslices = 0;
|
||||
for (Slice* slice : *slices) {
|
||||
// Skip slices that are outside of the given ROI
|
||||
if (slice->range(fromFrame, toFrame).empty())
|
||||
continue;
|
||||
|
||||
++nslices;
|
||||
}
|
||||
|
||||
fputl(nslices, f);
|
||||
fputl(0, f); // 8 reserved bytes
|
||||
fputl(0, f);
|
||||
|
||||
for (Slice* slice : *slices) {
|
||||
// Skip slices that are outside of the given ROI
|
||||
auto range = slice->range(fromFrame, toFrame);
|
||||
if (range.empty())
|
||||
continue;
|
||||
|
||||
int flags = 0;
|
||||
for (auto key : range) {
|
||||
if (key && !key->center().isEmpty()) {
|
||||
flags |= ASE_SLICE_FLAG_HAS_CENTER_BOUNDS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fputl(range.countKeys(), f); // number of keys
|
||||
fputl(flags, f); // flags
|
||||
fputl(0, f); // 4 bytes reserved
|
||||
ase_file_write_string(f, slice->name()); // slice name
|
||||
|
||||
frame_t frame = fromFrame;
|
||||
const SliceKey* oldKey = nullptr;
|
||||
for (auto key : range) {
|
||||
if (frame == fromFrame || key != oldKey) {
|
||||
fputl(frame, f);
|
||||
fputl(key ? key->bounds().x: 0, f);
|
||||
fputl(key ? key->bounds().y: 0, f);
|
||||
fputl(key ? key->bounds().w: 0, f);
|
||||
fputl(key ? key->bounds().h: 0, f);
|
||||
|
||||
if (flags & ASE_SLICE_FLAG_HAS_CENTER_BOUNDS) {
|
||||
if (key && !key->center().isEmpty()) {
|
||||
fputl(key->center().x, f);
|
||||
fputl(key->center().y, f);
|
||||
fputl(key->center().w, f);
|
||||
fputl(key->center().h, f);
|
||||
}
|
||||
else {
|
||||
fputl(0, f);
|
||||
fputl(0, f);
|
||||
fputl(key ? key->bounds().w: 0, f);
|
||||
fputl(key ? key->bounds().h: 0, f);
|
||||
}
|
||||
}
|
||||
|
||||
oldKey = key;
|
||||
}
|
||||
++frame;
|
||||
}
|
||||
|
||||
--nslices;
|
||||
}
|
||||
|
||||
ASSERT(nslices == 0);
|
||||
}
|
||||
|
||||
static bool ase_has_groups(LayerGroup* group)
|
||||
{
|
||||
for (Layer* child : group->layers()) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -21,12 +21,14 @@
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/xml_document.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/mutex.h"
|
||||
#include "base/scoped_lock.h"
|
||||
#include "base/shared_ptr.h"
|
||||
#include "base/string.h"
|
||||
#include "doc/doc.h"
|
||||
#include "doc/slice.h" // TODO add this header file in doc.h
|
||||
#include "docio/detect_format.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
@ -41,6 +43,36 @@ namespace app {
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace {
|
||||
|
||||
void updateXmlPartFromSliceKey(const SliceKey* key, TiXmlElement* xmlPart)
|
||||
{
|
||||
xmlPart->SetAttribute("x", key->bounds().x);
|
||||
xmlPart->SetAttribute("y", key->bounds().y);
|
||||
if (key->center().isEmpty()) {
|
||||
xmlPart->SetAttribute("w", key->bounds().w);
|
||||
xmlPart->SetAttribute("h", key->bounds().h);
|
||||
if (xmlPart->Attribute("w1")) xmlPart->RemoveAttribute("w1");
|
||||
if (xmlPart->Attribute("w2")) xmlPart->RemoveAttribute("w2");
|
||||
if (xmlPart->Attribute("w3")) xmlPart->RemoveAttribute("w3");
|
||||
if (xmlPart->Attribute("h1")) xmlPart->RemoveAttribute("h1");
|
||||
if (xmlPart->Attribute("h2")) xmlPart->RemoveAttribute("h2");
|
||||
if (xmlPart->Attribute("h3")) xmlPart->RemoveAttribute("h3");
|
||||
}
|
||||
else {
|
||||
xmlPart->SetAttribute("w1", key->center().x);
|
||||
xmlPart->SetAttribute("w2", key->center().w);
|
||||
xmlPart->SetAttribute("w3", key->bounds().w - key->center().x2());
|
||||
xmlPart->SetAttribute("h1", key->center().y);
|
||||
xmlPart->SetAttribute("h2", key->center().h);
|
||||
xmlPart->SetAttribute("h3", key->bounds().h - key->center().y2());
|
||||
if (xmlPart->Attribute("w")) xmlPart->RemoveAttribute("w");
|
||||
if (xmlPart->Attribute("h")) xmlPart->RemoveAttribute("h");
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::string get_readable_extensions()
|
||||
{
|
||||
std::string buf;
|
||||
@ -285,13 +317,21 @@ FileOp* FileOp::createLoadDocumentOperation(Context* context, const char* filena
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
fop->m_filename = filename;
|
||||
}
|
||||
|
||||
/* load just one frame */
|
||||
// Load just one frame
|
||||
if (flags & FILE_LOAD_ONE_FRAME)
|
||||
fop->m_oneframe = true;
|
||||
|
||||
// Does data file exist?
|
||||
if (flags & FILE_LOAD_DATA_FILE) {
|
||||
std::string dataFilename = base::replace_extension(filename, "aseprite-data");
|
||||
if (base::is_file(dataFilename))
|
||||
fop->m_dataFilename = dataFilename;
|
||||
}
|
||||
|
||||
done:;
|
||||
return fop.release();
|
||||
}
|
||||
@ -520,6 +560,11 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
fop->m_document->setFormatOptions(opts);
|
||||
}
|
||||
|
||||
// Does data file exist?
|
||||
std::string dataFilename = base::replace_extension(filename, "aseprite-data");
|
||||
if (base::is_file(dataFilename))
|
||||
fop->m_dataFilename = dataFilename;
|
||||
|
||||
return fop.release();
|
||||
}
|
||||
|
||||
@ -650,9 +695,17 @@ void FileOp::operate(IFileOpProgress* progress)
|
||||
// Direct load from one file.
|
||||
else {
|
||||
// Call the "load" procedure.
|
||||
if (!m_format->load(this))
|
||||
if (!m_format->load(this)) {
|
||||
setError("Error loading sprite from file \"%s\"\n",
|
||||
m_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Load special data from .aseprite-data file
|
||||
if (m_document &&
|
||||
m_document->sprite() &&
|
||||
!m_dataFilename.empty()) {
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
// Save //////////////////////////////////////////////////////////////////////
|
||||
@ -707,9 +760,18 @@ void FileOp::operate(IFileOpProgress* progress)
|
||||
// Direct save to a file.
|
||||
else {
|
||||
// Call the "save" procedure.
|
||||
if (!m_format->save(this))
|
||||
if (!m_format->save(this)) {
|
||||
setError("Error saving the sprite in the file \"%s\"\n",
|
||||
m_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Save special data from .aseprite-data file
|
||||
if (m_document &&
|
||||
m_document->sprite() &&
|
||||
!hasError() &&
|
||||
!m_dataFilename.empty()) {
|
||||
saveData();
|
||||
}
|
||||
#else
|
||||
setError(
|
||||
@ -896,7 +958,7 @@ Image* FileOp::sequenceImage(PixelFormat pixelFormat, int w, int h)
|
||||
|
||||
void FileOp::setError(const char *format, ...)
|
||||
{
|
||||
char buf_error[4096];
|
||||
char buf_error[4096]; // TODO possible stack overflow
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vsnprintf(buf_error, sizeof(buf_error), format, ap);
|
||||
@ -995,4 +1057,141 @@ void FileOp::prepareForSequence()
|
||||
m_formatOptions.reset();
|
||||
}
|
||||
|
||||
void FileOp::loadData()
|
||||
{
|
||||
try {
|
||||
XmlDocumentRef xmlDoc = open_xml(m_dataFilename);
|
||||
TiXmlHandle handle(xmlDoc.get());
|
||||
|
||||
TiXmlElement* xmlSlices = handle
|
||||
.FirstChild("sprite")
|
||||
.FirstChild("slices").ToElement();
|
||||
|
||||
// Update theme.xml file
|
||||
if (xmlSlices &&
|
||||
xmlSlices->Attribute("theme")) {
|
||||
std::string themeFileName = xmlSlices->Attribute("theme");
|
||||
|
||||
// Open theme XML file
|
||||
XmlDocumentRef xmlThemeDoc = open_xml(
|
||||
base::join_path(base::get_file_path(m_dataFilename), themeFileName));
|
||||
TiXmlHandle themeHandle(xmlThemeDoc.get());
|
||||
for (TiXmlElement* xmlPart = themeHandle
|
||||
.FirstChild("theme")
|
||||
.FirstChild("parts")
|
||||
.FirstChild("part").ToElement();
|
||||
xmlPart;
|
||||
xmlPart=xmlPart->NextSiblingElement()) {
|
||||
const char* partId = xmlPart->Attribute("id");
|
||||
if (!partId)
|
||||
continue;
|
||||
|
||||
auto slice = new doc::Slice();
|
||||
slice->setName(partId);
|
||||
|
||||
doc::SliceKey key;
|
||||
|
||||
int x = strtol(xmlPart->Attribute("x"), NULL, 10);
|
||||
int y = strtol(xmlPart->Attribute("y"), NULL, 10);
|
||||
|
||||
if (xmlPart->Attribute("w1")) {
|
||||
int w1 = xmlPart->Attribute("w1") ? strtol(xmlPart->Attribute("w1"), NULL, 10): 0;
|
||||
int w2 = xmlPart->Attribute("w2") ? strtol(xmlPart->Attribute("w2"), NULL, 10): 0;
|
||||
int w3 = xmlPart->Attribute("w3") ? strtol(xmlPart->Attribute("w3"), NULL, 10): 0;
|
||||
int h1 = xmlPart->Attribute("h1") ? strtol(xmlPart->Attribute("h1"), NULL, 10): 0;
|
||||
int h2 = xmlPart->Attribute("h2") ? strtol(xmlPart->Attribute("h2"), NULL, 10): 0;
|
||||
int h3 = xmlPart->Attribute("h3") ? strtol(xmlPart->Attribute("h3"), NULL, 10): 0;
|
||||
|
||||
key.setBounds(gfx::Rect(x, y, w1+w2+w3, h1+h2+h3));
|
||||
key.setCenter(gfx::Rect(w1, h1, w2, h2));
|
||||
}
|
||||
else if (xmlPart->Attribute("w")) {
|
||||
int w = xmlPart->Attribute("w") ? strtol(xmlPart->Attribute("w"), NULL, 10): 0;
|
||||
int h = xmlPart->Attribute("h") ? strtol(xmlPart->Attribute("h"), NULL, 10): 0;
|
||||
key.setBounds(gfx::Rect(x, y, w, h));
|
||||
}
|
||||
|
||||
slice->insert(0, key);
|
||||
m_document->sprite()->slices().add(slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
setError("Error loading data file: %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void FileOp::saveData()
|
||||
{
|
||||
try {
|
||||
XmlDocumentRef xmlDoc = open_xml(m_dataFilename);
|
||||
TiXmlHandle handle(xmlDoc.get());
|
||||
|
||||
TiXmlElement* xmlSlices = handle
|
||||
.FirstChild("sprite")
|
||||
.FirstChild("slices").ToElement();
|
||||
|
||||
if (xmlSlices &&
|
||||
xmlSlices->Attribute("theme")) {
|
||||
// Open theme XML file
|
||||
std::string themeFileName = base::join_path(
|
||||
base::get_file_path(m_dataFilename), xmlSlices->Attribute("theme"));
|
||||
XmlDocumentRef xmlThemeDoc = open_xml(themeFileName);
|
||||
|
||||
TiXmlHandle themeHandle(xmlThemeDoc.get());
|
||||
TiXmlElement* xmlNext = nullptr;
|
||||
std::set<std::string> existent;
|
||||
|
||||
TiXmlElement* xmlParts =
|
||||
themeHandle
|
||||
.FirstChild("theme")
|
||||
.FirstChild("parts").ToElement();
|
||||
|
||||
for (TiXmlElement* xmlPart=(TiXmlElement*)xmlParts->FirstChild("part");
|
||||
xmlPart;
|
||||
xmlPart=xmlNext) {
|
||||
xmlNext = xmlPart->NextSiblingElement();
|
||||
|
||||
const char* partId = xmlPart->Attribute("id");
|
||||
if (!partId)
|
||||
continue;
|
||||
|
||||
bool found = false;
|
||||
for (auto slice : m_document->sprite()->slices()) {
|
||||
const SliceKey* key;
|
||||
if ((slice->name() == partId) &&
|
||||
(key = slice->getByFrame(0))) {
|
||||
existent.insert(slice->name());
|
||||
updateXmlPartFromSliceKey(key, xmlPart);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete this <part> element (as the slice was removed)
|
||||
if (!found)
|
||||
xmlPart->Parent()->RemoveChild(xmlPart);
|
||||
}
|
||||
|
||||
for (auto slice : m_document->sprite()->slices()) {
|
||||
const SliceKey* key;
|
||||
if (existent.find(slice->name()) == existent.end() &&
|
||||
(key = slice->getByFrame(0))) {
|
||||
|
||||
TiXmlElement xmlPart("part");
|
||||
xmlPart.SetAttribute("id", slice->name().c_str());
|
||||
updateXmlPartFromSliceKey(key, &xmlPart);
|
||||
|
||||
xmlParts->InsertAfterChild(xmlParts->LastChild(), xmlPart);
|
||||
}
|
||||
}
|
||||
|
||||
save_xml(xmlThemeDoc, themeFileName);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
setError("Error loading data file: %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -25,6 +25,7 @@
|
||||
#define FILE_LOAD_SEQUENCE_ASK_CHECKBOX 0x00000004
|
||||
#define FILE_LOAD_SEQUENCE_YES 0x00000008
|
||||
#define FILE_LOAD_ONE_FRAME 0x00000010
|
||||
#define FILE_LOAD_DATA_FILE 0x00000020
|
||||
|
||||
namespace doc {
|
||||
class Document;
|
||||
@ -173,6 +174,7 @@ namespace app {
|
||||
// releaseDocument() member function)
|
||||
Document* m_document; // Loaded document, or document to be saved.
|
||||
std::string m_filename; // File-name to load/save.
|
||||
std::string m_dataFilename; // File-name for a special XML .aseprite-data where extra sprite data can be stored
|
||||
FileOpROI m_roi;
|
||||
|
||||
// Shared fields between threads.
|
||||
@ -206,6 +208,8 @@ namespace app {
|
||||
} m_seq;
|
||||
|
||||
void prepareForSequence();
|
||||
void loadData();
|
||||
void saveData();
|
||||
};
|
||||
|
||||
// Available extensions for each load/save operation.
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -13,6 +13,7 @@
|
||||
#include "app/document_undo.h"
|
||||
#include "app/tools/pick_ink.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/slice.h"
|
||||
#include "gfx/region.h"
|
||||
|
||||
namespace app {
|
||||
@ -168,16 +169,42 @@ public:
|
||||
|
||||
|
||||
class SliceInk : public Ink {
|
||||
AlgoHLine m_proc;
|
||||
bool m_createSlice;
|
||||
gfx::Rect m_maxBounds;
|
||||
|
||||
public:
|
||||
SliceInk() {
|
||||
m_createSlice = false;
|
||||
}
|
||||
|
||||
Ink* clone() override { return new SliceInk(*this); }
|
||||
|
||||
bool isSlice() const override { return true; }
|
||||
bool needsCelCoordinates() const override { return false; }
|
||||
|
||||
void prepareInk(ToolLoop* loop) override { }
|
||||
void prepareInk(ToolLoop* loop) override {
|
||||
m_proc = get_ink_proc<XorInkProcessing>(loop->sprite()->pixelFormat());
|
||||
}
|
||||
|
||||
void inkHline(int x1, int y, int x2, ToolLoop* loop) override {
|
||||
// TODO show the selection-preview with a XOR color or something like that
|
||||
draw_hline(loop->getDstImage(), x1, y, x2, loop->getPrimaryColor());
|
||||
if (m_createSlice)
|
||||
m_maxBounds |= gfx::Rect(x1, y, x2-x1+1, 1);
|
||||
else
|
||||
(*m_proc)(x1, y, x2, loop);
|
||||
}
|
||||
|
||||
void setFinalStep(ToolLoop* loop, bool state) override {
|
||||
m_createSlice = state;
|
||||
if (state) {
|
||||
m_maxBounds = gfx::Rect(0, 0, 0, 0);
|
||||
}
|
||||
else if (loop->getMouseButton() == ToolLoop::Left) {
|
||||
Slice* slice = new Slice;
|
||||
SliceKey key(m_maxBounds);
|
||||
slice->insert(loop->getFrame(), key);
|
||||
loop->addSlice(slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -345,7 +372,6 @@ public:
|
||||
|
||||
m_maxBounds |= gfx::Rect(x1, y, x2-x1+1, 1);
|
||||
}
|
||||
// TODO show the selection-preview with a XOR color or something like that
|
||||
else {
|
||||
(*m_proc)(x1, y, x2, loop);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -26,6 +26,7 @@ namespace doc {
|
||||
class Mask;
|
||||
class Remap;
|
||||
class RgbMap;
|
||||
class Slice;
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
@ -117,6 +118,9 @@ namespace app {
|
||||
virtual Mask* getMask() = 0;
|
||||
virtual void setMask(Mask* newMask) = 0;
|
||||
|
||||
// Adds a new slice (only for slice ink)
|
||||
virtual void addSlice(doc::Slice* newSlice) = 0;
|
||||
|
||||
// Gets mask X,Y origin coordinates
|
||||
virtual gfx::Point getMaskOrigin() = 0;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -109,7 +109,9 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
|
||||
|
||||
bool res = m_toolLoop->getController()->releaseButton(m_stroke, spritePoint);
|
||||
|
||||
if (!res && (m_toolLoop->getInk()->isSelection() || m_toolLoop->getFilled())) {
|
||||
if (!res && (m_toolLoop->getInk()->isSelection() ||
|
||||
m_toolLoop->getInk()->isSlice() ||
|
||||
m_toolLoop->getFilled())) {
|
||||
m_toolLoop->getInk()->setFinalStep(m_toolLoop, true);
|
||||
doLoopStep(true);
|
||||
m_toolLoop->getInk()->setFinalStep(m_toolLoop, false);
|
||||
|
@ -53,9 +53,6 @@ bool AppMenuItem::onProcessMessage(Message* msg)
|
||||
case kCloseMessage:
|
||||
// disable the menu (the keyboard shortcuts are processed by "manager_msg_proc")
|
||||
setEnabled(false);
|
||||
|
||||
if (!s_contextParams.empty())
|
||||
s_contextParams.clear();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "doc/doc.h"
|
||||
#include "doc/document_event.h"
|
||||
#include "doc/mask_boundaries.h"
|
||||
#include "doc/slice.h"
|
||||
#include "she/surface.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/ui.h"
|
||||
@ -703,7 +704,7 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
||||
g->fillRegion(theme->colors.editorFace(), outside);
|
||||
}
|
||||
|
||||
// Grids
|
||||
// Grids & slices
|
||||
{
|
||||
// Clipping
|
||||
gfx::Rect cliprc = editorToScreen(rc).offset(-bounds().origin());
|
||||
@ -743,6 +744,10 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
||||
m_docPref.grid.color(), alpha);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw slices
|
||||
if (m_docPref.show.slices())
|
||||
drawSlices(g);
|
||||
}
|
||||
}
|
||||
|
||||
@ -954,6 +959,51 @@ void Editor::drawGrid(Graphics* g, const gfx::Rect& spriteBounds, const Rect& gr
|
||||
g->drawVLine(grid_color, c, y1, spriteBounds.h);
|
||||
}
|
||||
|
||||
void Editor::drawSlices(ui::Graphics* g)
|
||||
{
|
||||
if ((m_flags & kShowSlices) == 0)
|
||||
return;
|
||||
|
||||
if (!isVisible() || !m_document)
|
||||
return;
|
||||
|
||||
for (auto slice : m_sprite->slices()) {
|
||||
auto key = slice->getByFrame(m_frame);
|
||||
if (!key)
|
||||
continue;
|
||||
|
||||
doc::color_t docColor = slice->userData().color();
|
||||
gfx::Color color = gfx::rgba(doc::rgba_getr(docColor),
|
||||
doc::rgba_getg(docColor),
|
||||
doc::rgba_getb(docColor),
|
||||
doc::rgba_geta(docColor));
|
||||
gfx::Rect out =
|
||||
editorToScreen(key->bounds())
|
||||
.offset(-bounds().origin());
|
||||
|
||||
if (!key->center().isEmpty()) {
|
||||
gfx::Rect in =
|
||||
editorToScreen(gfx::Rect(key->center()).offset(key->bounds().origin()))
|
||||
.offset(-bounds().origin());
|
||||
|
||||
auto in_color = gfx::rgba(gfx::getr(color),
|
||||
gfx::getg(color),
|
||||
gfx::getb(color),
|
||||
doc::rgba_geta(docColor)/4);
|
||||
if (in.y > out.y && in.y < out.y2())
|
||||
g->drawHLine(in_color, out.x, in.y, out.w);
|
||||
if (in.y2() > out.y && in.y2() < out.y2())
|
||||
g->drawHLine(in_color, out.x, in.y2(), out.w);
|
||||
if (in.x > out.x && in.x < out.x2())
|
||||
g->drawVLine(in_color, in.x, out.y, out.h);
|
||||
if (in.x2() > out.x && in.x2() < out.x2())
|
||||
g->drawVLine(in_color, in.x2(), out.y, out.h);
|
||||
}
|
||||
|
||||
g->drawRect(color, out);
|
||||
}
|
||||
}
|
||||
|
||||
void Editor::flashCurrentLayer()
|
||||
{
|
||||
if (!Preferences::instance().experimental.flashLayer())
|
||||
@ -1621,6 +1671,63 @@ bool Editor::isInsideSelection()
|
||||
m_document->mask()->containsPoint(spritePos.x, spritePos.y);
|
||||
}
|
||||
|
||||
EditorHit Editor::calcHit(const gfx::Point& mouseScreenPos)
|
||||
{
|
||||
tools::Ink* ink = getCurrentEditorInk();
|
||||
|
||||
if (ink) {
|
||||
// Check if we can transform slices
|
||||
if (ink->isSlice()) {
|
||||
if (m_docPref.show.slices()) {
|
||||
for (auto slice : m_sprite->slices()) {
|
||||
auto key = slice->getByFrame(m_frame);
|
||||
if (key) {
|
||||
gfx::Rect bounds = editorToScreen(key->bounds());
|
||||
gfx::Rect center = key->center();
|
||||
|
||||
if (bounds.contains(mouseScreenPos) &&
|
||||
!bounds.shrink(5*guiscale()).contains(mouseScreenPos)) {
|
||||
int border =
|
||||
(mouseScreenPos.x <= bounds.x ? LEFT: 0) |
|
||||
(mouseScreenPos.y <= bounds.y ? TOP: 0) |
|
||||
(mouseScreenPos.x >= bounds.x2() ? RIGHT: 0) |
|
||||
(mouseScreenPos.y >= bounds.y2() ? BOTTOM: 0);
|
||||
|
||||
EditorHit hit(EditorHit::SliceBounds);
|
||||
hit.setBorder(border);
|
||||
hit.setSlice(slice);
|
||||
return hit;
|
||||
}
|
||||
else if (!center.isEmpty()) {
|
||||
center = editorToScreen(
|
||||
center.offset(key->bounds().origin()));
|
||||
|
||||
bool horz1 = gfx::Rect(bounds.x, center.y-2*guiscale(), bounds.w, 5*guiscale()).contains(mouseScreenPos);
|
||||
bool horz2 = gfx::Rect(bounds.x, center.y2()-2*guiscale(), bounds.w, 5*guiscale()).contains(mouseScreenPos);
|
||||
bool vert1 = gfx::Rect(center.x-2*guiscale(), bounds.y, 5*guiscale(), bounds.h).contains(mouseScreenPos);
|
||||
bool vert2 = gfx::Rect(center.x2()-2*guiscale(), bounds.y, 5*guiscale(), bounds.h).contains(mouseScreenPos);
|
||||
|
||||
if (horz1 || horz2 || vert1 || vert2) {
|
||||
int border =
|
||||
(horz1 ? TOP: 0) |
|
||||
(horz2 ? BOTTOM: 0) |
|
||||
(vert1 ? LEFT: 0) |
|
||||
(vert2 ? RIGHT: 0);
|
||||
EditorHit hit(EditorHit::SliceCenter);
|
||||
hit.setBorder(border);
|
||||
hit.setSlice(slice);
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EditorHit(EditorHit::None);
|
||||
}
|
||||
|
||||
void Editor::setZoomAndCenterInMouse(const Zoom& zoom,
|
||||
const gfx::Point& mousePos,
|
||||
ZoomBehavior zoomBehavior)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "app/tools/tool_loop_modifiers.h"
|
||||
#include "app/ui/color_source.h"
|
||||
#include "app/ui/editor/brush_preview.h"
|
||||
#include "app/ui/editor/editor_hit.h"
|
||||
#include "app/ui/editor/editor_observers.h"
|
||||
#include "app/ui/editor/editor_state.h"
|
||||
#include "app/ui/editor/editor_states_history.h"
|
||||
@ -74,13 +75,15 @@ namespace app {
|
||||
kShowOutside = 8,
|
||||
kShowDecorators = 16,
|
||||
kShowSymmetryLine = 32,
|
||||
kUseNonactiveLayersOpacityWhenEnabled = 64,
|
||||
kShowSlices = 64,
|
||||
kUseNonactiveLayersOpacityWhenEnabled = 128,
|
||||
kDefaultEditorFlags = (kShowGrid |
|
||||
kShowMask |
|
||||
kShowOnionskin |
|
||||
kShowOutside |
|
||||
kShowDecorators |
|
||||
kShowSymmetryLine |
|
||||
kShowSlices |
|
||||
kUseNonactiveLayersOpacityWhenEnabled)
|
||||
};
|
||||
|
||||
@ -195,6 +198,10 @@ namespace app {
|
||||
// Returns true if the cursor is inside the active mask/selection.
|
||||
bool isInsideSelection();
|
||||
|
||||
// Returns the element that will be modified if the mouse is used
|
||||
// in the given position.
|
||||
EditorHit calcHit(const gfx::Point& mouseScreenPos);
|
||||
|
||||
void setZoomAndCenterInMouse(const render::Zoom& zoom,
|
||||
const gfx::Point& mousePos, ZoomBehavior zoomBehavior);
|
||||
|
||||
@ -271,7 +278,8 @@ namespace app {
|
||||
void drawMaskSafe();
|
||||
void drawMask(ui::Graphics* g);
|
||||
void drawGrid(ui::Graphics* g, const gfx::Rect& spriteBounds, const gfx::Rect& gridBounds,
|
||||
const app::Color& color, int alpha);
|
||||
const app::Color& color, int alpha);
|
||||
void drawSlices(ui::Graphics* g);
|
||||
|
||||
void setCursor(const gfx::Point& mouseScreenPos);
|
||||
|
||||
|
48
src/app/ui/editor/editor_hit.h
Normal file
48
src/app/ui/editor/editor_hit.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_EDITOR_HIT_H_INCLUDED
|
||||
#define APP_UI_EDITOR_HIT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace doc {
|
||||
class Slice;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
// TODO Complete this class to calculate if the mouse hit any Editor
|
||||
// element/decorator
|
||||
class EditorHit {
|
||||
public:
|
||||
enum Type {
|
||||
None,
|
||||
SliceBounds,
|
||||
SliceCenter,
|
||||
};
|
||||
|
||||
EditorHit(Type type)
|
||||
: m_type(type)
|
||||
, m_border(0)
|
||||
, m_slice(nullptr) {
|
||||
}
|
||||
|
||||
Type type() const { return m_type; }
|
||||
int border() const { return m_border; }
|
||||
doc::Slice* slice() const { return m_slice; }
|
||||
|
||||
void setBorder(int border) { m_border = border; }
|
||||
void setSlice(doc::Slice* slice) { m_slice = slice; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
int m_border;
|
||||
doc::Slice* m_slice;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif // APP_UI_EDITOR_HIT_H_INCLUDED
|
@ -82,7 +82,6 @@ MovingCelState::MovingCelState(Editor* editor,
|
||||
, m_celList(collect.celList())
|
||||
, m_celOffset(0.0, 0.0)
|
||||
, m_celScale(1.0, 1.0)
|
||||
, m_canceled(false)
|
||||
, m_hasReference(false)
|
||||
, m_scaled(false)
|
||||
, m_handle(handle)
|
||||
@ -147,8 +146,7 @@ bool MovingCelState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
// If the user didn't cancel the operation...
|
||||
if (!m_canceled) {
|
||||
{
|
||||
ContextWriter writer(m_reader, 1000);
|
||||
Transaction transaction(writer.context(), "Cel Movement", ModifyDocument);
|
||||
DocumentApi api = document->getApi(transaction);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -61,7 +61,6 @@ namespace app {
|
||||
gfx::PointF m_celOffset;
|
||||
gfx::SizeF m_celMainSize;
|
||||
gfx::SizeF m_celScale;
|
||||
bool m_canceled;
|
||||
bool m_maskVisible;
|
||||
bool m_hasReference;
|
||||
bool m_scaled;
|
||||
|
145
src/app/ui/editor/moving_slice_state.cpp
Normal file
145
src/app/ui/editor/moving_slice_state.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
// 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/ui/editor/moving_slice_state.h"
|
||||
|
||||
#include "app/cmd/set_slice_key.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "doc/slice.h"
|
||||
#include "ui/message.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
MovingSliceState::MovingSliceState(Editor* editor, MouseMessage* msg,
|
||||
const EditorHit& hit)
|
||||
: m_hit(hit)
|
||||
{
|
||||
m_mouseStart = editor->screenToEditor(msg->position());
|
||||
|
||||
auto keyPtr = m_hit.slice()->getByFrame(editor->frame());
|
||||
ASSERT(keyPtr);
|
||||
if (keyPtr)
|
||||
m_keyStart = m_key = *keyPtr;
|
||||
|
||||
editor->captureMouse();
|
||||
}
|
||||
|
||||
bool MovingSliceState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
{
|
||||
ContextWriter writer(UIContext::instance(), 1000);
|
||||
Transaction transaction(writer.context(), "Slice Movement", ModifyDocument);
|
||||
|
||||
doc::SliceKey newKey = m_key;
|
||||
m_hit.slice()->insert(editor->frame(), m_keyStart);
|
||||
|
||||
transaction.execute(new cmd::SetSliceKey(m_hit.slice(),
|
||||
editor->frame(),
|
||||
newKey));
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
editor->backToPreviousState();
|
||||
editor->releaseMouse();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MovingSliceState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
gfx::Point newCursorPos = editor->screenToEditor(msg->position());
|
||||
gfx::Point delta = newCursorPos - m_mouseStart;
|
||||
|
||||
m_key = m_keyStart;
|
||||
gfx::Rect rc =
|
||||
(m_hit.type() == EditorHit::SliceCenter ? m_key.center():
|
||||
m_key.bounds());
|
||||
|
||||
if (m_hit.border() & LEFT) {
|
||||
rc.x += delta.x;
|
||||
rc.w -= delta.x;
|
||||
if (rc.w < 1) {
|
||||
rc.x += rc.w-1;
|
||||
rc.w = 1;
|
||||
}
|
||||
}
|
||||
if (m_hit.border() & TOP) {
|
||||
rc.y += delta.y;
|
||||
rc.h -= delta.y;
|
||||
if (rc.h < 1) {
|
||||
rc.y += rc.h-1;
|
||||
rc.h = 1;
|
||||
}
|
||||
}
|
||||
if (m_hit.border() & RIGHT) {
|
||||
rc.w += delta.x;
|
||||
if (rc.w < 1)
|
||||
rc.w = 1;
|
||||
}
|
||||
if (m_hit.border() & BOTTOM) {
|
||||
rc.h += delta.y;
|
||||
if (rc.h < 1)
|
||||
rc.h = 1;
|
||||
}
|
||||
|
||||
if (m_hit.type() == EditorHit::SliceCenter)
|
||||
m_key.setCenter(rc);
|
||||
else
|
||||
m_key.setBounds(rc);
|
||||
|
||||
// Update the slice key
|
||||
m_hit.slice()->insert(editor->frame(), m_key);
|
||||
|
||||
// Redraw the editor.
|
||||
editor->invalidate();
|
||||
|
||||
// Use StandbyState implementation
|
||||
return StandbyState::onMouseMove(editor, msg);
|
||||
}
|
||||
|
||||
bool MovingSliceState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
|
||||
{
|
||||
switch (m_hit.border()) {
|
||||
case TOP | LEFT:
|
||||
editor->showMouseCursor(kSizeNWCursor);
|
||||
break;
|
||||
case TOP:
|
||||
editor->showMouseCursor(kSizeNCursor);
|
||||
break;
|
||||
case TOP | RIGHT:
|
||||
editor->showMouseCursor(kSizeNECursor);
|
||||
break;
|
||||
case LEFT:
|
||||
editor->showMouseCursor(kSizeWCursor);
|
||||
break;
|
||||
case RIGHT:
|
||||
editor->showMouseCursor(kSizeECursor);
|
||||
break;
|
||||
case BOTTOM | LEFT:
|
||||
editor->showMouseCursor(kSizeSWCursor);
|
||||
break;
|
||||
case BOTTOM:
|
||||
editor->showMouseCursor(kSizeSCursor);
|
||||
break;
|
||||
case BOTTOM | RIGHT:
|
||||
editor->showMouseCursor(kSizeSECursor);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace app
|
38
src/app/ui/editor/moving_slice_state.h
Normal file
38
src/app/ui/editor/moving_slice_state.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_EDITOR_MOVING_SLICE_STATE_H_INCLUDED
|
||||
#define APP_UI_EDITOR_MOVING_SLICE_STATE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/editor/editor_hit.h"
|
||||
#include "app/ui/editor/standby_state.h"
|
||||
#include "doc/slice.h"
|
||||
|
||||
namespace app {
|
||||
class Editor;
|
||||
|
||||
class MovingSliceState : public StandbyState {
|
||||
public:
|
||||
MovingSliceState(Editor* editor, ui::MouseMessage* msg,
|
||||
const EditorHit& hit);
|
||||
|
||||
bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
|
||||
bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
|
||||
bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override;
|
||||
|
||||
bool requireBrushPreview() override { return false; }
|
||||
|
||||
private:
|
||||
EditorHit m_hit;
|
||||
gfx::Point m_mouseStart;
|
||||
doc::SliceKey m_keyStart;
|
||||
doc::SliceKey m_key;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -11,6 +11,7 @@
|
||||
#include "app/ui/editor/standby_state.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/app_menus.h"
|
||||
#include "app/color_picker.h"
|
||||
#include "app/commands/cmd_eyedropper.h"
|
||||
#include "app/commands/commands.h"
|
||||
@ -22,6 +23,7 @@
|
||||
#include "app/tools/ink.h"
|
||||
#include "app/tools/pick_ink.h"
|
||||
#include "app/tools/tool.h"
|
||||
#include "app/ui/app_menuitem.h"
|
||||
#include "app/ui/document_view.h"
|
||||
#include "app/ui/editor/drawing_state.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
@ -29,6 +31,7 @@
|
||||
#include "app/ui/editor/handle_type.h"
|
||||
#include "app/ui/editor/moving_cel_state.h"
|
||||
#include "app/ui/editor/moving_pixels_state.h"
|
||||
#include "app/ui/editor/moving_slice_state.h"
|
||||
#include "app/ui/editor/moving_symmetry_state.h"
|
||||
#include "app/ui/editor/pivot_helpers.h"
|
||||
#include "app/ui/editor/pixels_movement.h"
|
||||
@ -46,6 +49,7 @@
|
||||
#include "base/pi.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "fixmath/fixmath.h"
|
||||
#include "gfx/rect.h"
|
||||
@ -217,6 +221,29 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (clickedInk->isSlice()) {
|
||||
EditorHit hit = editor->calcHit(msg->position());
|
||||
switch (hit.type()) {
|
||||
case EditorHit::SliceBounds:
|
||||
case EditorHit::SliceCenter:
|
||||
if (msg->left()) {
|
||||
MovingSliceState* newState = new MovingSliceState(editor, msg, hit);
|
||||
editor->setState(EditorStatePtr(newState));
|
||||
}
|
||||
else {
|
||||
Menu* popupMenu = AppMenus::instance()->getSlicePopupMenu();
|
||||
if (popupMenu) {
|
||||
Params params;
|
||||
params.set("id", base::convert_to<std::string>(hit.slice()->id()).c_str());
|
||||
AppMenuItem::setContextParams(params);
|
||||
popupMenu->showPopup(msg->position());
|
||||
AppMenuItem::setContextParams(Params());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only if the selected tool or quick tool is selection, we give the
|
||||
// possibility to transform/move the selection. In other case,
|
||||
// e.g. when selection is used with right-click mode, the
|
||||
@ -409,8 +436,41 @@ bool StandbyState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
|
||||
return true;
|
||||
}
|
||||
else if (ink->isSlice()) {
|
||||
editor->showBrushPreview(mouseScreenPos);
|
||||
return true;
|
||||
EditorHit hit = editor->calcHit(mouseScreenPos);
|
||||
switch (hit.type()) {
|
||||
case EditorHit::None:
|
||||
// Do nothing, continue
|
||||
break;
|
||||
case EditorHit::SliceBounds:
|
||||
case EditorHit::SliceCenter:
|
||||
switch (hit.border()) {
|
||||
case TOP | LEFT:
|
||||
editor->showMouseCursor(kSizeNWCursor);
|
||||
break;
|
||||
case TOP:
|
||||
editor->showMouseCursor(kSizeNCursor);
|
||||
break;
|
||||
case TOP | RIGHT:
|
||||
editor->showMouseCursor(kSizeNECursor);
|
||||
break;
|
||||
case LEFT:
|
||||
editor->showMouseCursor(kSizeWCursor);
|
||||
break;
|
||||
case RIGHT:
|
||||
editor->showMouseCursor(kSizeECursor);
|
||||
break;
|
||||
case BOTTOM | LEFT:
|
||||
editor->showMouseCursor(kSizeSWCursor);
|
||||
break;
|
||||
case BOTTOM:
|
||||
editor->showMouseCursor(kSizeSCursor);
|
||||
break;
|
||||
case BOTTOM | RIGHT:
|
||||
editor->showMouseCursor(kSizeSECursor);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,6 +550,27 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
|
||||
buf+std::strlen(buf), " :grid: %d %d", col, row);
|
||||
}
|
||||
|
||||
if (editor->docPref().show.slices()) {
|
||||
int count = 0;
|
||||
for (auto slice : editor->document()->sprite()->slices()) {
|
||||
auto key = slice->getByFrame(editor->frame());
|
||||
if (key &&
|
||||
key->bounds().contains(
|
||||
int(std::floor(spritePos.x)),
|
||||
int(std::floor(spritePos.y)))) {
|
||||
if (++count == 3) {
|
||||
sprintf(
|
||||
buf+std::strlen(buf), " :slice: ...");
|
||||
break;
|
||||
}
|
||||
|
||||
sprintf(
|
||||
buf+std::strlen(buf), " :slice: %s",
|
||||
slice->name().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBar::instance()->setStatusText(0, buf);
|
||||
}
|
||||
|
||||
@ -700,6 +781,7 @@ bool StandbyState::Decorator::onSetCursor(tools::Ink* ink, Editor* editor, const
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move symmetry
|
||||
gfx::Rect box1, box2;
|
||||
if (getSymmetryHandles(editor, box1, box2) &&
|
||||
(box1.contains(mouseScreenPos) ||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -11,6 +11,7 @@
|
||||
#include "app/ui/editor/tool_loop_impl.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/cmd/add_slice.h"
|
||||
#include "app/cmd/set_mask.h"
|
||||
#include "app/color.h"
|
||||
#include "app/color_utils.h"
|
||||
@ -415,6 +416,10 @@ public:
|
||||
// Show selection edges
|
||||
m_docPref.show.selectionEdges(true);
|
||||
}
|
||||
// Slice ink
|
||||
else if (getInk()->isSlice()) {
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
m_transaction.commit();
|
||||
}
|
||||
@ -461,6 +466,9 @@ public:
|
||||
void setMask(Mask* newMask) override {
|
||||
m_transaction.execute(new cmd::SetMask(m_document, newMask));
|
||||
}
|
||||
void addSlice(Slice* newSlice) override {
|
||||
m_transaction.execute(new cmd::AddSlice(m_sprite, newSlice));
|
||||
}
|
||||
gfx::Point getMaskOrigin() override { return m_maskOrigin; }
|
||||
bool getFilled() override { return m_filled; }
|
||||
bool getPreviewFilled() override { return m_previewFilled; }
|
||||
@ -603,6 +611,7 @@ public:
|
||||
bool useMask() override { return false; }
|
||||
Mask* getMask() override { return nullptr; }
|
||||
void setMask(Mask* newMask) override { }
|
||||
void addSlice(Slice* newSlice) override { }
|
||||
gfx::Point getMaskOrigin() override { return gfx::Point(0, 0); }
|
||||
bool getFilled() override { return false; }
|
||||
bool getPreviewFilled() override { return false; }
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -19,10 +19,6 @@ namespace doc {
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
class Splitter;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
class FrameTagWindow : protected app::gen::FrameTagProperties {
|
||||
|
113
src/app/ui/slice_window.cpp
Normal file
113
src/app/ui/slice_window.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
// 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/ui/slice_window.h"
|
||||
|
||||
#include "app/document.h"
|
||||
#include "app/ui/user_data_popup.h"
|
||||
#include "base/bind.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace app {
|
||||
|
||||
SliceWindow::SliceWindow(const doc::Sprite* sprite,
|
||||
const doc::Slice* slice,
|
||||
const doc::frame_t frameNum)
|
||||
: m_userData(slice->userData())
|
||||
{
|
||||
name()->setText(slice->name());
|
||||
|
||||
const doc::SliceKey* key = slice->getByFrame(frameNum);
|
||||
ASSERT(key);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
userData()->Click.connect(base::Bind<void>(&SliceWindow::onPopupUserData, this));
|
||||
|
||||
boundsX()->setTextf("%d", key->bounds().x);
|
||||
boundsY()->setTextf("%d", key->bounds().y);
|
||||
boundsW()->setTextf("%d", key->bounds().w);
|
||||
boundsH()->setTextf("%d", key->bounds().h);
|
||||
|
||||
center()->Click.connect(base::Bind<void>(&SliceWindow::onCenterChange, this));
|
||||
|
||||
if (!key->center().isEmpty()) {
|
||||
center()->setSelected(true);
|
||||
centerX()->setTextf("%d", key->center().x);
|
||||
centerY()->setTextf("%d", key->center().y);
|
||||
centerW()->setTextf("%d", key->center().w);
|
||||
centerH()->setTextf("%d", key->center().h);
|
||||
}
|
||||
else
|
||||
onCenterChange();
|
||||
}
|
||||
|
||||
bool SliceWindow::show()
|
||||
{
|
||||
openWindowInForeground();
|
||||
return (closer() == ok());
|
||||
}
|
||||
|
||||
std::string SliceWindow::nameValue() const
|
||||
{
|
||||
return name()->text();
|
||||
}
|
||||
|
||||
gfx::Rect SliceWindow::boundsValue() const
|
||||
{
|
||||
gfx::Rect rc(boundsX()->textInt(),
|
||||
boundsY()->textInt(),
|
||||
boundsW()->textInt(),
|
||||
boundsH()->textInt());
|
||||
if (rc.w < 1) rc.w = 1;
|
||||
if (rc.h < 1) rc.h = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
gfx::Rect SliceWindow::centerValue() const
|
||||
{
|
||||
if (!center()->isSelected())
|
||||
return gfx::Rect(0, 0, 0, 0);
|
||||
|
||||
gfx::Rect rc(centerX()->textInt(),
|
||||
centerY()->textInt(),
|
||||
centerW()->textInt(),
|
||||
centerH()->textInt());
|
||||
if (rc.w < 1) rc.w = 1;
|
||||
if (rc.h < 1) rc.h = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void SliceWindow::onCenterChange()
|
||||
{
|
||||
bool state = center()->isSelected();
|
||||
|
||||
centerX()->setEnabled(state);
|
||||
centerY()->setEnabled(state);
|
||||
centerW()->setEnabled(state);
|
||||
centerH()->setEnabled(state);
|
||||
|
||||
if (state) {
|
||||
centerX()->setText("1");
|
||||
centerY()->setText("1");
|
||||
centerW()->setTextf("%d", std::max(1, boundsW()->textInt()-2));
|
||||
centerH()->setTextf("%d", std::max(1, boundsH()->textInt()-2));
|
||||
}
|
||||
}
|
||||
|
||||
void SliceWindow::onPopupUserData()
|
||||
{
|
||||
show_user_data_popup(userData()->bounds(), m_userData);
|
||||
}
|
||||
|
||||
} // namespace app
|
46
src/app/ui/slice_window.h
Normal file
46
src/app/ui/slice_window.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_SLICE_WINDOW_H_INCLUDED
|
||||
#define APP_UI_SLICE_WINDOW_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/anidir.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/user_data.h"
|
||||
|
||||
#include "slice_properties.xml.h"
|
||||
|
||||
namespace doc {
|
||||
class Slice;
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
class SliceWindow : protected app::gen::SliceProperties {
|
||||
public:
|
||||
SliceWindow(const doc::Sprite* sprite,
|
||||
const doc::Slice* slice,
|
||||
const doc::frame_t frame);
|
||||
|
||||
bool show();
|
||||
|
||||
std::string nameValue() const;
|
||||
gfx::Rect boundsValue() const;
|
||||
gfx::Rect centerValue() const;
|
||||
const doc::UserData& userDataValue() { return m_userData; }
|
||||
|
||||
private:
|
||||
void onCenterChange();
|
||||
void onPopupUserData();
|
||||
|
||||
doc::UserData m_userData;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -936,6 +936,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
if (popupMenu) {
|
||||
AppMenuItem::setContextParams(params);
|
||||
popupMenu->showPopup(mouseMsg->position());
|
||||
AppMenuItem::setContextParams(Params());
|
||||
|
||||
m_state = STATE_STANDBY;
|
||||
invalidate();
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Aseprite Document Library
|
||||
# Copyright (C) 2001-2016 David Capello
|
||||
# Copyright (C) 2001-2017 David Capello
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
@ -58,6 +58,9 @@ add_library(doc-lib
|
||||
selected_frames.cpp
|
||||
selected_layers.cpp
|
||||
site.cpp
|
||||
slice.cpp
|
||||
slice_io.cpp
|
||||
slices.cpp
|
||||
sort_palette.cpp
|
||||
sprite.cpp
|
||||
sprites.cpp
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2001-2016 David Capello
|
||||
Copyright (c) 2001-2017 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Aseprite Document Library
|
||||
*Copyright (C) 2001-2016 David Capello*
|
||||
*Copyright (C) 2001-2017 David Capello*
|
||||
|
||||
> Distributed under [MIT license](LICENSE.txt)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
// Copyright (c) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -34,6 +34,8 @@
|
||||
#include "doc/remap.h"
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/site.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/slices.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
#endif
|
||||
|
189
src/doc/keyframes.h
Normal file
189
src/doc/keyframes.h
Normal file
@ -0,0 +1,189 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_KEYFRAMES_H_INCLUDED
|
||||
#define DOC_KEYFRAMES_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/disable_copying.h"
|
||||
#include "doc/frame.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
|
||||
template<typename T>
|
||||
class Keyframes {
|
||||
public:
|
||||
class Key {
|
||||
public:
|
||||
Key(const frame_t frame, T* value) : m_frame(frame), m_value(value) { }
|
||||
frame_t frame() const { return m_frame; }
|
||||
T* value() const { return m_value; }
|
||||
void setFrame(const frame_t frame) { m_frame = frame; }
|
||||
void setValue(T* value) { m_value = value; }
|
||||
private:
|
||||
frame_t m_frame;
|
||||
T* m_value;
|
||||
};
|
||||
|
||||
typedef std::vector<Key> List;
|
||||
typedef typename List::iterator iterator;
|
||||
typedef typename List::const_iterator const_iterator;
|
||||
|
||||
class Range {
|
||||
public:
|
||||
class RangeIterator {
|
||||
public:
|
||||
RangeIterator(const iterator& it,
|
||||
const iterator& end, const frame_t frame)
|
||||
: m_it(it), m_end(end), m_frame(frame) {
|
||||
if (it != end) {
|
||||
m_next = it;
|
||||
++m_next;
|
||||
}
|
||||
else
|
||||
m_next = end;
|
||||
}
|
||||
RangeIterator& operator++() {
|
||||
++m_frame;
|
||||
if (m_next != m_end &&
|
||||
m_next->frame() == m_frame) {
|
||||
m_it = m_next;
|
||||
++m_next;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const RangeIterator& other) {
|
||||
return (m_frame != other.m_frame+1);
|
||||
}
|
||||
T* operator*() {
|
||||
if (m_it != m_end)
|
||||
return m_it->value();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
private:
|
||||
iterator m_it, m_next;
|
||||
const iterator m_end;
|
||||
frame_t m_frame;
|
||||
};
|
||||
Range(const iterator& fromIt,
|
||||
const iterator& endIt,
|
||||
const frame_t from,
|
||||
const frame_t to)
|
||||
: m_fromIt(fromIt), m_endIt(endIt)
|
||||
, m_from(from), m_to(to) {
|
||||
}
|
||||
RangeIterator begin() const {
|
||||
return RangeIterator(m_fromIt, m_endIt, m_from);
|
||||
}
|
||||
RangeIterator end() const {
|
||||
return RangeIterator(m_fromIt, m_endIt, m_to);
|
||||
}
|
||||
bool empty() const {
|
||||
return (m_fromIt == m_endIt);
|
||||
}
|
||||
size_t countKeys() const {
|
||||
size_t count = 0;
|
||||
auto it = m_fromIt;
|
||||
for (; it!=m_endIt; ++it) {
|
||||
if (it->frame() > m_to)
|
||||
break;
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
private:
|
||||
const iterator m_fromIt, m_endIt;
|
||||
const frame_t m_from, m_to;
|
||||
};
|
||||
|
||||
Keyframes() { }
|
||||
|
||||
void insert(const frame_t frame, T* value) {
|
||||
auto it = getIterator(frame);
|
||||
if (it == end())
|
||||
m_keys.push_back(Key(frame, value));
|
||||
else if (it->frame() == frame)
|
||||
it->setValue(value);
|
||||
else {
|
||||
++it;
|
||||
m_keys.insert(it, Key(frame, value));
|
||||
}
|
||||
}
|
||||
|
||||
T* remove(const frame_t frame) {
|
||||
auto it = getIterator(frame);
|
||||
if (it != end()) {
|
||||
T* value = it->value();
|
||||
m_keys.erase(it);
|
||||
return value;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T* operator[](const frame_t frame) {
|
||||
auto it = getIterator(frame);
|
||||
if (it != end() && it->value())
|
||||
return it->value();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
iterator begin() { return m_keys.begin(); }
|
||||
iterator end() { return m_keys.end(); }
|
||||
const_iterator begin() const { return m_keys.begin(); }
|
||||
const_iterator end() const { return m_keys.end(); }
|
||||
|
||||
std::size_t size() const { return m_keys.size(); }
|
||||
bool empty() const { return m_keys.empty(); }
|
||||
|
||||
iterator getIterator(const frame_t frame) {
|
||||
auto
|
||||
it = m_keys.begin(),
|
||||
end = m_keys.end();
|
||||
auto next = it;
|
||||
for (; it != end; it=next) {
|
||||
++next;
|
||||
if ((frame >= it->frame()) &&
|
||||
(next == end || frame < next->frame())) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
frame_t fromFrame() const {
|
||||
if (!m_keys.empty())
|
||||
return m_keys.front().frame();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
frame_t toFrame() const {
|
||||
if (!m_keys.empty())
|
||||
return m_keys.back().frame();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
Range range(const frame_t from,
|
||||
const frame_t to) {
|
||||
return Range(getIterator(from), end(), from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
List m_keys;
|
||||
|
||||
// Disable operator=
|
||||
DISABLE_COPYING(Keyframes);
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
133
src/doc/keyframes_tests.cpp
Normal file
133
src/doc/keyframes_tests.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "doc/keyframes.h"
|
||||
|
||||
using namespace doc;
|
||||
|
||||
TEST(Keyframes, Operations)
|
||||
{
|
||||
Keyframes<int> k;
|
||||
|
||||
EXPECT_TRUE(k.empty());
|
||||
EXPECT_EQ(0, k.size());
|
||||
|
||||
k.insert(0, new int(5));
|
||||
EXPECT_FALSE(k.empty());
|
||||
EXPECT_EQ(1, k.size());
|
||||
EXPECT_EQ(0, k.fromFrame());
|
||||
EXPECT_EQ(0, k.toFrame());
|
||||
EXPECT_EQ(nullptr, k[-1]);
|
||||
EXPECT_EQ(5, *k[0]);
|
||||
EXPECT_EQ(5, *k[1]);
|
||||
EXPECT_EQ(5, *k[2]);
|
||||
|
||||
k.insert(2, new int(6));
|
||||
EXPECT_EQ(2, k.size());
|
||||
EXPECT_EQ(0, k.fromFrame());
|
||||
EXPECT_EQ(2, k.toFrame());
|
||||
EXPECT_EQ(nullptr, k[-1]);
|
||||
EXPECT_EQ(5, *k[0]);
|
||||
EXPECT_EQ(5, *k[1]);
|
||||
EXPECT_EQ(6, *k[2]);
|
||||
EXPECT_EQ(6, *k[3]);
|
||||
|
||||
k.insert(1, new int(3));
|
||||
EXPECT_EQ(3, k.size());
|
||||
EXPECT_EQ(0, k.fromFrame());
|
||||
EXPECT_EQ(2, k.toFrame());
|
||||
EXPECT_EQ(5, *k[0]);
|
||||
EXPECT_EQ(3, *k[1]);
|
||||
EXPECT_EQ(6, *k[2]);
|
||||
EXPECT_EQ(6, *k[3]);
|
||||
|
||||
k.remove(0);
|
||||
EXPECT_EQ(2, k.size());
|
||||
EXPECT_EQ(1, k.fromFrame());
|
||||
EXPECT_EQ(2, k.toFrame());
|
||||
EXPECT_EQ(nullptr, k[0]);
|
||||
EXPECT_EQ(3, *k[1]);
|
||||
EXPECT_EQ(6, *k[2]);
|
||||
EXPECT_EQ(6, *k[3]);
|
||||
|
||||
k.remove(3);
|
||||
EXPECT_EQ(1, k.size());
|
||||
EXPECT_EQ(1, k.fromFrame());
|
||||
EXPECT_EQ(1, k.toFrame());
|
||||
EXPECT_EQ(nullptr, k[0]);
|
||||
EXPECT_EQ(3, *k[1]);
|
||||
EXPECT_EQ(3, *k[2]);
|
||||
EXPECT_EQ(3, *k[3]);
|
||||
}
|
||||
|
||||
TEST(Keyframes, Range)
|
||||
{
|
||||
Keyframes<int> k;
|
||||
k.insert(0, new int(5));
|
||||
k.insert(2, nullptr);
|
||||
k.insert(4, new int(8));
|
||||
k.insert(6, new int(2));
|
||||
|
||||
EXPECT_EQ(0, k.fromFrame());
|
||||
EXPECT_EQ(6, k.toFrame());
|
||||
|
||||
EXPECT_EQ(5, *k[0]);
|
||||
EXPECT_EQ(5, *k[1]);
|
||||
EXPECT_EQ(nullptr, k[2]);
|
||||
EXPECT_EQ(nullptr, k[3]);
|
||||
EXPECT_EQ(8, *k[4]);
|
||||
EXPECT_EQ(8, *k[5]);
|
||||
EXPECT_EQ(2, *k[6]);
|
||||
EXPECT_EQ(2, *k[7]);
|
||||
|
||||
int pass = 0;
|
||||
for (int* value : k.range(0, 0)) {
|
||||
EXPECT_EQ(5, *value);
|
||||
++pass;
|
||||
}
|
||||
EXPECT_EQ(1, pass);
|
||||
|
||||
frame_t f = 1;
|
||||
for (int* value : k.range(1, 5)) {
|
||||
switch (f) {
|
||||
case 1: EXPECT_EQ(5, *value); break;
|
||||
case 2: EXPECT_EQ(nullptr, value); break;
|
||||
case 3: EXPECT_EQ(nullptr, value); break;
|
||||
case 4: EXPECT_EQ(8, *value); break;
|
||||
case 5: EXPECT_EQ(8, *value); break;
|
||||
default:
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
++f;
|
||||
}
|
||||
EXPECT_EQ(f, 6);
|
||||
|
||||
EXPECT_TRUE(k.range(-3, -1).empty());
|
||||
EXPECT_FALSE(k.range(0, 1).empty());
|
||||
EXPECT_FALSE(k.range(2, 3).empty());
|
||||
EXPECT_FALSE(k.range(7, 7).empty());
|
||||
|
||||
EXPECT_EQ(1, k.range(0, 0).countKeys());
|
||||
EXPECT_EQ(1, k.range(0, 1).countKeys());
|
||||
EXPECT_EQ(2, k.range(0, 2).countKeys());
|
||||
EXPECT_EQ(4, k.range(0, 7).countKeys());
|
||||
EXPECT_EQ(1, k.range(2, 3).countKeys());
|
||||
EXPECT_EQ(2, k.range(2, 4).countKeys());
|
||||
EXPECT_EQ(3, k.range(3, 7).countKeys());
|
||||
EXPECT_EQ(1, k.range(7, 7).countKeys());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
// Copyright (c) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -24,6 +24,7 @@ namespace doc {
|
||||
Sprite,
|
||||
Document,
|
||||
FrameTag,
|
||||
Slice,
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
83
src/doc/slice.cpp
Normal file
83
src/doc/slice.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/slice.h"
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "doc/slices.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
SliceKey::SliceKey()
|
||||
{
|
||||
}
|
||||
|
||||
SliceKey::SliceKey(// const frame_t frame,
|
||||
const gfx::Rect& bounds,
|
||||
const gfx::Rect& center)
|
||||
: m_bounds(bounds)
|
||||
, m_center(center)
|
||||
{
|
||||
}
|
||||
|
||||
Slice::Slice()
|
||||
: WithUserData(ObjectType::Slice)
|
||||
, m_owner(nullptr)
|
||||
, m_name("Slice")
|
||||
{
|
||||
// TODO default color should be configurable
|
||||
userData().setColor(doc::rgba(0, 0, 255, 255));
|
||||
}
|
||||
|
||||
Slice::~Slice()
|
||||
{
|
||||
ASSERT(!m_owner);
|
||||
}
|
||||
|
||||
int Slice::getMemSize() const
|
||||
{
|
||||
return sizeof(*this) + sizeof(SliceKey)*m_keys.size();
|
||||
}
|
||||
|
||||
void Slice::insert(const frame_t frame, const SliceKey& key)
|
||||
{
|
||||
m_keys.insert(frame, new SliceKey(key));
|
||||
}
|
||||
|
||||
void Slice::remove(const frame_t frame)
|
||||
{
|
||||
delete m_keys.remove(frame);
|
||||
}
|
||||
|
||||
const SliceKey* Slice::getByFrame(const frame_t frame) const
|
||||
{
|
||||
auto it = const_cast<Slice*>(this)->m_keys.getIterator(frame);
|
||||
if (it != m_keys.end())
|
||||
return it->value();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Slice::iterator Slice::getIteratorByFrame(const frame_t frame) const
|
||||
{
|
||||
return const_cast<Slice*>(this)->m_keys.getIterator(frame);
|
||||
}
|
||||
|
||||
void Slice::setOwner(Slices* owner)
|
||||
{
|
||||
m_owner = owner;
|
||||
}
|
||||
|
||||
void Slice::setName(const std::string& name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
} // namespace doc
|
88
src/doc/slice.h
Normal file
88
src/doc/slice.h
Normal file
@ -0,0 +1,88 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_SLICE_H_INCLUDED
|
||||
#define DOC_SLICE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/frame.h"
|
||||
#include "doc/keyframes.h"
|
||||
#include "doc/with_user_data.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace doc {
|
||||
class Slices;
|
||||
|
||||
class SliceKey {
|
||||
public:
|
||||
SliceKey();
|
||||
SliceKey(const gfx::Rect& bounds,
|
||||
const gfx::Rect& center = gfx::Rect());
|
||||
|
||||
bool isEmpty() const { return m_bounds.isEmpty(); }
|
||||
|
||||
const gfx::Rect& bounds() const { return m_bounds; }
|
||||
const gfx::Rect& center() const { return m_center; }
|
||||
|
||||
void setBounds(const gfx::Rect& bounds) { m_bounds = bounds; }
|
||||
void setCenter(const gfx::Rect& center) { m_center = center; }
|
||||
|
||||
private:
|
||||
gfx::Rect m_bounds;
|
||||
gfx::Rect m_center;
|
||||
};
|
||||
|
||||
class Slice : public WithUserData {
|
||||
typedef Keyframes<SliceKey> List;
|
||||
|
||||
public:
|
||||
typedef List::iterator iterator;
|
||||
typedef List::const_iterator const_iterator;
|
||||
|
||||
Slice();
|
||||
~Slice();
|
||||
|
||||
int getMemSize() const override;
|
||||
|
||||
void insert(const frame_t frame, const SliceKey& key);
|
||||
void remove(const frame_t frame);
|
||||
|
||||
const SliceKey* getByFrame(const frame_t frame) const;
|
||||
iterator getIteratorByFrame(const frame_t frame) const;
|
||||
|
||||
iterator begin() { return m_keys.begin(); }
|
||||
iterator end() { return m_keys.end(); }
|
||||
const_iterator begin() const { return m_keys.begin(); }
|
||||
const_iterator end() const { return m_keys.end(); }
|
||||
|
||||
std::size_t size() const { return m_keys.size(); }
|
||||
bool empty() const { return m_keys.empty(); }
|
||||
|
||||
Slices* owner() const { return m_owner; }
|
||||
frame_t fromFrame() const { return m_keys.fromFrame(); }
|
||||
frame_t toFrame() const { return m_keys.toFrame(); }
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
void setOwner(Slices* owner);
|
||||
void setName(const std::string& name);
|
||||
|
||||
List::Range range(const frame_t from, const frame_t to) {
|
||||
return m_keys.range(from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
Slices* m_owner;
|
||||
std::string m_name;
|
||||
List m_keys;
|
||||
|
||||
DISABLE_COPYING(Slice);
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
84
src/doc/slice_io.cpp
Normal file
84
src/doc/slice_io.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/slice_io.h"
|
||||
|
||||
#include "base/serialization.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/slice.h"
|
||||
#include "doc/string_io.h"
|
||||
#include "doc/user_data_io.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace doc {
|
||||
|
||||
using namespace base::serialization;
|
||||
using namespace base::serialization::little_endian;
|
||||
|
||||
void write_slice(std::ostream& os, const Slice* slice)
|
||||
{
|
||||
write32(os, slice->id());
|
||||
write_string(os, slice->name());
|
||||
write_user_data(os, slice->userData());
|
||||
|
||||
// Number of keys
|
||||
write32(os, slice->size());
|
||||
for (const auto& key : *slice) {
|
||||
write32(os, key.frame());
|
||||
write_slicekey(os, *key.value());
|
||||
}
|
||||
}
|
||||
|
||||
Slice* read_slice(std::istream& is, bool setId)
|
||||
{
|
||||
ObjectId id = read32(is);
|
||||
std::string name = read_string(is);
|
||||
UserData userData = read_user_data(is);
|
||||
size_t nkeys = read32(is);
|
||||
|
||||
base::UniquePtr<Slice> slice(new Slice);
|
||||
slice->setName(name);
|
||||
while (nkeys--) {
|
||||
frame_t fr = read32(is);
|
||||
slice->insert(fr, read_slicekey(is));
|
||||
}
|
||||
if (setId)
|
||||
slice->setId(id);
|
||||
return slice.release();
|
||||
}
|
||||
|
||||
void write_slicekey(std::ostream& os, const SliceKey& sliceKey)
|
||||
{
|
||||
write32(os, sliceKey.bounds().x);
|
||||
write32(os, sliceKey.bounds().y);
|
||||
write32(os, sliceKey.bounds().w);
|
||||
write32(os, sliceKey.bounds().h);
|
||||
write32(os, sliceKey.center().x);
|
||||
write32(os, sliceKey.center().y);
|
||||
write32(os, sliceKey.center().w);
|
||||
write32(os, sliceKey.center().h);
|
||||
}
|
||||
|
||||
SliceKey read_slicekey(std::istream& is)
|
||||
{
|
||||
gfx::Rect bounds, center;
|
||||
bounds.x = read32(is);
|
||||
bounds.y = read32(is);
|
||||
bounds.w = read32(is);
|
||||
bounds.h = read32(is);
|
||||
center.x = read32(is);
|
||||
center.y = read32(is);
|
||||
center.w = read32(is);
|
||||
center.h = read32(is);
|
||||
return SliceKey(bounds, center);
|
||||
}
|
||||
|
||||
}
|
26
src/doc/slice_io.h
Normal file
26
src/doc/slice_io.h
Normal file
@ -0,0 +1,26 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_SLICE_IO_H_INCLUDED
|
||||
#define DOC_SLICE_IO_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
namespace doc {
|
||||
|
||||
class Slice;
|
||||
class SliceKey;
|
||||
|
||||
void write_slice(std::ostream& os, const Slice* slice);
|
||||
Slice* read_slice(std::istream& is, bool setId = true);
|
||||
|
||||
void write_slicekey(std::ostream& os, const SliceKey& sliceKey);
|
||||
SliceKey read_slicekey(std::istream& is);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
67
src/doc/slices.cpp
Normal file
67
src/doc/slices.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/slices.h"
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "doc/slice.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace doc {
|
||||
|
||||
Slices::Slices(Sprite* sprite)
|
||||
: m_sprite(sprite)
|
||||
{
|
||||
}
|
||||
|
||||
Slices::~Slices()
|
||||
{
|
||||
for (Slice* slice : m_slices) {
|
||||
slice->setOwner(nullptr);
|
||||
delete slice;
|
||||
}
|
||||
}
|
||||
|
||||
void Slices::add(Slice* slice)
|
||||
{
|
||||
m_slices.push_back(slice);
|
||||
slice->setOwner(this);
|
||||
}
|
||||
|
||||
void Slices::remove(Slice* slice)
|
||||
{
|
||||
auto it = std::find(m_slices.begin(), m_slices.end(), slice);
|
||||
ASSERT(it != m_slices.end());
|
||||
if (it != m_slices.end())
|
||||
m_slices.erase(it);
|
||||
|
||||
slice->setOwner(nullptr);
|
||||
}
|
||||
|
||||
Slice* Slices::getByName(const std::string& name) const
|
||||
{
|
||||
for (Slice* slice : *this) {
|
||||
if (slice->name() == name)
|
||||
return slice;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Slice* Slices::getById(ObjectId id) const
|
||||
{
|
||||
for (Slice* slice : *this) {
|
||||
if (slice->id() == id)
|
||||
return slice;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace doc
|
57
src/doc/slices.h
Normal file
57
src/doc/slices.h
Normal file
@ -0,0 +1,57 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_SLICES_H_INCLUDED
|
||||
#define DOC_SLICES_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/disable_copying.h"
|
||||
#include "doc/object_id.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
|
||||
class Slice;
|
||||
class Sprite;
|
||||
|
||||
class Slices {
|
||||
typedef std::vector<Slice*> List;
|
||||
|
||||
public:
|
||||
typedef List::iterator iterator;
|
||||
typedef List::const_iterator const_iterator;
|
||||
|
||||
Slices(Sprite* sprite);
|
||||
~Slices();
|
||||
|
||||
Sprite* sprite() { return m_sprite; }
|
||||
|
||||
void add(Slice* slice);
|
||||
void remove(Slice* slice);
|
||||
|
||||
Slice* getByName(const std::string& name) const;
|
||||
Slice* getById(const ObjectId id) const;
|
||||
|
||||
iterator begin() { return m_slices.begin(); }
|
||||
iterator end() { return m_slices.end(); }
|
||||
const_iterator begin() const { return m_slices.begin(); }
|
||||
const_iterator end() const { return m_slices.end(); }
|
||||
|
||||
std::size_t size() const { return m_slices.size(); }
|
||||
bool empty() const { return m_slices.empty(); }
|
||||
|
||||
private:
|
||||
Sprite* m_sprite;
|
||||
List m_slices;
|
||||
|
||||
DISABLE_COPYING(Slices);
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
// Copyright (c) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -39,6 +39,7 @@ Sprite::Sprite(PixelFormat format, int width, int height, int ncolors)
|
||||
, m_pixelRatio(1, 1)
|
||||
, m_frames(1)
|
||||
, m_frameTags(this)
|
||||
, m_slices(this)
|
||||
{
|
||||
ASSERT(width > 0 && height > 0);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
// Copyright (c) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -19,6 +19,7 @@
|
||||
#include "doc/object.h"
|
||||
#include "doc/pixel_format.h"
|
||||
#include "doc/pixel_ratio.h"
|
||||
#include "doc/slices.h"
|
||||
#include "doc/sprite_position.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
@ -128,6 +129,9 @@ namespace doc {
|
||||
const FrameTags& frameTags() const { return m_frameTags; }
|
||||
FrameTags& frameTags() { return m_frameTags; }
|
||||
|
||||
const Slices& slices() const { return m_slices; }
|
||||
Slices& slices() { return m_slices; }
|
||||
|
||||
////////////////////////////////////////
|
||||
// Shared Images and CelData (for linked Cels)
|
||||
|
||||
@ -173,6 +177,7 @@ namespace doc {
|
||||
mutable RgbMap* m_rgbMap;
|
||||
|
||||
FrameTags m_frameTags;
|
||||
Slices m_slices;
|
||||
|
||||
// Disable default constructor and copying
|
||||
Sprite();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
// Copyright (c) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -19,6 +19,7 @@ namespace doc {
|
||||
}
|
||||
|
||||
const UserData& userData() const { return m_userData; }
|
||||
UserData& userData() { return m_userData; }
|
||||
|
||||
void setUserData(const UserData& userData) {
|
||||
m_userData = userData;
|
||||
|
Loading…
x
Reference in New Issue
Block a user