mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 04:19:12 +00:00
Merge branch 'tilemap-editor' into beta
This commit is contained in:
commit
e37ddbd7de
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -419,6 +419,10 @@
|
||||
<part id="outline_empty_pixel" x="208" y="224" w="5" h="5" />
|
||||
<part id="outline_full_pixel" x="214" y="224" w="5" h="5" />
|
||||
<part id="dynamics" x="176" y="144" w="16" h="16" />
|
||||
<part id="tiles" x="144" y="208" w="6" h="6" />
|
||||
<part id="tiles_manual" x="150" y="208" w="6" h="6" />
|
||||
<part id="tiles_auto" x="156" y="208" w="6" h="6" />
|
||||
<part id="tiles_stack" x="162" y="208" w="6" h="6" />
|
||||
</parts>
|
||||
<styles>
|
||||
<style id="box" />
|
||||
|
21
data/gui.xml
21
data/gui.xml
@ -83,6 +83,10 @@
|
||||
<key command="NewLayer" shortcut="Ctrl+Shift+J" mac="Cmd+Shift+J">
|
||||
<param name="viaCut" value="true" />
|
||||
</key>
|
||||
<key command="NewLayer" shortcut="Space+N">
|
||||
<param name="ask" value="true" />
|
||||
<param name="tilemap" value="true" />
|
||||
</key>
|
||||
<key command="GotoPreviousLayer" shortcut="Down" context="Normal" />
|
||||
<key command="GotoNextLayer" shortcut="Up" context="Normal" />
|
||||
<!-- Frame -->
|
||||
@ -138,6 +142,16 @@
|
||||
<key command="Timeline" shortcut="Tab">
|
||||
<param name="switch" value="true" />
|
||||
</key>
|
||||
<key command="ToggleTilesMode" shortcut="Space+Tab" />
|
||||
<key command="TilesetMode" shortcut="Space+1">
|
||||
<param name="mode" value="manual" />
|
||||
</key>
|
||||
<key command="TilesetMode" shortcut="Space+2">
|
||||
<param name="mode" value="auto" />
|
||||
</key>
|
||||
<key command="TilesetMode" shortcut="Space+3">
|
||||
<param name="mode" value="stack" />
|
||||
</key>
|
||||
<key command="PaletteEditor" shortcut="A" />
|
||||
<key command="PaletteEditor" shortcut="F4">
|
||||
<param name="edit" value="switch" />
|
||||
@ -424,6 +438,9 @@
|
||||
<key command="NewSpriteFromSelection" shortcut="Ctrl+Alt+N" mac="Cmd+Alt+N" />
|
||||
|
||||
<!-- Commands not associated to menu items and without shortcuts by default -->
|
||||
<key command="NewLayer">
|
||||
<param name="tilemap" value="true" />
|
||||
</key>
|
||||
<key command="PixelPerfectMode" />
|
||||
<key command="ContiguousFill" />
|
||||
<key command="SetInkType"><param name="type" value="simple" /></key>
|
||||
@ -811,6 +828,10 @@
|
||||
<param name="reference" value="true" />
|
||||
<param name="fromFile" value="true" />
|
||||
</item>
|
||||
<item command="NewLayer" text="@.layer_new_tilemap_layer">
|
||||
<param name="tilemap" value="true" />
|
||||
<param name="ask" value="true" />
|
||||
</item>
|
||||
</menu>
|
||||
<item command="RemoveLayer" text="@.layer_delete_layer" group="layer_remove" />
|
||||
<item command="BackgroundFromLayer" text="@.layer_background_from_layer" />
|
||||
|
@ -209,6 +209,7 @@
|
||||
</section>
|
||||
<section id="color_bar">
|
||||
<option id="box_size" type="int" default="11" />
|
||||
<option id="tiles_box_size" type="int" default="16" />
|
||||
<option id="fg_color" type="app::Color" default="app::Color::fromRgb(255, 255, 255)" />
|
||||
<option id="bg_color" type="app::Color" default="app::Color::fromRgb(0, 0, 0)" />
|
||||
<option id="selector" type="app::ColorBar::ColorSelector" default="app::ColorBar::ColorSelector::TINT_SHADE_TONE" />
|
||||
|
@ -203,6 +203,36 @@ clear = &Delete
|
||||
unlink = &Unlink
|
||||
link_cels = &Link Cels
|
||||
|
||||
[color_bar]
|
||||
fg = Foreground Color
|
||||
bg = Background Color
|
||||
fg_warning = Add foreground color to the palette
|
||||
bg_warning = Add background color to the palette
|
||||
edit_color = Edit Color
|
||||
sort_and_gradients = Sort & Gradients
|
||||
presets = Presets
|
||||
options = Options
|
||||
switch_tileset = Show/Hide Tileset
|
||||
tileset_mode_manual = <<<END
|
||||
Manual: Modify existent tiles,
|
||||
don't create new tiles automatically
|
||||
END
|
||||
tileset_mode_auto = <<<END
|
||||
Auto: Modify and reuse existent tiles,
|
||||
create/delete tiles if needed/possible
|
||||
END
|
||||
tileset_mode_stack = <<<END
|
||||
Stack: Don't modify existent tiles,
|
||||
generate and stack new tiles automatically
|
||||
END
|
||||
remap_palette = Remap Palette
|
||||
remap_palette_tooltip = Matches old indexes with new indexes
|
||||
remap_tiles = Remap Tiles
|
||||
remap_tiles_tooltip = Matches old tiles with new tiles
|
||||
clear_tiles = Clear Tiles
|
||||
resize_tiles = Resize Tiles
|
||||
drag_and_drop_tiles = Drag And Drop Tiles
|
||||
|
||||
[commands]
|
||||
About = About
|
||||
AddColor = Add {0} Color to Palette
|
||||
@ -252,6 +282,7 @@ Copy = Copy
|
||||
CopyCel = Copy Cel
|
||||
CopyColors = Copy Colors
|
||||
CopyMerged = Copy Merged
|
||||
CopyTiles = Copy Tiles
|
||||
CropSprite = Crop Sprite
|
||||
Cut = Cut
|
||||
DeselectMask = Deselect Mask
|
||||
@ -334,6 +365,7 @@ MoveColors = Move Colors
|
||||
MoveMask = Move {0} {1}
|
||||
MoveMask_Boundaries = Selection Boundaries
|
||||
MoveMask_Content = Selection Content
|
||||
MoveTiles = Move Tiles
|
||||
NewBrush = New Brush
|
||||
NewFile = New File
|
||||
NewFile_FromClipboard = New File from Clipboard
|
||||
@ -348,9 +380,11 @@ NewLayer_BeforeActiveLayer = New {} Below
|
||||
NewLayer_Layer = Layer
|
||||
NewLayer_Group = Group
|
||||
NewLayer_ReferenceLayer = Reference Layer
|
||||
NewLayer_TilemapLayer = Tilemap Layer
|
||||
NewLayer_FromClipboard = {} from Clipboard
|
||||
NewLayer_ViaCopy = {} via Copy
|
||||
NewLayer_ViaCut = {} via Cut
|
||||
NewLayer_WithDialog = {} (with dialog)
|
||||
NewSpriteFromSelection = New Sprite from Selection
|
||||
OpenBrowser = Open Browser
|
||||
OpenFile = Open Sprite
|
||||
@ -448,6 +482,11 @@ SymmetryMode = Symmetry Mode
|
||||
TiledMode = Tiled Mode
|
||||
Timeline = Switch Timeline
|
||||
TogglePreview = Toggle Preview
|
||||
ToggleTilesMode = Toggle Tiles Mode
|
||||
TilesetMode = Tileset Mode: {}
|
||||
TilesetMode_Manual = Manual
|
||||
TilesetMode_Auto = Auto
|
||||
TilesetMode_Stack = Stack
|
||||
ToggleTimelineThumbnails = Toggle Timeline Thumbnails
|
||||
Undo = Undo
|
||||
UndoHistory = Undo History
|
||||
@ -880,6 +919,7 @@ layer_new_group = New &Group
|
||||
layer_new_layer_via_copy = New Layer via &Copy
|
||||
layer_new_layer_via_cut = New Layer via Cu&t
|
||||
layer_new_reference_layer_from_file = New &Reference Layer from File
|
||||
layer_new_tilemap_layer = New Tilemap Layer
|
||||
layer_delete_layer = Delete Laye&r
|
||||
layer_background_from_layer = &Background from Layer
|
||||
layer_layer_from_background = &Layer from Background
|
||||
@ -974,8 +1014,14 @@ default_new_folder_name = New Folder
|
||||
[new_layer]
|
||||
title = New Layer
|
||||
name = Name:
|
||||
tileset = Tileset:
|
||||
default_new_layer_name = New Layer
|
||||
|
||||
[tileset_selector]
|
||||
new_tileset = New Tileset
|
||||
grid_width = Grid Width:
|
||||
grid_height = Grid Height:
|
||||
|
||||
[new_sprite]
|
||||
title = New Sprite
|
||||
size = Size:
|
||||
|
@ -1,20 +1,25 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2001-2016 by David Capello -->
|
||||
<gui>
|
||||
<window id="new_layer" text="@.title">
|
||||
<box vertical="true">
|
||||
<box horizontal="true">
|
||||
<box vertical="true" homogeneous="true">
|
||||
<label text="@.name" />
|
||||
</box>
|
||||
<box vertical="true" homogeneous="true">
|
||||
<entry maxsize="256" text="@.default_new_layer_name" id="name" magnet="true" />
|
||||
</box>
|
||||
</box>
|
||||
<box horizontal="true" homogeneous="true">
|
||||
<button text="@general.ok" closewindow="true" id="ok" magnet="true" />
|
||||
<button text="@general.cancel" closewindow="true" />
|
||||
</box>
|
||||
</box>
|
||||
</window>
|
||||
</gui>
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2019 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2001-2016 David Capello -->
|
||||
<gui>
|
||||
<window id="new_layer" text="@.title">
|
||||
<vbox>
|
||||
<grid columns="2">
|
||||
<label text="@.name" />
|
||||
<entry maxsize="256" text="@.default_new_layer_name" id="name" magnet="true" />
|
||||
|
||||
<vbox>
|
||||
<label id="tileset_label" text="@.tileset" />
|
||||
</vbox>
|
||||
<hbox id="tileset_options" />
|
||||
</grid>
|
||||
<hbox>
|
||||
<boxfiller />
|
||||
<hbox homogeneous="true">
|
||||
<button text="@general.ok" closewindow="true" id="ok" magnet="true" minwidth="60" />
|
||||
<button text="@general.cancel" closewindow="true" />
|
||||
</hbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</window>
|
||||
</gui>
|
||||
|
17
data/widgets/tileset_selector.xml
Normal file
17
data/widgets/tileset_selector.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2019 Igara Studio S.A. -->
|
||||
<gui>
|
||||
<vbox id="tileset_selector">
|
||||
<combobox id="tilesets">
|
||||
<listitem text="@.new_tileset" value="-1" />
|
||||
</combobox>
|
||||
|
||||
<hbox id="grid_options">
|
||||
<label text="@.grid_width" />
|
||||
<expr id="grid_width" text="" />
|
||||
|
||||
<label text="@.grid_height" />
|
||||
<expr id="grid_height" text="" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
</gui>
|
@ -25,7 +25,10 @@ ASE files use Intel (little-endian) byte order.
|
||||
* `PIXEL`: One pixel, depending on the image pixel format:
|
||||
- **RGBA**: `BYTE[4]`, each pixel have 4 bytes in this order Red, Green, Blue, Alpha.
|
||||
- **Grayscale**: `BYTE[2]`, each pixel have 2 bytes in the order Value, Alpha.
|
||||
- **Indexed**: `BYTE`, Each pixel uses 1 byte (the index).
|
||||
- **Indexed**: `BYTE`, each pixel uses 1 byte (the index).
|
||||
* `TILE`: **Tilemaps**: Each tile can be a 8-bit (`BYTE`), 16-bit
|
||||
(`WORD`), or 32-bit (`DWORD`) value and there are masks related to
|
||||
the meaning of each bit.
|
||||
|
||||
## Introduction
|
||||
|
||||
@ -146,6 +149,7 @@ Ignore this chunk if you find the new palette chunk (0x2019)
|
||||
WORD Layer type
|
||||
0 = Normal (image) layer
|
||||
1 = Group
|
||||
2 = Tilemap
|
||||
WORD Layer child level (see NOTE.1)
|
||||
WORD Default layer width in pixels (ignored)
|
||||
WORD Default layer height in pixels (ignored)
|
||||
@ -173,19 +177,24 @@ Ignore this chunk if you find the new palette chunk (0x2019)
|
||||
Note: valid only if file header flags field has bit 1 set
|
||||
BYTE[3] For future (set to zero)
|
||||
STRING Layer name
|
||||
+ If layer type = 2
|
||||
DWORD Tileset index
|
||||
|
||||
### Cel Chunk (0x2005)
|
||||
|
||||
This chunk determine where to put a cel in the specified
|
||||
layer/frame.
|
||||
This chunk determine where to put a cel in the specified layer/frame.
|
||||
|
||||
WORD Layer index (see NOTE.2)
|
||||
SHORT X position
|
||||
SHORT Y position
|
||||
BYTE Opacity level
|
||||
WORD Cel type
|
||||
WORD Cel Type
|
||||
0 - Raw Image Data (unused, compressed image is preferred)
|
||||
1 - Linked Cel
|
||||
2 - Compressed Image
|
||||
3 - Compressed Tilemap
|
||||
BYTE[7] For future (set to zero)
|
||||
+ For cel type = 0 (Raw Cel)
|
||||
+ For cel type = 0 (Raw Image Data)
|
||||
WORD Width in pixels
|
||||
WORD Height in pixels
|
||||
PIXEL[] Raw pixel data: row by row from top to bottom,
|
||||
@ -195,14 +204,18 @@ Ignore this chunk if you find the new palette chunk (0x2019)
|
||||
+ For cel type = 2 (Compressed Image)
|
||||
WORD Width in pixels
|
||||
WORD Height in pixels
|
||||
BYTE[] "Raw Cel" data compressed with ZLIB method
|
||||
|
||||
Details about the ZLIB and DEFLATE compression methods:
|
||||
|
||||
* https://www.ietf.org/rfc/rfc1950
|
||||
* https://www.ietf.org/rfc/rfc1951
|
||||
* Some extra notes that might help you to decode the data:
|
||||
http://george.chiramattel.com/blog/2007/09/deflatestream-block-length-does-not-match.html
|
||||
BYTE[] "Raw Cel" data compressed with ZLIB method (see NOTE.3)
|
||||
+ For cel type = 3 (Compressed Tilemap)
|
||||
WORD Width in pixels
|
||||
WORD Height in pixels
|
||||
WORD Bits per tile (8, 16, or 32)
|
||||
DWORD Bitmask for tile ID (e.g. 0x1fffffff for 32-bit tiles)
|
||||
DWORD Bitmask for X flip
|
||||
DWORD Bitmask for Y flip
|
||||
DWORD Bitmask for 90CW rotation
|
||||
BYTE[10] Reserved
|
||||
TILE[] Row by row, from top to bottom tile by tile
|
||||
compressed with ZLIB method (see NOTE.3)
|
||||
|
||||
### Cel Extra Chunk (0x2006)
|
||||
|
||||
@ -231,11 +244,23 @@ Color profile for RGB or grayscale values.
|
||||
this fixed gamma, because sRGB uses different gamma sections
|
||||
(linear and non-linear). If sRGB is specified with a fixed
|
||||
gamma = 1.0, it means that this is Linear sRGB.
|
||||
BYTE[8] Reserved (set to zero]
|
||||
BYTE[8] Reserved (set to zero)
|
||||
+ If type = ICC:
|
||||
DWORD ICC profile data length
|
||||
BYTE[] ICC profile data. More info: http://www.color.org/ICC1V42.pdf
|
||||
|
||||
### External Files Chunk (0x2008)
|
||||
|
||||
A list of external files linked with this file. It might be used to
|
||||
reference external palettes or tilesets.
|
||||
|
||||
DWORD Number of entries
|
||||
BYTE[8] Reserved (set to zero)
|
||||
+ For each entry
|
||||
DWORD Entry ID (this ID is referenced by tilesets or palettes)
|
||||
BYTE[8] Reserved (set to zero)
|
||||
STRING External file name
|
||||
|
||||
### Mask Chunk (0x2016) DEPRECATED
|
||||
|
||||
SHORT X position
|
||||
@ -326,6 +351,26 @@ belongs to that cel, etc.
|
||||
LONG Pivot X position (relative to the slice origin)
|
||||
LONG Pivot Y position (relative to the slice origin)
|
||||
|
||||
### Tileset Chunk (0x2023)
|
||||
|
||||
DWORD Tileset ID
|
||||
DWORD Tileset flags
|
||||
1 - Include link to external file
|
||||
2 - Include tiles inside this file
|
||||
DWORD Number of tiles
|
||||
WORD Tile Width
|
||||
WORD Tile Height
|
||||
BYTE[16] Reserved
|
||||
STRING Name of the tileset
|
||||
+ If flag 1 is set
|
||||
DWORD ID of the external file. This ID is one entry
|
||||
of the the External Files Chunk.
|
||||
DWORD Tileset ID in the external file
|
||||
+ If flag 2 is set
|
||||
DWORD Compressed data length
|
||||
PIXEL[] Compressed Tileset image (see NOTE.3):
|
||||
(Tile Width) x (Tile Height x Number of Tiles)
|
||||
|
||||
### Notes
|
||||
|
||||
#### NOTE.1
|
||||
@ -356,6 +401,15 @@ example:
|
||||
| `- Layer2 4
|
||||
`- Layer3 5
|
||||
|
||||
#### NOTE.3
|
||||
|
||||
Details about the ZLIB and DEFLATE compression methods:
|
||||
|
||||
* https://www.ietf.org/rfc/rfc1950
|
||||
* https://www.ietf.org/rfc/rfc1951
|
||||
* Some extra notes that might help you to decode the data:
|
||||
http://george.chiramattel.com/blog/2007/09/deflatestream-block-length-does-not-match.html
|
||||
|
||||
## File Format Changes
|
||||
|
||||
1. The first change from the first release of the new .ase format,
|
||||
|
@ -160,6 +160,7 @@ if(ENABLE_SCRIPTING)
|
||||
script/engine.cpp
|
||||
script/frame_class.cpp
|
||||
script/frames_class.cpp
|
||||
script/grid_class.cpp
|
||||
script/image_class.cpp
|
||||
script/image_iterator_class.cpp
|
||||
script/image_spec_class.cpp
|
||||
@ -185,6 +186,8 @@ if(ENABLE_SCRIPTING)
|
||||
script/sprites_class.cpp
|
||||
script/tag_class.cpp
|
||||
script/tags_class.cpp
|
||||
script/tileset_class.cpp
|
||||
script/tilesets_class.cpp
|
||||
script/tool_class.cpp
|
||||
script/values.cpp
|
||||
script/version_class.cpp
|
||||
@ -290,6 +293,7 @@ if(ENABLE_UI)
|
||||
commands/cmd_tiled_mode.cpp
|
||||
commands/cmd_timeline.cpp
|
||||
commands/cmd_toggle_preview.cpp
|
||||
commands/cmd_toggle_tiles_mode.cpp
|
||||
commands/cmd_toggle_timeline_thumbnails.cpp
|
||||
commands/cmd_undo_history.cpp
|
||||
commands/cmd_unlink_cel.cpp
|
||||
@ -298,6 +302,7 @@ if(ENABLE_UI)
|
||||
commands/filters/filter_target_buttons.cpp
|
||||
commands/filters/filter_window.cpp
|
||||
commands/screenshot.cpp
|
||||
commands/tileset_mode.cpp
|
||||
file_selector.cpp
|
||||
modules/editors.cpp
|
||||
modules/gfx.cpp
|
||||
@ -383,6 +388,7 @@ if(ENABLE_UI)
|
||||
ui/tabs.cpp
|
||||
ui/tag_window.cpp
|
||||
ui/task_widget.cpp
|
||||
ui/tileset_selector.cpp
|
||||
ui/timeline/ani_controls.cpp
|
||||
ui/timeline/timeline.cpp
|
||||
ui/toolbar.cpp
|
||||
@ -420,6 +426,8 @@ add_library(app-lib
|
||||
cmd/add_palette.cpp
|
||||
cmd/add_slice.cpp
|
||||
cmd/add_tag.cpp
|
||||
cmd/add_tile.cpp
|
||||
cmd/add_tileset.cpp
|
||||
cmd/assign_color_profile.cpp
|
||||
cmd/background_from_layer.cpp
|
||||
cmd/clear_cel.cpp
|
||||
@ -443,12 +451,16 @@ add_library(app-lib
|
||||
cmd/move_layer.cpp
|
||||
cmd/patch_cel.cpp
|
||||
cmd/remap_colors.cpp
|
||||
cmd/remap_tilemaps.cpp
|
||||
cmd/remap_tileset.cpp
|
||||
cmd/remove_cel.cpp
|
||||
cmd/remove_frame.cpp
|
||||
cmd/remove_layer.cpp
|
||||
cmd/remove_palette.cpp
|
||||
cmd/remove_slice.cpp
|
||||
cmd/remove_tag.cpp
|
||||
cmd/remove_tile.cpp
|
||||
cmd/remove_tileset.cpp
|
||||
cmd/replace_image.cpp
|
||||
cmd/reselect_mask.cpp
|
||||
cmd/set_cel_bounds.cpp
|
||||
@ -488,6 +500,7 @@ add_library(app-lib
|
||||
cmd/with_slice.cpp
|
||||
cmd/with_sprite.cpp
|
||||
cmd/with_tag.cpp
|
||||
cmd/with_tileset.cpp
|
||||
cmd_sequence.cpp
|
||||
cmd_transaction.cpp
|
||||
color.cpp
|
||||
@ -532,6 +545,7 @@ add_library(app-lib
|
||||
commands/filters/filter_worker.cpp
|
||||
commands/move_colors_command.cpp
|
||||
commands/move_thing.cpp
|
||||
commands/move_tiles_command.cpp
|
||||
commands/new_params.cpp
|
||||
commands/quick_command.cpp
|
||||
console.cpp
|
||||
@ -599,8 +613,8 @@ add_library(app-lib
|
||||
ui/layer_frame_comboboxes.cpp
|
||||
util/autocrop.cpp
|
||||
util/buffer_region.cpp
|
||||
util/cel_ops.cpp
|
||||
util/conversion_to_surface.cpp
|
||||
util/create_cel_copy.cpp
|
||||
util/expand_cel_canvas.cpp
|
||||
util/filetoks.cpp
|
||||
util/freetype_utils.cpp
|
||||
|
@ -67,6 +67,7 @@ void ActiveSiteHandler::getActiveSiteForDoc(Doc* doc, Site* site)
|
||||
site->frame(data.frame);
|
||||
site->range(data.range);
|
||||
site->selectedColors(data.selectedColors);
|
||||
site->selectedTiles(data.selectedTiles);
|
||||
}
|
||||
|
||||
void ActiveSiteHandler::setActiveLayerInDoc(Doc* doc, doc::Layer* layer)
|
||||
@ -109,6 +110,12 @@ void ActiveSiteHandler::setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks
|
||||
data.selectedColors = picks;
|
||||
}
|
||||
|
||||
void ActiveSiteHandler::setSelectedTilesInDoc(Doc* doc, const doc::PalettePicks& picks)
|
||||
{
|
||||
Data& data = getData(doc);
|
||||
data.selectedTiles = picks;
|
||||
}
|
||||
|
||||
void ActiveSiteHandler::onAddLayer(DocEvent& ev)
|
||||
{
|
||||
Data& data = getData(ev.document());
|
||||
|
@ -41,6 +41,7 @@ namespace app {
|
||||
void setActiveFrameInDoc(Doc* doc, doc::frame_t frame);
|
||||
void setRangeInDoc(Doc* doc, const DocRange& range);
|
||||
void setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks& picks);
|
||||
void setSelectedTilesInDoc(Doc* doc, const doc::PalettePicks& picks);
|
||||
|
||||
private:
|
||||
// DocObserver impl
|
||||
@ -55,6 +56,7 @@ namespace app {
|
||||
doc::frame_t frame;
|
||||
DocRange range;
|
||||
doc::PalettePicks selectedColors;
|
||||
doc::PalettePicks selectedTiles;
|
||||
};
|
||||
|
||||
Data& getData(Doc* doc);
|
||||
|
@ -288,6 +288,14 @@ os::Shortcut get_os_shortcut_from_key(const Key* key)
|
||||
{
|
||||
if (key && !key->accels().empty()) {
|
||||
const ui::Accelerator& accel = key->accels().front();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Shortcuts with spacebar as modifier do not work well in macOS
|
||||
// (they will be called when the space bar is unpressed too).
|
||||
if ((accel.modifiers() & ui::kKeySpaceModifier) == ui::kKeySpaceModifier)
|
||||
return os::Shortcut();
|
||||
#endif
|
||||
|
||||
return os::Shortcut(
|
||||
(accel.unicodeChar() ? accel.unicodeChar():
|
||||
from_scancode_to_unicode(accel.scancode())),
|
||||
|
112
src/app/cmd/add_tile.cpp
Normal file
112
src/app/cmd/add_tile.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// 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_tile.h"
|
||||
|
||||
#include "app/doc.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tilesets.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
AddTile::AddTile(doc::Tileset* tileset,
|
||||
const doc::ImageRef& image)
|
||||
: WithTileset(tileset)
|
||||
, WithImage(image.get())
|
||||
, m_size(0)
|
||||
, m_tileIndex(doc::tile_i_notile)
|
||||
, m_imageRef(image)
|
||||
{
|
||||
}
|
||||
|
||||
AddTile::AddTile(doc::Tileset* tileset,
|
||||
const doc::tile_index ti)
|
||||
: WithTileset(tileset)
|
||||
, WithImage(tileset->get(ti).get())
|
||||
, m_size(0)
|
||||
, m_tileIndex(ti)
|
||||
, m_imageRef(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void AddTile::onExecute()
|
||||
{
|
||||
doc::Tileset* tileset = this->tileset();
|
||||
ASSERT(tileset);
|
||||
|
||||
if (m_tileIndex != doc::tile_i_notile) {
|
||||
ASSERT(!m_imageRef);
|
||||
tileset->sprite()->incrementVersion();
|
||||
tileset->incrementVersion();
|
||||
}
|
||||
else {
|
||||
ASSERT(m_imageRef);
|
||||
addTile(tileset, m_imageRef);
|
||||
m_imageRef.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AddTile::onUndo()
|
||||
{
|
||||
doc::Tileset* tileset = this->tileset();
|
||||
ASSERT(tileset);
|
||||
|
||||
write_image(m_stream, image());
|
||||
m_size = size_t(m_stream.tellp());
|
||||
|
||||
tileset->erase(m_tileIndex);
|
||||
|
||||
tileset->sprite()->incrementVersion();
|
||||
tileset->incrementVersion();
|
||||
}
|
||||
|
||||
void AddTile::onRedo()
|
||||
{
|
||||
doc::Tileset* tileset = this->tileset();
|
||||
|
||||
ASSERT(!m_imageRef);
|
||||
m_imageRef.reset(read_image(m_stream));
|
||||
ASSERT(m_imageRef);
|
||||
|
||||
addTile(tileset, m_imageRef);
|
||||
m_imageRef.reset();
|
||||
|
||||
m_stream.str(std::string());
|
||||
m_stream.clear();
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void AddTile::onFireNotifications()
|
||||
{
|
||||
doc::Tileset* tileset = this->tileset();
|
||||
|
||||
// Notify that the tileset's changed
|
||||
static_cast<Doc*>(tileset->sprite()->document())
|
||||
->notifyTilesetChanged(tileset);
|
||||
}
|
||||
|
||||
void AddTile::addTile(doc::Tileset* tileset, const doc::ImageRef& image)
|
||||
{
|
||||
if (m_tileIndex == doc::tile_i_notile)
|
||||
m_tileIndex = tileset->add(image);
|
||||
else
|
||||
tileset->insert(m_tileIndex, image);
|
||||
|
||||
tileset->sprite()->incrementVersion();
|
||||
tileset->incrementVersion();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
57
src/app/cmd/add_tile.h
Normal file
57
src/app/cmd/add_tile.h
Normal file
@ -0,0 +1,57 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_ADD_TILE_H_INCLUDED
|
||||
#define APP_CMD_ADD_TILE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_image.h"
|
||||
#include "app/cmd/with_tileset.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/tile.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace doc {
|
||||
class Tileset;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
class AddTile : public Cmd
|
||||
, public WithTileset
|
||||
, public WithImage {
|
||||
public:
|
||||
AddTile(doc::Tileset* tileset, const doc::ImageRef& image);
|
||||
AddTile(doc::Tileset* tileset, const doc::tile_index ti);
|
||||
|
||||
doc::tile_index tileIndex() const { return m_tileIndex; }
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
void onFireNotifications() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this) + m_size;
|
||||
}
|
||||
|
||||
private:
|
||||
void addTile(doc::Tileset* tileset,
|
||||
const doc::ImageRef& image);
|
||||
|
||||
size_t m_size;
|
||||
std::stringstream m_stream;
|
||||
doc::tile_index m_tileIndex;
|
||||
doc::ImageRef m_imageRef;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
88
src/app/cmd/add_tileset.cpp
Normal file
88
src/app/cmd/add_tileset.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// 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_tileset.h"
|
||||
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/subobjects_io.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tileset_io.h"
|
||||
#include "doc/tilesets.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
AddTileset::AddTileset(doc::Sprite* sprite, doc::Tileset* tileset)
|
||||
: WithSprite(sprite)
|
||||
, WithTileset(tileset)
|
||||
, m_size(0)
|
||||
, m_tilesetIndex(-1)
|
||||
{
|
||||
}
|
||||
|
||||
AddTileset::AddTileset(doc::Sprite* sprite, const doc::tileset_index tsi)
|
||||
: WithSprite(sprite)
|
||||
, WithTileset(sprite->tilesets()->get(tsi))
|
||||
, m_size(0)
|
||||
, m_tilesetIndex(tsi)
|
||||
{
|
||||
}
|
||||
|
||||
void AddTileset::onExecute()
|
||||
{
|
||||
Tileset* tileset = this->tileset();
|
||||
|
||||
addTileset(tileset);
|
||||
}
|
||||
|
||||
void AddTileset::onUndo()
|
||||
{
|
||||
doc::Tileset* tileset = this->tileset();
|
||||
write_tileset(m_stream, tileset);
|
||||
m_size = size_t(m_stream.tellp());
|
||||
|
||||
doc::Sprite* sprite = this->sprite();
|
||||
sprite->tilesets()->erase(m_tilesetIndex);
|
||||
|
||||
sprite->incrementVersion();
|
||||
sprite->tilesets()->incrementVersion();
|
||||
|
||||
delete tileset;
|
||||
}
|
||||
|
||||
void AddTileset::onRedo()
|
||||
{
|
||||
auto sprite = this->sprite();
|
||||
doc::Tileset* tileset = read_tileset(m_stream, sprite);
|
||||
|
||||
addTileset(tileset);
|
||||
|
||||
m_stream.str(std::string());
|
||||
m_stream.clear();
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void AddTileset::addTileset(doc::Tileset* tileset)
|
||||
{
|
||||
auto sprite = this->sprite();
|
||||
|
||||
if (m_tilesetIndex == -1)
|
||||
m_tilesetIndex = sprite->tilesets()->add(tileset);
|
||||
else
|
||||
sprite->tilesets()->set(m_tilesetIndex, tileset);
|
||||
|
||||
sprite->incrementVersion();
|
||||
sprite->tilesets()->incrementVersion();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
53
src/app/cmd/add_tileset.h
Normal file
53
src/app/cmd/add_tileset.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_ADD_TILESET_H_INCLUDED
|
||||
#define APP_CMD_ADD_TILESET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_sprite.h"
|
||||
#include "app/cmd/with_tileset.h"
|
||||
#include "doc/tile.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace doc {
|
||||
class Tileset;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
class AddTileset : public Cmd
|
||||
, public WithSprite
|
||||
, public WithTileset {
|
||||
public:
|
||||
AddTileset(doc::Sprite* sprite, doc::Tileset* tileset);
|
||||
AddTileset(doc::Sprite* sprite, const doc::tileset_index tsi);
|
||||
|
||||
doc::tileset_index tilesetIndex() const { return m_tilesetIndex; }
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this) + m_size;
|
||||
}
|
||||
|
||||
private:
|
||||
void addTileset(doc::Tileset* tileset);
|
||||
|
||||
size_t m_size;
|
||||
std::stringstream m_stream;
|
||||
doc::tileset_index m_tilesetIndex;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -43,21 +43,22 @@ BackgroundFromLayer::BackgroundFromLayer(Layer* layer)
|
||||
void BackgroundFromLayer::onExecute()
|
||||
{
|
||||
Layer* layer = this->layer();
|
||||
ASSERT(!layer->isTilemap()); // TODO support background tilemaps
|
||||
|
||||
Sprite* sprite = layer->sprite();
|
||||
auto doc = static_cast<Doc*>(sprite->document());
|
||||
color_t bgcolor = doc->bgColor();
|
||||
|
||||
// Create a temporary image to draw each cel of the new Background
|
||||
// layer.
|
||||
ImageRef bg_image(Image::create(sprite->pixelFormat(),
|
||||
sprite->width(),
|
||||
sprite->height()));
|
||||
ImageRef bg_image(Image::create(sprite->spec()));
|
||||
|
||||
CelList cels;
|
||||
layer->getCels(cels);
|
||||
for (Cel* cel : cels) {
|
||||
Image* cel_image = cel->image();
|
||||
ASSERT(cel_image);
|
||||
ASSERT(cel_image->pixelFormat() != IMAGE_TILEMAP);
|
||||
|
||||
clear_image(bg_image.get(), bgcolor);
|
||||
render::composite_image(
|
||||
|
@ -19,11 +19,12 @@
|
||||
#include "app/cmd/set_cel_data.h"
|
||||
#include "app/cmd/unlink_cel.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/util/create_cel_copy.h"
|
||||
#include "app/util/cel_ops.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/rasterize.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
@ -32,8 +33,8 @@ namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
CopyCel::CopyCel(
|
||||
LayerImage* srcLayer, frame_t srcFrame,
|
||||
LayerImage* dstLayer, frame_t dstFrame, bool continuous)
|
||||
Layer* srcLayer, frame_t srcFrame,
|
||||
Layer* dstLayer, frame_t dstFrame, bool continuous)
|
||||
: m_srcLayer(srcLayer)
|
||||
, m_dstLayer(dstLayer)
|
||||
, m_srcFrame(srcFrame)
|
||||
@ -44,8 +45,8 @@ CopyCel::CopyCel(
|
||||
|
||||
void CopyCel::onExecute()
|
||||
{
|
||||
LayerImage* srcLayer = static_cast<LayerImage*>(m_srcLayer.layer());
|
||||
LayerImage* dstLayer = static_cast<LayerImage*>(m_dstLayer.layer());
|
||||
Layer* srcLayer = m_srcLayer.layer();
|
||||
Layer* dstLayer = m_dstLayer.layer();
|
||||
|
||||
ASSERT(srcLayer);
|
||||
ASSERT(dstLayer);
|
||||
@ -89,9 +90,17 @@ void CopyCel::onExecute()
|
||||
!srcCel || !srcImage)
|
||||
return;
|
||||
|
||||
ASSERT(!dstLayer->isTilemap()); // TODO support background tilemaps
|
||||
|
||||
if (createLink) {
|
||||
executeAndAdd(new cmd::SetCelData(dstCel, srcCel->dataRef()));
|
||||
}
|
||||
// Rasterize tilemap into the regular image background layer
|
||||
else if (srcLayer->isTilemap()) {
|
||||
ImageRef tmp(Image::createCopy(dstImage.get()));
|
||||
render::rasterize(tmp.get(), srcCel, 0, 0, false);
|
||||
executeAndAdd(new cmd::CopyRect(dstImage.get(), tmp.get(), gfx::Clip(tmp->bounds())));
|
||||
}
|
||||
else {
|
||||
BlendMode blend = (srcLayer->isBackground() ?
|
||||
BlendMode::SRC:
|
||||
@ -114,7 +123,7 @@ void CopyCel::onExecute()
|
||||
if (createLink)
|
||||
dstCel = Cel::MakeLink(m_dstFrame, srcCel);
|
||||
else
|
||||
dstCel = create_cel_copy(srcCel, dstSprite, dstLayer, m_dstFrame);
|
||||
dstCel = create_cel_copy(this, srcCel, dstSprite, dstLayer, m_dstFrame);
|
||||
|
||||
executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -13,10 +14,6 @@
|
||||
#include "doc/color.h"
|
||||
#include "doc/frame.h"
|
||||
|
||||
namespace doc {
|
||||
class LayerImage;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
@ -24,8 +21,8 @@ namespace cmd {
|
||||
class CopyCel : public CmdSequence {
|
||||
public:
|
||||
CopyCel(
|
||||
LayerImage* srcLayer, frame_t srcFrame,
|
||||
LayerImage* dstLayer, frame_t dstFrame, bool continuous);
|
||||
Layer* srcLayer, frame_t srcFrame,
|
||||
Layer* dstLayer, frame_t dstFrame, bool continuous);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -11,8 +11,11 @@
|
||||
|
||||
#include "app/cmd/copy_region.h"
|
||||
|
||||
#include "app/doc.h"
|
||||
#include "app/util/buffer_region.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tileset.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
@ -42,6 +45,18 @@ CopyRegion::CopyRegion(Image* dst, const Image* src,
|
||||
save_image_region_in_buffer(m_region, src, dstPos, m_buffer);
|
||||
}
|
||||
|
||||
CopyTileRegion::CopyTileRegion(Image* dst, const Image* src,
|
||||
const gfx::Region& region,
|
||||
const gfx::Point& dstPos,
|
||||
bool alreadyCopied,
|
||||
const doc::tile_index tileIndex,
|
||||
const doc::Tileset* tileset)
|
||||
: CopyRegion(dst, src, region, dstPos, alreadyCopied)
|
||||
, m_tileIndex(tileIndex)
|
||||
, m_tilesetId(tileset ? tileset->id(): NullId)
|
||||
{
|
||||
}
|
||||
|
||||
void CopyRegion::onExecute()
|
||||
{
|
||||
if (!m_alreadyCopied)
|
||||
@ -65,6 +80,26 @@ void CopyRegion::swap()
|
||||
|
||||
swap_image_region_with_buffer(m_region, image, m_buffer);
|
||||
image->incrementVersion();
|
||||
|
||||
rehash();
|
||||
}
|
||||
|
||||
void CopyTileRegion::rehash()
|
||||
{
|
||||
ASSERT(m_tileIndex != tile_i_notile);
|
||||
ASSERT(m_tilesetId != NullId);
|
||||
if (m_tilesetId != NullId) {
|
||||
auto tileset = get<Tileset>(m_tilesetId);
|
||||
ASSERT(tileset);
|
||||
if (tileset) {
|
||||
tileset->incrementVersion();
|
||||
tileset->notifyTileContentChange(m_tileIndex);
|
||||
|
||||
// Notify thath the tileset changed
|
||||
static_cast<Doc*>(tileset->sprite()->document())
|
||||
->notifyTilesetChanged(tileset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -12,9 +12,14 @@
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_image.h"
|
||||
#include "base/buffer.h"
|
||||
#include "doc/tile.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/region.h"
|
||||
|
||||
namespace doc {
|
||||
class Tileset;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
@ -41,12 +46,29 @@ namespace cmd {
|
||||
|
||||
private:
|
||||
void swap();
|
||||
virtual void rehash() { }
|
||||
|
||||
bool m_alreadyCopied;
|
||||
gfx::Region m_region;
|
||||
base::buffer m_buffer;
|
||||
};
|
||||
|
||||
class CopyTileRegion : public CopyRegion {
|
||||
public:
|
||||
CopyTileRegion(Image* dst, const Image* src,
|
||||
const gfx::Region& region,
|
||||
const gfx::Point& dstPos,
|
||||
bool alreadyCopied,
|
||||
const doc::tile_index tileIndex,
|
||||
const doc::Tileset* tileset);
|
||||
|
||||
private:
|
||||
void rehash() override;
|
||||
|
||||
doc::tile_index m_tileIndex;
|
||||
doc::ObjectId m_tilesetId;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -11,6 +12,8 @@
|
||||
#include "app/cmd/crop_cel.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/primitives.h"
|
||||
|
||||
namespace app {
|
||||
@ -47,10 +50,18 @@ void CropCel::cropImage(const gfx::Point& origin,
|
||||
{
|
||||
Cel* cel = this->cel();
|
||||
|
||||
gfx::Rect localBounds(bounds);
|
||||
if (cel->layer()->isTilemap()) {
|
||||
doc::Tileset* tileset = static_cast<LayerTilemap*>(cel->layer())->tileset();
|
||||
if (tileset) {
|
||||
doc::Grid grid = tileset->grid();
|
||||
localBounds = grid.canvasToTile(bounds);
|
||||
}
|
||||
}
|
||||
if (bounds != cel->image()->bounds()) {
|
||||
ImageRef image(crop_image(cel->image(),
|
||||
bounds.x, bounds.y,
|
||||
bounds.w, bounds.h,
|
||||
localBounds.x, localBounds.y,
|
||||
localBounds.w, localBounds.h,
|
||||
cel->image()->maskColor()));
|
||||
ObjectId id = cel->image()->id();
|
||||
ObjectVersion ver = cel->image()->version();
|
||||
@ -59,7 +70,7 @@ void CropCel::cropImage(const gfx::Point& origin,
|
||||
image->setId(id);
|
||||
image->setVersion(ver);
|
||||
image->incrementVersion();
|
||||
cel->data()->setImage(image);
|
||||
cel->data()->setImage(image, cel->layer());
|
||||
cel->data()->incrementVersion();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -124,7 +124,7 @@ void FlattenLayers::onExecute()
|
||||
else {
|
||||
gfx::Rect bounds(image->bounds());
|
||||
if (doc::algorithm::shrink_bounds(
|
||||
image.get(), bounds, image->maskColor())) {
|
||||
image.get(), image->maskColor(), nullptr, bounds)) {
|
||||
cel_image.reset(
|
||||
doc::crop_image(image.get(), bounds, image->maskColor()));
|
||||
cel = new Cel(frame, cel_image);
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -20,11 +21,12 @@
|
||||
#include "app/cmd/set_cel_frame.h"
|
||||
#include "app/cmd/unlink_cel.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/util/create_cel_copy.h"
|
||||
#include "app/util/cel_ops.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/rasterize.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
@ -90,10 +92,18 @@ void MoveCel::onExecute()
|
||||
!srcCel || !srcImage)
|
||||
return;
|
||||
|
||||
ASSERT(!dstLayer->isTilemap()); // TODO support background tilemaps
|
||||
|
||||
if (createLink) {
|
||||
executeAndAdd(new cmd::SetCelData(dstCel, srcCel->dataRef()));
|
||||
executeAndAdd(new cmd::UnlinkCel(srcCel));
|
||||
}
|
||||
// Rasterize tilemap into the regular image background layer
|
||||
else if (srcLayer->isTilemap()) {
|
||||
ImageRef tmp(Image::createCopy(dstImage.get()));
|
||||
render::rasterize(tmp.get(), srcCel, 0, 0, false);
|
||||
executeAndAdd(new cmd::CopyRect(dstImage.get(), tmp.get(), gfx::Clip(tmp->bounds())));
|
||||
}
|
||||
else {
|
||||
BlendMode blend = (srcLayer->isBackground() ?
|
||||
BlendMode::SRC:
|
||||
@ -119,7 +129,7 @@ void MoveCel::onExecute()
|
||||
executeAndAdd(new cmd::SetCelFrame(srcCel, m_dstFrame));
|
||||
}
|
||||
else {
|
||||
dstCel = create_cel_copy(srcCel, dstSprite, dstLayer, m_dstFrame);
|
||||
dstCel = create_cel_copy(this, srcCel, dstSprite, dstLayer, m_dstFrame);
|
||||
|
||||
executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
|
||||
executeAndAdd(new cmd::ClearCel(srcCel));
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -14,6 +15,7 @@
|
||||
#include "app/cmd/crop_cel.h"
|
||||
#include "app/cmd/trim_cel.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
@ -36,17 +38,37 @@ void PatchCel::onExecute()
|
||||
{
|
||||
Cel* cel = this->cel();
|
||||
|
||||
const gfx::Rect newBounds =
|
||||
cel->bounds() | gfx::Rect(m_region.bounds()).offset(m_pos);
|
||||
if (cel->bounds() != newBounds) {
|
||||
executeAndAdd(new CropCel(cel, newBounds));
|
||||
gfx::Rect newBounds;
|
||||
gfx::Region regionInTiles;
|
||||
doc::Grid grid;
|
||||
if (cel->image()->pixelFormat() == IMAGE_TILEMAP) {
|
||||
newBounds = cel->bounds() | m_region.bounds();
|
||||
auto tileset = static_cast<LayerTilemap*>(cel->layer())->tileset();
|
||||
grid = tileset->grid();
|
||||
grid.origin(m_pos);
|
||||
regionInTiles = grid.canvasToTile(m_region);
|
||||
}
|
||||
else {
|
||||
newBounds = cel->bounds() | gfx::Rect(m_region.bounds()).offset(m_pos);
|
||||
}
|
||||
|
||||
executeAndAdd(
|
||||
new CopyRegion(cel->image(),
|
||||
m_patch,
|
||||
m_region,
|
||||
m_pos - cel->position()));
|
||||
if (cel->bounds() != newBounds)
|
||||
executeAndAdd(new CropCel(cel, newBounds));
|
||||
|
||||
if (cel->image()->pixelFormat() == IMAGE_TILEMAP) {
|
||||
executeAndAdd(
|
||||
new CopyRegion(cel->image(),
|
||||
m_patch,
|
||||
regionInTiles,
|
||||
-grid.canvasToTile(cel->position())));
|
||||
}
|
||||
else {
|
||||
executeAndAdd(
|
||||
new CopyRegion(cel->image(),
|
||||
m_patch,
|
||||
m_region,
|
||||
m_pos - cel->position()));
|
||||
}
|
||||
|
||||
executeAndAdd(new TrimCel(cel));
|
||||
|
||||
|
@ -31,7 +31,7 @@ void RemapColors::onExecute()
|
||||
{
|
||||
Sprite* spr = sprite();
|
||||
if (spr->pixelFormat() == IMAGE_INDEXED) {
|
||||
spr->remapImages(0, spr->lastFrame(), m_remap);
|
||||
spr->remapImages(m_remap);
|
||||
incrementVersions(spr);
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@ void RemapColors::onUndo()
|
||||
{
|
||||
Sprite* spr = this->sprite();
|
||||
if (spr->pixelFormat() == IMAGE_INDEXED) {
|
||||
spr->remapImages(0, spr->lastFrame(), m_remap.invert());
|
||||
spr->remapImages(m_remap.invert());
|
||||
incrementVersions(spr);
|
||||
}
|
||||
}
|
||||
|
62
src/app/cmd/remap_tilemaps.cpp
Normal file
62
src/app/cmd/remap_tilemaps.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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/remap_tilemaps.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/cels_range.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/remap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tileset.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
RemapTilemaps::RemapTilemaps(Tileset* tileset,
|
||||
const Remap& remap)
|
||||
: WithTileset(tileset)
|
||||
, m_remap(remap)
|
||||
{
|
||||
}
|
||||
|
||||
void RemapTilemaps::onExecute()
|
||||
{
|
||||
Tileset* tileset = this->tileset();
|
||||
Sprite* spr = tileset->sprite();
|
||||
spr->remapTilemaps(tileset, m_remap);
|
||||
incrementVersions(tileset);
|
||||
}
|
||||
|
||||
void RemapTilemaps::onUndo()
|
||||
{
|
||||
Tileset* tileset = this->tileset();
|
||||
Sprite* spr = tileset->sprite();
|
||||
spr->remapTilemaps(tileset, m_remap.invert());
|
||||
incrementVersions(tileset);
|
||||
}
|
||||
|
||||
void RemapTilemaps::incrementVersions(Tileset* tileset)
|
||||
{
|
||||
Sprite* spr = tileset->sprite();
|
||||
for (const Cel* cel : spr->uniqueCels()) {
|
||||
if (cel->layer()->isTilemap() &&
|
||||
static_cast<LayerTilemap*>(cel->layer())->tileset() == tileset) {
|
||||
cel->image()->incrementVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
41
src/app/cmd/remap_tilemaps.h
Normal file
41
src/app/cmd/remap_tilemaps.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_REMAP_TILEMAPS_H_INCLUDED
|
||||
#define APP_CMD_REMAP_TILEMAPS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_tileset.h"
|
||||
#include "doc/remap.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class RemapTilemaps : public Cmd
|
||||
, public WithTileset {
|
||||
public:
|
||||
RemapTilemaps(Tileset* tileset,
|
||||
const Remap& remap);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this) + m_remap.getMemSize();
|
||||
}
|
||||
|
||||
private:
|
||||
void incrementVersions(Tileset* tileset);
|
||||
|
||||
Remap m_remap;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
54
src/app/cmd/remap_tileset.cpp
Normal file
54
src/app/cmd/remap_tileset.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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/remap_tileset.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/cels_range.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/remap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tileset.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
RemapTileset::RemapTileset(Tileset* tileset,
|
||||
const Remap& remap)
|
||||
: WithTileset(tileset)
|
||||
, m_remap(remap)
|
||||
{
|
||||
}
|
||||
|
||||
void RemapTileset::onExecute()
|
||||
{
|
||||
Tileset* tileset = this->tileset();
|
||||
applyRemap(tileset, m_remap);
|
||||
}
|
||||
|
||||
void RemapTileset::onUndo()
|
||||
{
|
||||
Tileset* tileset = this->tileset();
|
||||
applyRemap(tileset, m_remap.invert());
|
||||
}
|
||||
|
||||
void RemapTileset::applyRemap(Tileset* tileset, const Remap& remap)
|
||||
{
|
||||
tileset->remap(remap);
|
||||
tileset->incrementVersion();
|
||||
tileset->sprite()->incrementVersion();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
41
src/app/cmd/remap_tileset.h
Normal file
41
src/app/cmd/remap_tileset.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_REMAP_TILESET_H_INCLUDED
|
||||
#define APP_CMD_REMAP_TILESET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_tileset.h"
|
||||
#include "doc/remap.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class RemapTileset : public Cmd
|
||||
, public WithTileset {
|
||||
public:
|
||||
RemapTileset(Tileset* tileset,
|
||||
const Remap& remap);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this) + m_remap.getMemSize();
|
||||
}
|
||||
|
||||
private:
|
||||
void applyRemap(Tileset* tileset, const Remap& remap);
|
||||
|
||||
Remap m_remap;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
42
src/app/cmd/remove_tile.cpp
Normal file
42
src/app/cmd/remove_tile.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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_tile.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
RemoveTile::RemoveTile(Tileset* tileset, const tile_index ti)
|
||||
: AddTile(tileset, ti)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveTile::onExecute()
|
||||
{
|
||||
AddTile::onUndo();
|
||||
}
|
||||
|
||||
void RemoveTile::onUndo()
|
||||
{
|
||||
AddTile::onRedo();
|
||||
}
|
||||
|
||||
void RemoveTile::onRedo()
|
||||
{
|
||||
AddTile::onUndo();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
30
src/app/cmd/remove_tile.h
Normal file
30
src/app/cmd/remove_tile.h
Normal file
@ -0,0 +1,30 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_REMOVE_TILE_H_INCLUDED
|
||||
#define APP_CMD_REMOVE_TILE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd/add_tile.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class RemoveTile : public AddTile {
|
||||
public:
|
||||
RemoveTile(Tileset* tileset, const tile_index ti);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
43
src/app/cmd/remove_tileset.cpp
Normal file
43
src/app/cmd/remove_tileset.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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_tileset.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
RemoveTileset::RemoveTileset(Sprite* sprite,
|
||||
const tileset_index si)
|
||||
: AddTileset(sprite, si)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveTileset::onExecute()
|
||||
{
|
||||
AddTileset::onUndo();
|
||||
}
|
||||
|
||||
void RemoveTileset::onUndo()
|
||||
{
|
||||
AddTileset::onRedo();
|
||||
}
|
||||
|
||||
void RemoveTileset::onRedo()
|
||||
{
|
||||
AddTileset::onUndo();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
31
src/app/cmd/remove_tileset.h
Normal file
31
src/app/cmd/remove_tileset.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_REMOVE_TILESET_H_INCLUDED
|
||||
#define APP_CMD_REMOVE_TILESET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd/add_tileset.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class RemoveTileset : public AddTileset {
|
||||
public:
|
||||
RemoveTileset(Sprite* sprite,
|
||||
const tileset_index si);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -17,6 +17,7 @@
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/subobjects_io.h"
|
||||
#include "doc/tilesets.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
@ -75,6 +76,16 @@ void ReplaceImage::replaceImage(ObjectId oldId, const ImageRef& newImage)
|
||||
cel->data()->incrementVersion();
|
||||
}
|
||||
|
||||
if (spr->hasTilesets()) {
|
||||
for (Tileset* tileset : *spr->tilesets()) {
|
||||
for (tile_index i=0; i<tileset->size(); ++i) {
|
||||
ImageRef image = tileset->get(i);
|
||||
if (image && image->id() == oldId)
|
||||
tileset->incrementVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spr->replaceImage(oldId, newImage);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/subobjects_io.h"
|
||||
|
||||
@ -81,7 +82,9 @@ void SetCelData::createCopy()
|
||||
|
||||
ASSERT(!m_dataCopy);
|
||||
m_dataCopy.reset(new CelData(*cel->data()));
|
||||
m_dataCopy->setImage(ImageRef(Image::createCopy(cel->image())));
|
||||
m_dataCopy->setImage(
|
||||
ImageRef(Image::createCopy(cel->image())),
|
||||
cel->layer());
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tilesets.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/task_delegate.h"
|
||||
|
||||
@ -35,16 +36,16 @@ namespace {
|
||||
|
||||
class SuperDelegate : public render::TaskDelegate {
|
||||
public:
|
||||
SuperDelegate(int ncels, render::TaskDelegate* delegate)
|
||||
: m_ncels(ncels)
|
||||
, m_curCel(0)
|
||||
SuperDelegate(int nimages, render::TaskDelegate* delegate)
|
||||
: m_nimages(nimages)
|
||||
, m_curImage(0)
|
||||
, m_delegate(delegate) {
|
||||
}
|
||||
|
||||
void notifyTaskProgress(double progress) override {
|
||||
if (m_delegate)
|
||||
m_delegate->notifyTaskProgress(
|
||||
(progress + m_curCel) / m_ncels);
|
||||
(progress + m_curImage) / m_nimages);
|
||||
}
|
||||
|
||||
bool continueTask() override {
|
||||
@ -54,13 +55,13 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
void nextCel() {
|
||||
++m_curCel;
|
||||
void nextImage() {
|
||||
++m_curImage;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_ncels;
|
||||
int m_curCel;
|
||||
int m_nimages;
|
||||
int m_curImage;
|
||||
TaskDelegate* m_delegate;
|
||||
};
|
||||
|
||||
@ -79,24 +80,53 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||
if (sprite->pixelFormat() == newFormat)
|
||||
return;
|
||||
|
||||
SuperDelegate superDel(sprite->uniqueCels().size(), delegate);
|
||||
const auto rgbMapFor = sprite->rgbMapForSprite();
|
||||
// Calculate the number of images to convert just to show a proper
|
||||
// progress bar.
|
||||
tile_index nimages = 0;
|
||||
for (Cel* cel : sprite->uniqueCels())
|
||||
if (!cel->layer()->isTilemap())
|
||||
++nimages;
|
||||
if (sprite->hasTilesets()) {
|
||||
for (Tileset* tileset : *sprite->tilesets())
|
||||
nimages += tileset->size();
|
||||
}
|
||||
|
||||
SuperDelegate superDel(nimages, delegate);
|
||||
|
||||
// Convert cel images
|
||||
for (Cel* cel : sprite->uniqueCels()) {
|
||||
ImageRef old_image = cel->imageRef();
|
||||
ImageRef new_image(
|
||||
render::convert_pixel_format
|
||||
(old_image.get(), nullptr, newFormat,
|
||||
dithering,
|
||||
sprite->rgbMap(cel->frame(), rgbMapFor, mapAlgorithm),
|
||||
sprite->palette(cel->frame()),
|
||||
cel->layer()->isBackground(),
|
||||
old_image->maskColor(),
|
||||
toGray,
|
||||
&superDel));
|
||||
if (cel->layer()->isTilemap())
|
||||
continue;
|
||||
|
||||
m_seq.add(new cmd::ReplaceImage(sprite, old_image, new_image));
|
||||
superDel.nextCel();
|
||||
ImageRef oldImage = cel->imageRef();
|
||||
convertImage(sprite, dithering,
|
||||
oldImage,
|
||||
cel->frame(),
|
||||
cel->layer()->isBackground(),
|
||||
mapAlgorithm,
|
||||
toGray,
|
||||
&superDel);
|
||||
|
||||
superDel.nextImage();
|
||||
}
|
||||
|
||||
// Convert tileset images
|
||||
if (sprite->hasTilesets()) {
|
||||
for (Tileset* tileset : *sprite->tilesets()) {
|
||||
for (tile_index i=0; i<tileset->size(); ++i) {
|
||||
ImageRef oldImage = tileset->get(i);
|
||||
if (oldImage) {
|
||||
convertImage(sprite, dithering,
|
||||
oldImage,
|
||||
0, // TODO select a frame or generate other tilesets?
|
||||
false, // TODO is background? it depends of the layer where this tileset is used
|
||||
mapAlgorithm,
|
||||
toGray,
|
||||
&superDel);
|
||||
}
|
||||
superDel.nextImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set all cels opacity to 100% if we are converting to indexed.
|
||||
@ -159,5 +189,31 @@ void SetPixelFormat::setFormat(PixelFormat format)
|
||||
doc->notify_observers<DocEvent&>(&DocObserver::onPixelFormatChanged, ev);
|
||||
}
|
||||
|
||||
void SetPixelFormat::convertImage(doc::Sprite* sprite,
|
||||
const render::Dithering& dithering,
|
||||
const doc::ImageRef& oldImage,
|
||||
const doc::frame_t frame,
|
||||
const bool isBackground,
|
||||
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate)
|
||||
{
|
||||
ASSERT(oldImage);
|
||||
ASSERT(oldImage->pixelFormat() != IMAGE_TILEMAP);
|
||||
|
||||
ImageRef newImage(
|
||||
render::convert_pixel_format
|
||||
(oldImage.get(), nullptr, m_newFormat,
|
||||
dithering,
|
||||
sprite->rgbMap(frame, sprite->rgbMapForSprite(), mapAlgorithm),
|
||||
sprite->palette(frame),
|
||||
isBackground,
|
||||
oldImage->maskColor(),
|
||||
toGray,
|
||||
delegate));
|
||||
|
||||
m_seq.add(new cmd::ReplaceImage(sprite, oldImage, newImage));
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "app/cmd/with_sprite.h"
|
||||
#include "app/cmd_sequence.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/pixel_format.h"
|
||||
#include "doc/rgbmap_algorithm.h"
|
||||
|
||||
@ -47,6 +49,14 @@ namespace cmd {
|
||||
|
||||
private:
|
||||
void setFormat(doc::PixelFormat format);
|
||||
void convertImage(doc::Sprite* sprite,
|
||||
const render::Dithering& dithering,
|
||||
const doc::ImageRef& oldImage,
|
||||
const doc::frame_t frame,
|
||||
const bool isBackground,
|
||||
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate);
|
||||
|
||||
doc::PixelFormat m_oldFormat;
|
||||
doc::PixelFormat m_newFormat;
|
||||
|
@ -59,7 +59,7 @@ void ShiftMaskedCel::shift(int dx, int dy)
|
||||
newImage->setId(id);
|
||||
newImage->setVersion(ver);
|
||||
newImage->incrementVersion();
|
||||
cel->data()->setImage(newImage);
|
||||
cel->data()->setImage(newImage, cel->layer());
|
||||
}
|
||||
cel->data()->setBounds(newBounds);
|
||||
cel->data()->incrementVersion();
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -25,10 +26,11 @@ using namespace doc;
|
||||
TrimCel::TrimCel(Cel* cel)
|
||||
{
|
||||
gfx::Rect newBounds;
|
||||
if (algorithm::shrink_bounds(cel->image(), newBounds,
|
||||
cel->image()->maskColor())) {
|
||||
if (algorithm::shrink_bounds(cel->image(),
|
||||
cel->image()->maskColor(),
|
||||
cel->layer(), newBounds)) {
|
||||
newBounds.offset(cel->position());
|
||||
if (cel->bounds() != newBounds) {
|
||||
if (cel->imageBounds() != newBounds) {
|
||||
add(new cmd::CropCel(cel, newBounds));
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
@ -36,7 +37,7 @@ void UnlinkCel::onExecute()
|
||||
|
||||
ImageRef imgCopy(Image::createCopy(oldCelData->image()));
|
||||
CelDataRef celDataCopy(new CelData(*oldCelData));
|
||||
celDataCopy->setImage(imgCopy);
|
||||
celDataCopy->setImage(imgCopy, cel->layer());
|
||||
celDataCopy->setUserData(oldCelData->userData());
|
||||
|
||||
if (m_newImageId) {
|
||||
|
31
src/app/cmd/with_tileset.cpp
Normal file
31
src/app/cmd/with_tileset.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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_tileset.h"
|
||||
|
||||
#include "doc/tileset.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
WithTileset::WithTileset(Tileset* tileset)
|
||||
: m_tilesetId(tileset->id())
|
||||
{
|
||||
}
|
||||
|
||||
Tileset* WithTileset::tileset()
|
||||
{
|
||||
return get<Tileset>(m_tilesetId);
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
32
src/app/cmd/with_tileset.h
Normal file
32
src/app/cmd/with_tileset.h
Normal file
@ -0,0 +1,32 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_WITH_TILESET_H_INCLUDED
|
||||
#define APP_CMD_WITH_TILESET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/object_id.h"
|
||||
|
||||
namespace doc {
|
||||
class Tileset;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
class WithTileset {
|
||||
public:
|
||||
WithTileset(doc::Tileset* tileset);
|
||||
doc::Tileset* tileset();
|
||||
|
||||
private:
|
||||
doc::ObjectId m_tilesetId;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -21,16 +21,16 @@ namespace app {
|
||||
|
||||
void add(Cmd* cmd);
|
||||
|
||||
// Helper to create a CmdSequence in the same onExecute() member
|
||||
// function.
|
||||
void executeAndAdd(Cmd* cmd);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
size_t onMemSize() const override;
|
||||
|
||||
// Helper to create a CmdSequence in the same onExecute() member
|
||||
// function.
|
||||
void executeAndAdd(Cmd* cmd);
|
||||
|
||||
private:
|
||||
std::vector<Cmd*> m_cmds;
|
||||
};
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/tile.h"
|
||||
#include "gfx/hsl.h"
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
@ -83,7 +84,7 @@ Color Color::fromGray(int g, int a)
|
||||
// static
|
||||
Color Color::fromIndex(int index)
|
||||
{
|
||||
ASSERT(index >= 0);
|
||||
ASSERT(index >= 0 || index == tile_i_notile);
|
||||
|
||||
Color color(Color::IndexType);
|
||||
color.m_value.index = index;
|
||||
@ -114,6 +115,7 @@ Color Color::fromImage(PixelFormat pixelFormat, color_t c)
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
case IMAGE_TILEMAP:
|
||||
color = Color::fromIndex(c);
|
||||
break;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -17,11 +17,15 @@
|
||||
#include "app/util/wrap_point.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "gfx/point.h"
|
||||
#include "render/get_sprite_pixel.h"
|
||||
|
||||
#define PICKER_TRACE(...) // TRACE
|
||||
|
||||
namespace app {
|
||||
|
||||
namespace {
|
||||
@ -45,14 +49,48 @@ bool get_cel_pixel(const Cel* cel,
|
||||
if (!celBounds.contains(pos))
|
||||
return false;
|
||||
|
||||
pos.x = (pos.x-celBounds.x)*image->width()/celBounds.w;
|
||||
pos.y = (pos.y-celBounds.y)*image->height()/celBounds.h;
|
||||
const gfx::Point ipos(pos);
|
||||
if (!image->bounds().contains(ipos))
|
||||
return false;
|
||||
// For tilemaps
|
||||
if (image->pixelFormat() == IMAGE_TILEMAP) {
|
||||
ASSERT(cel->layer()->isTilemap());
|
||||
|
||||
output = get_pixel(image, ipos.x, ipos.y);
|
||||
return true;
|
||||
auto layerTilemap = static_cast<doc::LayerTilemap*>(cel->layer());
|
||||
doc::Grid grid = layerTilemap->tileset()->grid();
|
||||
grid.origin(grid.origin() + cel->position());
|
||||
|
||||
gfx::Point tilePos = grid.canvasToTile(gfx::Point(pos));
|
||||
PICKER_TRACE("PICKER: tilePos=(%d %d)\n", tilePos.x,tilePos.y);
|
||||
if (!image->bounds().contains(tilePos))
|
||||
return false;
|
||||
|
||||
const doc::tile_index ti =
|
||||
get_pixel(image, tilePos.x, tilePos.y);
|
||||
|
||||
PICKER_TRACE("PICKER: tile index=%d\n", ti);
|
||||
|
||||
doc::ImageRef tile = layerTilemap->tileset()->get(ti);
|
||||
if (!tile)
|
||||
return false;
|
||||
|
||||
const gfx::Point ipos =
|
||||
gfx::Point(pos) - grid.tileToCanvas(tilePos);
|
||||
|
||||
PICKER_TRACE("PICKER: ipos=%d %d\n", ipos.x, ipos.y);
|
||||
|
||||
output = get_pixel(tile.get(), ipos.x, ipos.y);
|
||||
PICKER_TRACE("PICKER: output=%d\n", output);
|
||||
return true;
|
||||
}
|
||||
// Regular images
|
||||
else {
|
||||
pos.x = (pos.x-celBounds.x)*image->width()/celBounds.w;
|
||||
pos.y = (pos.y-celBounds.y)*image->height()/celBounds.h;
|
||||
const gfx::Point ipos(pos);
|
||||
if (!image->bounds().contains(ipos))
|
||||
return false;
|
||||
|
||||
output = get_pixel(image, ipos.x, ipos.y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -88,31 +126,54 @@ void ColorPicker::pickColor(const Site& site,
|
||||
|
||||
// Pick from the composed image
|
||||
case FromComposition: {
|
||||
m_color = app::Color::fromImage(
|
||||
sprite->pixelFormat(),
|
||||
render::get_sprite_pixel(sprite, pos.x, pos.y,
|
||||
site.frame(), proj,
|
||||
Preferences::instance().experimental.newBlend()));
|
||||
|
||||
doc::CelList cels;
|
||||
sprite->pickCels(pos.x, pos.y, site.frame(), kOpacityThreshold,
|
||||
sprite->allVisibleLayers(), cels);
|
||||
if (!cels.empty())
|
||||
m_layer = cels.front()->layer();
|
||||
|
||||
if (site.tilemapMode() == TilemapMode::Tiles) {
|
||||
if (!cels.empty()) {
|
||||
const gfx::Point tilePos = site.grid().canvasToTile(gfx::Point(pos));
|
||||
if (cels.front()->image()->bounds().contains(tilePos)) {
|
||||
m_color = app::Color::fromIndex(
|
||||
doc::get_pixel(cels.front()->image(), tilePos.x, tilePos.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (site.tilemapMode() == TilemapMode::Pixels) {
|
||||
m_color = app::Color::fromImage(
|
||||
sprite->pixelFormat(),
|
||||
render::get_sprite_pixel(sprite, pos.x, pos.y,
|
||||
site.frame(), proj,
|
||||
Preferences::instance().experimental.newBlend()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Pick from the current layer
|
||||
case FromActiveLayer: {
|
||||
const Cel* cel = site.cel();
|
||||
if (cel) {
|
||||
if (!cel)
|
||||
return;
|
||||
|
||||
if (site.tilemapMode() == TilemapMode::Tiles) {
|
||||
const gfx::Point tilePos = site.grid().canvasToTile(gfx::Point(pos));
|
||||
if (cel->image()->bounds().contains(tilePos)) {
|
||||
m_color = app::Color::fromIndex(
|
||||
doc::get_pixel(cel->image(), tilePos.x, tilePos.y));
|
||||
}
|
||||
}
|
||||
else if (site.tilemapMode() == TilemapMode::Pixels) {
|
||||
doc::color_t imageColor;
|
||||
if (!get_cel_pixel(cel, pos.x, pos.y,
|
||||
site.frame(), imageColor))
|
||||
return;
|
||||
|
||||
const doc::Image* image = cel->image();
|
||||
switch (image->pixelFormat()) {
|
||||
doc::PixelFormat pixelFormat =
|
||||
(cel->layer()->isTilemap() ? sprite->pixelFormat():
|
||||
cel->image()->pixelFormat());
|
||||
switch (pixelFormat) {
|
||||
case IMAGE_RGB:
|
||||
m_alpha = doc::rgba_geta(imageColor);
|
||||
break;
|
||||
@ -121,7 +182,7 @@ void ColorPicker::pickColor(const Site& site,
|
||||
break;
|
||||
}
|
||||
|
||||
m_color = app::Color::fromImage(image->pixelFormat(), imageColor);
|
||||
m_color = app::Color::fromImage(pixelFormat, imageColor);
|
||||
m_layer = const_cast<Layer*>(site.layer());
|
||||
}
|
||||
break;
|
||||
|
@ -100,6 +100,9 @@ doc::color_t color_utils::color_for_image(const app::Color& color, PixelFormat f
|
||||
case IMAGE_INDEXED:
|
||||
c = color.getIndex();
|
||||
break;
|
||||
case IMAGE_TILEMAP:
|
||||
c = color.getIndex(); // TODO Add app::Color::getTile() ?
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
@ -122,6 +125,9 @@ doc::color_t color_utils::color_for_image_without_alpha(const app::Color& color,
|
||||
case IMAGE_INDEXED:
|
||||
c = color.getIndex();
|
||||
break;
|
||||
case IMAGE_TILEMAP:
|
||||
c = color.getIndex(); // TODO Add app::Color::getTile() ?
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
@ -165,6 +171,9 @@ doc::color_t color_utils::color_for_target_mask(const app::Color& color, const C
|
||||
c = get_current_palette()->findBestfit(r, g, b, a, mask);
|
||||
}
|
||||
break;
|
||||
case IMAGE_TILEMAP:
|
||||
c = color.getIndex(); // TODO Add app::Color::getTile() ?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -43,7 +43,10 @@ bool BackgroundFromLayerCommand::onEnabled(Context* context)
|
||||
// Doesn't have a background layer
|
||||
!context->checkFlags(ContextFlags::HasBackgroundLayer) &&
|
||||
// Isn't a reference layer
|
||||
!context->checkFlags(ContextFlags::ActiveLayerIsReference);
|
||||
!context->checkFlags(ContextFlags::ActiveLayerIsReference) &&
|
||||
// Isn't a tilemap layer
|
||||
// TODO support background tilemaps
|
||||
!context->checkFlags(ContextFlags::ActiveLayerIsTilemap);
|
||||
}
|
||||
|
||||
void BackgroundFromLayerCommand::onExecute(Context* context)
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -61,11 +62,9 @@ void ClearCelCommand::onExecute(Context* context)
|
||||
continue;
|
||||
}
|
||||
|
||||
LayerImage* layerImage = static_cast<LayerImage*>(layer);
|
||||
|
||||
for (frame_t frame : site->selectedFrames().reversed()) {
|
||||
if (layerImage->cel(frame))
|
||||
document->getApi(tx).clearCel(layerImage, frame);
|
||||
if (Cel* cel = layer->cel(frame))
|
||||
document->getApi(tx).clearCel(cel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -8,22 +9,18 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/color.h"
|
||||
#include "app/color_picker.h"
|
||||
#include "app/commands/cmd_eyedropper.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/context.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/site.h"
|
||||
#include "app/tools/tool.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "ui/manager.h"
|
||||
#include "ui/system.h"
|
||||
|
||||
@ -65,6 +62,11 @@ void EyedropperCommand::pickSample(const Site& site,
|
||||
|
||||
app::Color picked = picker.color();
|
||||
|
||||
if (site.tilemapMode() == TilemapMode::Tiles) {
|
||||
color = app::Color::fromIndex(picked.getIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channel) {
|
||||
case app::gen::EyedropperChannel::COLOR_ALPHA:
|
||||
color = picked;
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -75,8 +75,8 @@ void MaskContentCommand::onExecute(Context* context)
|
||||
|
||||
Mask newMask;
|
||||
gfx::Rect imgBounds = cel->image()->bounds();
|
||||
if (algorithm::shrink_bounds(cel->image(), imgBounds, color)) {
|
||||
newMask.replace(imgBounds.offset(cel->bounds().origin()));
|
||||
if (algorithm::shrink_cel_bounds(cel, color, imgBounds)) {
|
||||
newMask.replace(imgBounds);
|
||||
}
|
||||
else {
|
||||
newMask.replace(cel->bounds());
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -26,7 +26,7 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/render.h"
|
||||
#include "render/rasterize.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
namespace app {
|
||||
@ -57,11 +57,15 @@ bool MergeDownLayerCommand::onEnabled(Context* context)
|
||||
return false;
|
||||
|
||||
const Layer* src_layer = reader.layer();
|
||||
if (!src_layer || !src_layer->isImage())
|
||||
if (!src_layer ||
|
||||
!src_layer->isImage() ||
|
||||
src_layer->isTilemap()) // TODO Add support to merge tilemaps (and groups!)
|
||||
return false;
|
||||
|
||||
const Layer* dst_layer = src_layer->getPrevious();
|
||||
if (!dst_layer || !dst_layer->isImage())
|
||||
if (!dst_layer ||
|
||||
!dst_layer->isImage() ||
|
||||
dst_layer->isTilemap()) // TODO Add support to merge tilemaps
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -72,10 +76,11 @@ void MergeDownLayerCommand::onExecute(Context* context)
|
||||
ContextWriter writer(context);
|
||||
Doc* document(writer.document());
|
||||
Sprite* sprite(writer.sprite());
|
||||
Tx tx(writer.context(), "Merge Down Layer", ModifyDocument);
|
||||
LayerImage* src_layer = static_cast<LayerImage*>(writer.layer());
|
||||
Layer* dst_layer = src_layer->getPrevious();
|
||||
|
||||
Tx tx(writer.context(), friendlyName(), ModifyDocument);
|
||||
|
||||
for (frame_t frpos = 0; frpos<sprite->totalFrames(); ++frpos) {
|
||||
// Get frames
|
||||
Cel* src_cel = src_layer->cel(frpos);
|
||||
@ -103,7 +108,8 @@ void MergeDownLayerCommand::onExecute(Context* context)
|
||||
// Copy this cel to the destination layer...
|
||||
|
||||
// Creating a copy of the image
|
||||
dst_image.reset(Image::createCopy(src_image));
|
||||
dst_image.reset(
|
||||
render::rasterize_with_cel_bounds(src_cel));
|
||||
|
||||
// Creating a copy of the cell
|
||||
dst_cel = new Cel(frpos, dst_image);
|
||||
@ -133,14 +139,10 @@ void MergeDownLayerCommand::onExecute(Context* context)
|
||||
bounds.y-dst_cel->y(),
|
||||
bounds.w, bounds.h, bgcolor));
|
||||
|
||||
// Merge src_image in new_image
|
||||
render::composite_image(
|
||||
new_image.get(), src_image,
|
||||
sprite->palette(src_cel->frame()),
|
||||
src_cel->x()-bounds.x,
|
||||
src_cel->y()-bounds.y,
|
||||
opacity,
|
||||
src_layer->blendMode());
|
||||
// Draw src_cel on new_image
|
||||
render::rasterize(
|
||||
new_image.get(), src_cel,
|
||||
-bounds.x, -bounds.y, false);
|
||||
|
||||
// First unlink the dst_cel
|
||||
if (dst_cel->links())
|
||||
|
@ -10,6 +10,7 @@
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/cmd/add_tileset.h"
|
||||
#include "app/cmd/clear_mask.h"
|
||||
#include "app/cmd/move_layer.h"
|
||||
#include "app/cmd/trim_cel.h"
|
||||
@ -27,11 +28,13 @@
|
||||
#include "app/tx.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui/tileset_selector.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/clipboard.h"
|
||||
#include "app/util/new_image_from_mask.h"
|
||||
#include "app/util/range_utils.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "fmt/format.h"
|
||||
@ -56,6 +59,7 @@ struct NewLayerParams : public NewParams {
|
||||
Param<std::string> name { this, std::string(), "name" };
|
||||
Param<bool> group { this, false, "group" };
|
||||
Param<bool> reference { this, false, "reference" };
|
||||
Param<bool> tilemap { this, false, "tilemap" };
|
||||
Param<bool> ask { this, false, "ask" };
|
||||
Param<bool> fromFile { this, false, { "fromFile", "from-file" } };
|
||||
Param<bool> fromClipboard { this, false, "fromClipboard" };
|
||||
@ -67,7 +71,7 @@ struct NewLayerParams : public NewParams {
|
||||
|
||||
class NewLayerCommand : public CommandWithNewParams<NewLayerParams> {
|
||||
public:
|
||||
enum class Type { Layer, Group, ReferenceLayer };
|
||||
enum class Type { Layer, Group, ReferenceLayer, TilemapLayer };
|
||||
enum class Place { AfterActiveLayer, BeforeActiveLayer, Top };
|
||||
|
||||
NewLayerCommand();
|
||||
@ -102,6 +106,10 @@ void NewLayerCommand::onLoadParams(const Params& commandParams)
|
||||
m_type = Type::Group;
|
||||
else if (params().reference())
|
||||
m_type = Type::ReferenceLayer;
|
||||
else if (params().tilemap())
|
||||
m_type = Type::TilemapLayer;
|
||||
else
|
||||
m_type = Type::Layer;
|
||||
|
||||
m_place = Place::AfterActiveLayer;
|
||||
if (params().top())
|
||||
@ -142,10 +150,10 @@ private:
|
||||
|
||||
void NewLayerCommand::onExecute(Context* context)
|
||||
{
|
||||
ContextWriter writer(context);
|
||||
ContextReader reader(context);
|
||||
Site site = context->activeSite();
|
||||
Doc* document(writer.document());
|
||||
Sprite* sprite(writer.sprite());
|
||||
Doc* document(reader.document());
|
||||
Sprite* sprite(reader.sprite());
|
||||
std::string name;
|
||||
|
||||
Doc* pasteDoc = nullptr;
|
||||
@ -183,21 +191,40 @@ void NewLayerCommand::onExecute(Context* context)
|
||||
return;
|
||||
}
|
||||
|
||||
// Information about the tileset to be used for new tilemaps
|
||||
TilesetSelector::Info tilesetInfo;
|
||||
tilesetInfo.newTileset = true;
|
||||
tilesetInfo.grid = context->activeSite().grid();
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
// If params specify to ask the user about the name...
|
||||
if (params().ask() && context->isUIAvailable()) {
|
||||
// We open the window to ask the name
|
||||
app::gen::NewLayer window;
|
||||
TilesetSelector* tilesetSelector = nullptr;
|
||||
window.name()->setText(name.c_str());
|
||||
window.name()->setMinSize(gfx::Size(128, 0));
|
||||
|
||||
// Tileset selector for new tilemaps
|
||||
const bool isTilemap = (m_type == Type::TilemapLayer);
|
||||
window.tilesetLabel()->setVisible(isTilemap);
|
||||
window.tilesetOptions()->setVisible(isTilemap);
|
||||
if (isTilemap) {
|
||||
tilesetSelector = new TilesetSelector(sprite, tilesetInfo);
|
||||
window.tilesetOptions()->addChild(tilesetSelector);
|
||||
}
|
||||
|
||||
window.openWindowInForeground();
|
||||
if (window.closer() != window.ok())
|
||||
return;
|
||||
|
||||
name = window.name()->text();
|
||||
if (tilesetSelector)
|
||||
tilesetInfo = tilesetSelector->getInfo();
|
||||
}
|
||||
#endif
|
||||
|
||||
ContextWriter writer(reader);
|
||||
LayerGroup* parent = sprite->root();
|
||||
Layer* activeLayer = writer.layer();
|
||||
SelectedLayers selLayers = site.selectedLayers();
|
||||
@ -236,6 +263,24 @@ void NewLayerCommand::onExecute(Context* context)
|
||||
layer->setReference(true);
|
||||
afterBackground = true;
|
||||
break;
|
||||
case Type::TilemapLayer: {
|
||||
tileset_index tsi;
|
||||
if (tilesetInfo.newTileset) {
|
||||
auto tileset = new Tileset(sprite, tilesetInfo.grid, 0);
|
||||
auto addTileset = new cmd::AddTileset(sprite, tileset);
|
||||
tx(addTileset);
|
||||
|
||||
tsi = addTileset->tilesetIndex();
|
||||
}
|
||||
else {
|
||||
tsi = tilesetInfo.tsi;
|
||||
}
|
||||
|
||||
layer = new LayerTilemap(sprite, tsi);
|
||||
layer->setName(name);
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(layer);
|
||||
@ -443,6 +488,8 @@ std::string NewLayerCommand::onGetFriendlyName() const
|
||||
text = fmt::format(Strings::commands_NewLayer_ViaCopy(), text);
|
||||
if (params().viaCut())
|
||||
text = fmt::format(Strings::commands_NewLayer_ViaCut(), text);
|
||||
if (params().ask())
|
||||
text = fmt::format(Strings::commands_NewLayer_WithDialog(), text);
|
||||
return text;
|
||||
}
|
||||
|
||||
@ -490,6 +537,7 @@ std::string NewLayerCommand::layerPrefix() const
|
||||
case Type::Layer: return Strings::commands_NewLayer_Layer();
|
||||
case Type::Group: return Strings::commands_NewLayer_Group();
|
||||
case Type::ReferenceLayer: return Strings::commands_NewLayer_ReferenceLayer();
|
||||
case Type::TilemapLayer: return Strings::commands_NewLayer_TilemapLayer();
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ void SelectTileCommand::onExecute(Context* ctx)
|
||||
mask->copyFrom(doc->mask());
|
||||
|
||||
{
|
||||
gfx::Rect gridBounds = doc->sprite()->gridBounds();
|
||||
gfx::Rect gridBounds = writer.site()->gridBounds();
|
||||
gfx::Point pos = current_editor->screenToEditor(ui::get_mouse_position());
|
||||
pos = snap_to_grid(gridBounds, pos, PreferSnapTo::BoxOrigin);
|
||||
gridBounds.setOrigin(pos);
|
||||
|
45
src/app/commands/cmd_toggle_tiles_mode.cpp
Normal file
45
src/app/commands/cmd_toggle_tiles_mode.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
class ToggleTilesModeCommand : public Command {
|
||||
public:
|
||||
ToggleTilesModeCommand()
|
||||
: Command(CommandId::ToggleTilesMode(), CmdUIOnlyFlag) {
|
||||
}
|
||||
|
||||
protected:
|
||||
bool onChecked(Context* context) override {
|
||||
auto colorBar = ColorBar::instance();
|
||||
return (colorBar->tilemapMode() == TilemapMode::Tiles);
|
||||
}
|
||||
|
||||
void onExecute(Context* context) override {
|
||||
auto colorBar = ColorBar::instance();
|
||||
colorBar->setTilemapMode(
|
||||
colorBar->tilemapMode() == TilemapMode::Pixels ?
|
||||
TilemapMode::Tiles:
|
||||
TilemapMode::Pixels);
|
||||
}
|
||||
};
|
||||
|
||||
Command* CommandFactory::createToggleTilesModeCommand()
|
||||
{
|
||||
return new ToggleTilesModeCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -16,6 +16,7 @@ FOR_EACH_COMMAND(ColorCurve)
|
||||
FOR_EACH_COMMAND(ColorQuantization)
|
||||
FOR_EACH_COMMAND(ConvolutionMatrix)
|
||||
FOR_EACH_COMMAND(CopyColors)
|
||||
FOR_EACH_COMMAND(CopyTiles)
|
||||
FOR_EACH_COMMAND(CropSprite)
|
||||
FOR_EACH_COMMAND(Despeckle)
|
||||
FOR_EACH_COMMAND(ExportSpriteSheet)
|
||||
@ -27,6 +28,7 @@ FOR_EACH_COMMAND(LayerFromBackground)
|
||||
FOR_EACH_COMMAND(LoadPalette)
|
||||
FOR_EACH_COMMAND(MergeDownLayer)
|
||||
FOR_EACH_COMMAND(MoveColors)
|
||||
FOR_EACH_COMMAND(MoveTiles)
|
||||
FOR_EACH_COMMAND(NewFile)
|
||||
FOR_EACH_COMMAND(NewFrame)
|
||||
FOR_EACH_COMMAND(NewLayer)
|
||||
@ -156,8 +158,10 @@ FOR_EACH_COMMAND(SwapCheckerboardColors)
|
||||
FOR_EACH_COMMAND(SwitchColors)
|
||||
FOR_EACH_COMMAND(SymmetryMode)
|
||||
FOR_EACH_COMMAND(TiledMode)
|
||||
FOR_EACH_COMMAND(TilesetMode)
|
||||
FOR_EACH_COMMAND(Timeline)
|
||||
FOR_EACH_COMMAND(TogglePreview)
|
||||
FOR_EACH_COMMAND(ToggleTilesMode)
|
||||
FOR_EACH_COMMAND(ToggleTimelineThumbnails)
|
||||
FOR_EACH_COMMAND(UndoHistory)
|
||||
FOR_EACH_COMMAND(UnlinkCel)
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "app/ui/palette_view.h"
|
||||
#include "app/ui/timeline/timeline.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/cel_ops.h"
|
||||
#include "app/util/range_utils.h"
|
||||
#include "doc/algorithm/shrink_bounds.h"
|
||||
#include "doc/cel.h"
|
||||
@ -222,7 +223,24 @@ void FilterManagerImpl::apply()
|
||||
gfx::Rect output;
|
||||
if (algorithm::shrink_bounds2(m_src.get(), m_dst.get(),
|
||||
m_bounds, output)) {
|
||||
if (m_cel->layer()->isBackground()) {
|
||||
if (m_cel->layer()->isTilemap()) {
|
||||
modify_tilemap_cel_region(
|
||||
*m_tx,
|
||||
m_cel,
|
||||
gfx::Region(output),
|
||||
m_site.tilesetMode(),
|
||||
[this](const doc::ImageRef& origTile,
|
||||
const gfx::Rect& tileBoundsInCanvas) -> doc::ImageRef {
|
||||
return ImageRef(
|
||||
crop_image(m_dst.get(),
|
||||
tileBoundsInCanvas.x,
|
||||
tileBoundsInCanvas.y,
|
||||
tileBoundsInCanvas.w,
|
||||
tileBoundsInCanvas.h,
|
||||
m_dst->maskColor()));
|
||||
});
|
||||
}
|
||||
else if (m_cel->layer()->isBackground()) {
|
||||
(*m_tx)(
|
||||
new cmd::CopyRegion(
|
||||
m_cel->image(),
|
||||
@ -459,10 +477,7 @@ void FilterManagerImpl::init(Cel* cel)
|
||||
throw InvalidAreaException();
|
||||
|
||||
m_cel = cel;
|
||||
m_src.reset(
|
||||
crop_image(
|
||||
cel->image(),
|
||||
gfx::Rect(m_site.sprite()->bounds()).offset(-cel->position()), 0));
|
||||
m_src = crop_cel_image(cel, 0);
|
||||
m_dst.reset(Image::createCopy(m_src.get()));
|
||||
|
||||
m_row = -1;
|
||||
|
88
src/app/commands/move_tiles_command.cpp
Normal file
88
src/app/commands/move_tiles_command.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/new_params.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/tx.h"
|
||||
#include "app/util/cel_ops.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/remap.h"
|
||||
#include "doc/tileset.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
struct MoveTilesParams : public NewParams {
|
||||
Param<int> before { this, 0, "before" };
|
||||
};
|
||||
|
||||
class MoveTilesCommand : public CommandWithNewParams<MoveTilesParams> {
|
||||
public:
|
||||
MoveTilesCommand(const bool copy)
|
||||
: CommandWithNewParams<MoveTilesParams>(
|
||||
(copy ? CommandId::CopyTiles():
|
||||
CommandId::MoveTiles()), CmdRecordableFlag)
|
||||
, m_copy(copy) { }
|
||||
|
||||
protected:
|
||||
bool onEnabled(Context* ctx) override {
|
||||
return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
||||
ContextFlags::HasActiveLayer |
|
||||
ContextFlags::ActiveLayerIsTilemap);
|
||||
}
|
||||
|
||||
void onExecute(Context* ctx) override {
|
||||
ContextWriter writer(ctx);
|
||||
doc::Layer* layer = writer.layer();
|
||||
if (!layer || !layer->isTilemap())
|
||||
return;
|
||||
|
||||
doc::Tileset* tileset = static_cast<LayerTilemap*>(layer)->tileset();
|
||||
ASSERT(tileset);
|
||||
if (!tileset)
|
||||
return;
|
||||
|
||||
PalettePicks picks = writer.site()->selectedTiles();
|
||||
if (picks.picks() == 0)
|
||||
return;
|
||||
|
||||
Tx tx(writer.context(), onGetFriendlyName(), ModifyDocument);
|
||||
const int beforeIndex = params().before();
|
||||
int currentEntry = picks.firstPick();
|
||||
|
||||
if (m_copy)
|
||||
copy_tiles_in_tileset(tx, tileset, picks, currentEntry, beforeIndex);
|
||||
else
|
||||
move_tiles_in_tileset(tx, tileset, picks, currentEntry, beforeIndex);
|
||||
|
||||
tx.commit();
|
||||
|
||||
ctx->setSelectedTiles(picks);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_copy;
|
||||
};
|
||||
|
||||
Command* CommandFactory::createMoveTilesCommand()
|
||||
{
|
||||
return new MoveTilesCommand(false);
|
||||
}
|
||||
|
||||
Command* CommandFactory::createCopyTilesCommand()
|
||||
{
|
||||
return new MoveTilesCommand(true);
|
||||
}
|
||||
|
||||
} // namespace app
|
67
src/app/commands/tileset_mode.cpp
Normal file
67
src/app/commands/tileset_mode.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
class TilesetModeCommand : public Command {
|
||||
public:
|
||||
TilesetModeCommand()
|
||||
: Command(CommandId::TilesetMode(), CmdUIOnlyFlag) {
|
||||
m_mode = TilesetMode::Auto;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void onLoadParams(const Params& params) override {
|
||||
std::string mode = params.get("mode");
|
||||
if (mode == "manual") m_mode = TilesetMode::Manual;
|
||||
else if (mode == "stack") m_mode = TilesetMode::Stack;
|
||||
else m_mode = TilesetMode::Auto;
|
||||
}
|
||||
|
||||
bool onChecked(Context* context) override {
|
||||
auto colorBar = ColorBar::instance();
|
||||
return (colorBar->tilesetMode() == m_mode);
|
||||
}
|
||||
|
||||
void onExecute(Context* context) override {
|
||||
auto colorBar = ColorBar::instance();
|
||||
colorBar->setTilesetMode(m_mode);
|
||||
}
|
||||
|
||||
std::string onGetFriendlyName() const override {
|
||||
std::string mode;
|
||||
switch (m_mode) {
|
||||
case TilesetMode::Manual: mode = Strings::commands_TilesetMode_Manual(); break;
|
||||
case TilesetMode::Auto: mode = Strings::commands_TilesetMode_Auto(); break;
|
||||
case TilesetMode::Stack: mode = Strings::commands_TilesetMode_Stack(); break;
|
||||
}
|
||||
return fmt::format(getBaseFriendlyName(), mode);
|
||||
}
|
||||
|
||||
private:
|
||||
TilesetMode m_mode;
|
||||
};
|
||||
|
||||
Command* CommandFactory::createTilesetModeCommand()
|
||||
{
|
||||
return new TilesetModeCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -44,7 +44,7 @@ public:
|
||||
m_view.attachToView(&m_textbox);
|
||||
m_button.setMinSize(gfx::Size(60*ui::guiscale(), 0));
|
||||
|
||||
Grid* grid = new Grid(1, false);
|
||||
ui::Grid* grid = new ui::Grid(1, false);
|
||||
grid->addChildInCell(&m_view, 1, 1, HORIZONTAL | VERTICAL);
|
||||
grid->addChildInCell(&m_button, 1, 1, CENTER);
|
||||
addChild(grid);
|
||||
|
@ -23,6 +23,12 @@
|
||||
#include "doc/layer.h"
|
||||
#include "ui/system.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tilesets.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
@ -104,6 +110,11 @@ void Context::setSelectedColors(const doc::PalettePicks& picks)
|
||||
onSetSelectedColors(picks);
|
||||
}
|
||||
|
||||
void Context::setSelectedTiles(const doc::PalettePicks& picks)
|
||||
{
|
||||
onSetSelectedTiles(picks);
|
||||
}
|
||||
|
||||
bool Context::hasModifiedDocuments() const
|
||||
{
|
||||
for (auto doc : documents())
|
||||
@ -175,6 +186,20 @@ void Context::executeCommand(Command* command, const Params& params)
|
||||
// TODO move this code to another place (e.g. a Workplace/Tabs widget)
|
||||
if (isUIAvailable())
|
||||
app_rebuild_documents_tabs();
|
||||
|
||||
#ifdef _DEBUG // Special checks for debugging purposes
|
||||
{
|
||||
Site site = activeSite();
|
||||
// Check that all tileset hash tables are valid
|
||||
if (site.sprite() &&
|
||||
site.sprite()->hasTilesets()) {
|
||||
for (Tileset* tileset : *site.sprite()->tilesets()) {
|
||||
if (tileset)
|
||||
tileset->assertValidHashTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch (base::Exception& e) {
|
||||
LOG(ERROR, "CTXT: Exception caught executing %s command\n%s\n",
|
||||
@ -256,6 +281,12 @@ void Context::onSetSelectedColors(const doc::PalettePicks& picks)
|
||||
activeSiteHandler()->setSelectedColorsInDoc(m_lastSelectedDoc, picks);
|
||||
}
|
||||
|
||||
void Context::onSetSelectedTiles(const doc::PalettePicks& picks)
|
||||
{
|
||||
if (m_lastSelectedDoc)
|
||||
activeSiteHandler()->setSelectedTilesInDoc(m_lastSelectedDoc, picks);
|
||||
}
|
||||
|
||||
ActiveSiteHandler* Context::activeSiteHandler() const
|
||||
{
|
||||
if (!m_activeSiteHandler)
|
||||
|
@ -91,6 +91,7 @@ namespace app {
|
||||
void setActiveFrame(doc::frame_t frame);
|
||||
void setRange(const DocRange& range);
|
||||
void setSelectedColors(const doc::PalettePicks& picks);
|
||||
void setSelectedTiles(const doc::PalettePicks& picks);
|
||||
bool hasModifiedDocuments() const;
|
||||
void notifyActiveSiteChanged();
|
||||
|
||||
@ -115,6 +116,7 @@ namespace app {
|
||||
virtual void onSetActiveFrame(const doc::frame_t frame);
|
||||
virtual void onSetRange(const DocRange& range);
|
||||
virtual void onSetSelectedColors(const doc::PalettePicks& picks);
|
||||
virtual void onSetSelectedTiles(const doc::PalettePicks& picks);
|
||||
virtual void onCloseDocument(Doc* doc);
|
||||
|
||||
Doc* lastSelectedDoc() { return m_lastSelectedDoc; }
|
||||
|
@ -99,6 +99,9 @@ void ContextFlags::updateFlagsFromSite(const Site& site)
|
||||
if (layer->isReference())
|
||||
m_flags |= ActiveLayerIsReference;
|
||||
|
||||
if (layer->isTilemap())
|
||||
m_flags |= ActiveLayerIsTilemap;
|
||||
|
||||
if (layer->isImage()) {
|
||||
m_flags |= ActiveLayerIsImage;
|
||||
|
||||
@ -113,6 +116,9 @@ void ContextFlags::updateFlagsFromSite(const Site& site)
|
||||
|
||||
if (site.selectedColors().picks() > 0)
|
||||
m_flags |= HasSelectedColors;
|
||||
|
||||
if (site.selectedTiles().picks() > 0)
|
||||
m_flags |= HasSelectedTiles;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -33,7 +33,9 @@ namespace app {
|
||||
ActiveLayerIsVisible = 1 << 11,
|
||||
ActiveLayerIsEditable = 1 << 12,
|
||||
ActiveLayerIsReference = 1 << 13,
|
||||
HasSelectedColors = 1 << 14,
|
||||
ActiveLayerIsTilemap = 1 << 14,
|
||||
HasSelectedColors = 1 << 15,
|
||||
HasSelectedTiles = 1 << 16,
|
||||
};
|
||||
|
||||
ContextFlags();
|
||||
|
@ -198,6 +198,7 @@ bool BackupObserver::saveDocData(Doc* doc)
|
||||
diff.frameDuration ? "frameDuration": "",
|
||||
diff.tags ? "tags": "",
|
||||
diff.palettes ? "palettes": "",
|
||||
diff.tilesets ? "tilesets": "",
|
||||
diff.layers ? "layers": "",
|
||||
diff.cels ? "cels": "",
|
||||
diff.images ? "images": "",
|
||||
|
15
src/app/crash/doc_format.h
Normal file
15
src/app/crash/doc_format.h
Normal file
@ -0,0 +1,15 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CRASH_DOC_FORMAT_H_INCLUDED
|
||||
#define APP_CRASH_DOC_FORMAT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#define DOC_FORMAT_VERSION_0 0 // Old version
|
||||
#define DOC_FORMAT_VERSION_1 1 // New version with tilesets
|
||||
#define DOC_FORMAT_VERSION_LAST 1
|
||||
|
||||
#endif
|
@ -12,6 +12,7 @@
|
||||
#include "app/crash/read_document.h"
|
||||
|
||||
#include "app/console.h"
|
||||
#include "app/crash/doc_format.h"
|
||||
#include "app/crash/internals.h"
|
||||
#include "app/doc.h"
|
||||
#include "base/clamp.h"
|
||||
@ -28,6 +29,7 @@
|
||||
#include "doc/frame.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "doc/slice.h"
|
||||
@ -37,6 +39,9 @@
|
||||
#include "doc/subobjects_io.h"
|
||||
#include "doc/tag.h"
|
||||
#include "doc/tag_io.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tileset_io.h"
|
||||
#include "doc/tilesets.h"
|
||||
#include "doc/user_data_io.h"
|
||||
#include "fixmath/fixmath.h"
|
||||
|
||||
@ -56,7 +61,8 @@ class Reader : public SubObjectsIO {
|
||||
public:
|
||||
Reader(const std::string& dir,
|
||||
base::task_token* t)
|
||||
: m_sprite(nullptr)
|
||||
: m_docFormatVer(DOC_FORMAT_VERSION_0)
|
||||
, m_sprite(nullptr)
|
||||
, m_dir(dir)
|
||||
, m_docId(0)
|
||||
, m_docVersions(nullptr)
|
||||
@ -178,6 +184,10 @@ private:
|
||||
Doc* readDocument(std::ifstream& s) {
|
||||
ObjectId sprId = read32(s);
|
||||
std::string filename = read_string(s);
|
||||
m_docFormatVer = read16(s);
|
||||
if (s.eof()) m_docFormatVer = DOC_FORMAT_VERSION_0;
|
||||
|
||||
TRACE("RECO: internal format version=%d\n", m_docFormatVer);
|
||||
|
||||
// Load DocumentInfo only
|
||||
if (m_loadInfo) {
|
||||
@ -199,6 +209,7 @@ private:
|
||||
}
|
||||
|
||||
Sprite* readSprite(std::ifstream& s) {
|
||||
// Header
|
||||
ColorMode mode = (ColorMode)read8(s);
|
||||
int w = read16(s);
|
||||
int h = read16(s);
|
||||
@ -242,6 +253,19 @@ private:
|
||||
Console().printf("Invalid number of frames #%d\n", nframes);
|
||||
}
|
||||
|
||||
// IDs of all tilesets
|
||||
if (m_docFormatVer >= DOC_FORMAT_VERSION_1) {
|
||||
int ntilesets = read32(s);
|
||||
if (ntilesets > 0 && ntilesets < 0xffffff) {
|
||||
for (int i=0; i<ntilesets; ++i) {
|
||||
ObjectId tilesetId = read32(s);
|
||||
Tileset* tileset = loadObject<Tileset*>("tset", tilesetId, &Reader::readTileset);
|
||||
if (tileset)
|
||||
spr->tilesets()->add(tileset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read layers
|
||||
int nlayers = read32(s);
|
||||
if (nlayers >= 1 && nlayers < 0xfffff) {
|
||||
@ -395,14 +419,27 @@ private:
|
||||
LayerFlags flags = (LayerFlags)read32(s);
|
||||
ObjectType type = (ObjectType)read16(s);
|
||||
ASSERT(type == ObjectType::LayerImage ||
|
||||
type == ObjectType::LayerGroup);
|
||||
type == ObjectType::LayerGroup ||
|
||||
type == ObjectType::LayerTilemap);
|
||||
|
||||
std::string name = read_string(s);
|
||||
std::unique_ptr<Layer> lay;
|
||||
|
||||
switch (type) {
|
||||
case ObjectType::LayerImage: {
|
||||
lay.reset(new LayerImage(m_sprite));
|
||||
|
||||
case ObjectType::LayerImage:
|
||||
case ObjectType::LayerTilemap: {
|
||||
switch (type) {
|
||||
case ObjectType::LayerImage:
|
||||
lay.reset(new LayerImage(m_sprite));
|
||||
break;
|
||||
case ObjectType::LayerTilemap: {
|
||||
tileset_index tilesetIndex = read32(s);
|
||||
lay.reset(new LayerTilemap(m_sprite, tilesetIndex));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lay->setName(name);
|
||||
lay->setFlags(flags);
|
||||
|
||||
@ -460,6 +497,10 @@ private:
|
||||
return read_palette(s);
|
||||
}
|
||||
|
||||
Tileset* readTileset(std::ifstream& s) {
|
||||
return read_tileset(s, m_sprite, false);
|
||||
}
|
||||
|
||||
Tag* readTag(std::ifstream& s) {
|
||||
return read_tag(s, false);
|
||||
}
|
||||
@ -498,6 +539,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
int m_docFormatVer;
|
||||
Sprite* m_sprite; // Used to pass the sprite in LayerImage() ctor
|
||||
std::string m_dir;
|
||||
ObjectVersion m_docId;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "app/crash/write_document.h"
|
||||
|
||||
#include "app/crash/doc_format.h"
|
||||
#include "app/crash/internals.h"
|
||||
#include "app/doc.h"
|
||||
#include "base/convert_to.h"
|
||||
@ -26,6 +27,7 @@
|
||||
#include "doc/frame.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "doc/slice.h"
|
||||
@ -34,6 +36,9 @@
|
||||
#include "doc/string_io.h"
|
||||
#include "doc/tag.h"
|
||||
#include "doc/tag_io.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tileset_io.h"
|
||||
#include "doc/tilesets.h"
|
||||
#include "doc/user_data_io.h"
|
||||
#include "fixmath/fixmath.h"
|
||||
|
||||
@ -72,6 +77,12 @@ public:
|
||||
if (!saveObject("pal", pal, &Writer::writePalette))
|
||||
return false;
|
||||
|
||||
if (spr->hasTilesets()) {
|
||||
for (Tileset* tset : *spr->tilesets())
|
||||
if (!saveObject("tset", tset, &Writer::writeTileset))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Tag* frtag : spr->tags())
|
||||
if (!saveObject("frtag", frtag, &Writer::writeFrameTag))
|
||||
return false;
|
||||
@ -135,20 +146,29 @@ private:
|
||||
bool writeDocumentFile(std::ofstream& s, Doc* doc) {
|
||||
write32(s, doc->sprite()->id());
|
||||
write_string(s, doc->filename());
|
||||
write16(s, DOC_FORMAT_VERSION_LAST);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeSprite(std::ofstream& s, Sprite* spr) {
|
||||
// Header
|
||||
write8(s, int(spr->colorMode()));
|
||||
write16(s, spr->width());
|
||||
write16(s, spr->height());
|
||||
write32(s, spr->transparentColor());
|
||||
write32(s, spr->totalFrames());
|
||||
|
||||
// Frame durations
|
||||
write32(s, spr->totalFrames());
|
||||
for (frame_t fr = 0; fr < spr->totalFrames(); ++fr)
|
||||
write32(s, spr->frameDuration(fr));
|
||||
|
||||
// IDs of all tilesets
|
||||
write32(s, spr->hasTilesets() ? spr->tilesets()->size(): 0);
|
||||
if (spr->hasTilesets()) {
|
||||
for (Tileset* tileset : *spr->tilesets())
|
||||
write32(s, tileset->id());
|
||||
}
|
||||
|
||||
// IDs of all main layers
|
||||
write32(s, spr->allLayersCount());
|
||||
writeAllLayersID(s, 0, spr->root());
|
||||
@ -216,7 +236,12 @@ private:
|
||||
|
||||
switch (lay->type()) {
|
||||
|
||||
case ObjectType::LayerImage: {
|
||||
case ObjectType::LayerImage:
|
||||
case ObjectType::LayerTilemap: {
|
||||
// Tileset index
|
||||
if (lay->type() == ObjectType::LayerTilemap)
|
||||
write32(s, static_cast<const LayerTilemap*>(lay)->tilesetIndex());
|
||||
|
||||
CelConstIterator it, begin = static_cast<const LayerImage*>(lay)->getCelBegin();
|
||||
CelConstIterator end = static_cast<const LayerImage*>(lay)->getCelEnd();
|
||||
|
||||
@ -263,6 +288,11 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeTileset(std::ofstream& s, Tileset* tileset) {
|
||||
write_tileset(s, tileset);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeFrameTag(std::ofstream& s, Tag* frameTag) {
|
||||
write_tag(s, frameTag);
|
||||
return true;
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "app/file/format_options.h"
|
||||
#include "app/flatten.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/util/create_cel_copy.h"
|
||||
#include "app/util/cel_ops.h"
|
||||
#include "base/memory.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer.h"
|
||||
@ -259,6 +259,13 @@ void Doc::notifySelectionBoundariesChanged()
|
||||
notify_observers<DocEvent&>(&DocObserver::onSelectionBoundariesChanged, ev);
|
||||
}
|
||||
|
||||
void Doc::notifyTilesetChanged(Tileset* tileset)
|
||||
{
|
||||
DocEvent ev(this);
|
||||
ev.tileset(tileset);
|
||||
notify_observers<DocEvent&>(&DocObserver::onTilesetChanged, ev);
|
||||
}
|
||||
|
||||
bool Doc::isModified() const
|
||||
{
|
||||
return !m_undo->isSavedState();
|
||||
@ -443,7 +450,8 @@ void Doc::copyLayerContent(const Layer* sourceLayer0, Doc* destDoc, Layer* destL
|
||||
it->second));
|
||||
}
|
||||
else {
|
||||
newCel.reset(create_cel_copy(sourceCel,
|
||||
newCel.reset(create_cel_copy(nullptr, // TODO add undo information?
|
||||
sourceCel,
|
||||
destLayer->sprite(),
|
||||
destLayer,
|
||||
sourceCel->frame()));
|
||||
|
@ -35,6 +35,7 @@ namespace doc {
|
||||
class Layer;
|
||||
class Mask;
|
||||
class Sprite;
|
||||
class Tileset;
|
||||
}
|
||||
|
||||
namespace gfx {
|
||||
@ -114,6 +115,7 @@ namespace app {
|
||||
void notifyCelCopied(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame);
|
||||
void notifySelectionChanged();
|
||||
void notifySelectionBoundariesChanged();
|
||||
void notifyTilesetChanged(Tileset* tileset);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// File related properties
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -270,20 +270,22 @@ bool DocApi::cropCel(LayerImage* layer,
|
||||
crop_image(image,
|
||||
paintPos.x, paintPos.y,
|
||||
newCelBounds.w, newCelBounds.h,
|
||||
m_document->bgColor(layer)));
|
||||
image->pixelFormat() == IMAGE_TILEMAP ?
|
||||
tile_i_notile : m_document->bgColor(layer)));
|
||||
|
||||
// Try to shrink the image ignoring transparent borders
|
||||
gfx::Rect frameBounds;
|
||||
if (doc::algorithm::shrink_bounds(newImage.get(),
|
||||
frameBounds,
|
||||
newImage->maskColor())) {
|
||||
newImage->maskColor(),
|
||||
layer, frameBounds)) {
|
||||
// In this case the new cel image can be even smaller
|
||||
if (frameBounds != newImage->bounds()) {
|
||||
newImage = ImageRef(
|
||||
crop_image(newImage.get(),
|
||||
frameBounds.x, frameBounds.y,
|
||||
frameBounds.w, frameBounds.h,
|
||||
m_document->bgColor(layer)));
|
||||
image->pixelFormat() == IMAGE_TILEMAP ?
|
||||
tile_i_notile : m_document->bgColor(layer)));
|
||||
|
||||
newCelPos += frameBounds.origin();
|
||||
}
|
||||
@ -532,8 +534,9 @@ void DocApi::setCelOpacity(Sprite* sprite, Cel* cel, int newOpacity)
|
||||
m_transaction.execute(new cmd::SetCelOpacity(cel, newOpacity));
|
||||
}
|
||||
|
||||
void DocApi::clearCel(LayerImage* layer, frame_t frame)
|
||||
void DocApi::clearCel(Layer* layer, frame_t frame)
|
||||
{
|
||||
ASSERT(layer->isImage());
|
||||
if (Cel* cel = layer->cel(frame))
|
||||
clearCel(cel);
|
||||
}
|
||||
@ -680,7 +683,11 @@ Layer* DocApi::duplicateLayerAfter(Layer* sourceLayer, LayerGroup* parent, Layer
|
||||
ASSERT(parent);
|
||||
std::unique_ptr<Layer> newLayerPtr;
|
||||
|
||||
if (sourceLayer->isImage())
|
||||
if (sourceLayer->isTilemap()) {
|
||||
newLayerPtr.reset(new LayerTilemap(sourceLayer->sprite(),
|
||||
static_cast<LayerTilemap*>(sourceLayer)->tilesetIndex()));
|
||||
}
|
||||
else if (sourceLayer->isImage())
|
||||
newLayerPtr.reset(new LayerImage(sourceLayer->sprite()));
|
||||
else if (sourceLayer->isGroup())
|
||||
newLayerPtr.reset(new LayerGroup(sourceLayer->sprite()));
|
||||
|
@ -75,7 +75,7 @@ namespace app {
|
||||
// Cels API
|
||||
void addCel(LayerImage* layer, Cel* cel);
|
||||
Cel* addCel(LayerImage* layer, frame_t frameNumber, const ImageRef& image);
|
||||
void clearCel(LayerImage* layer, frame_t frame);
|
||||
void clearCel(Layer* layer, frame_t frame);
|
||||
void clearCel(Cel* cel);
|
||||
void clearCelAndAllLinks(Cel* cel);
|
||||
void setCelPosition(Sprite* sprite, Cel* cel, int x, int y);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -15,10 +15,13 @@
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tag.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tilesets.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -70,7 +73,7 @@ DocDiff compare_docs(const Doc* a,
|
||||
}
|
||||
}
|
||||
|
||||
// Palettes layers
|
||||
// Palettes
|
||||
if (a->sprite()->getPalettes().size() != b->sprite()->getPalettes().size()) {
|
||||
const PalettesList& aPals = a->sprite()->getPalettes();
|
||||
const PalettesList& bPals = b->sprite()->getPalettes();
|
||||
@ -88,6 +91,35 @@ DocDiff compare_docs(const Doc* a,
|
||||
}
|
||||
}
|
||||
|
||||
// Compare tilesets
|
||||
const tile_index aTilesetSize = (a->sprite()->hasTilesets() ? a->sprite()->tilesets()->size(): 0);
|
||||
const tile_index bTilesetSize = (b->sprite()->hasTilesets() ? b->sprite()->tilesets()->size(): 0);
|
||||
if (aTilesetSize != bTilesetSize) {
|
||||
diff.anything = diff.tilesets = true;
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<aTilesetSize; ++i) {
|
||||
Tileset* aTileset = a->sprite()->tilesets()->get(i);
|
||||
Tileset* bTileset = b->sprite()->tilesets()->get(i);
|
||||
|
||||
if (aTileset->grid().tileSize() != bTileset->grid().tileSize() ||
|
||||
aTileset->size() != bTileset->size()) {
|
||||
diff.anything = diff.tilesets = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
for (tile_index ti=0; ti<aTileset->size(); ++ti) {
|
||||
if (!is_same_image(aTileset->get(ti).get(),
|
||||
bTileset->get(ti).get())) {
|
||||
diff.anything = diff.tilesets = true;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
done:;
|
||||
}
|
||||
|
||||
// Compare layers
|
||||
if (a->sprite()->allLayersCount() != b->sprite()->allLayersCount()) {
|
||||
diff.anything = diff.layers = true;
|
||||
@ -106,7 +138,9 @@ DocDiff compare_docs(const Doc* a,
|
||||
aLay->name() != bLay->name() ||
|
||||
aLay->flags() != bLay->flags() ||
|
||||
(aLay->isImage() && bLay->isImage() &&
|
||||
(((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity()))) {
|
||||
(((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity())) ||
|
||||
(aLay->isTilemap() && bLay->isTilemap() &&
|
||||
(((const LayerTilemap*)aLay)->tilesetIndex() != ((const LayerTilemap*)bLay)->tilesetIndex()))) {
|
||||
diff.anything = diff.layers = true;
|
||||
break;
|
||||
}
|
||||
@ -128,7 +162,7 @@ DocDiff compare_docs(const Doc* a,
|
||||
}
|
||||
if (aCel->image() && bCel->image()) {
|
||||
if (aCel->image()->bounds() != bCel->image()->bounds() ||
|
||||
count_diff_between_images(aCel->image(), bCel->image()))
|
||||
!is_same_image(aCel->image(), bCel->image()))
|
||||
diff.anything = diff.images = true;
|
||||
}
|
||||
else if (aCel->image() != bCel->image())
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -19,6 +19,7 @@ namespace app {
|
||||
bool frameDuration : 1;
|
||||
bool tags : 1;
|
||||
bool palettes : 1;
|
||||
bool tilesets : 1;
|
||||
bool layers : 1;
|
||||
bool cels : 1;
|
||||
bool images : 1;
|
||||
@ -32,6 +33,7 @@ namespace app {
|
||||
frameDuration(false),
|
||||
tags(false),
|
||||
palettes(false),
|
||||
tilesets(false),
|
||||
layers(false),
|
||||
cels(false),
|
||||
images(false),
|
||||
|
@ -20,6 +20,7 @@ namespace doc {
|
||||
class Slice;
|
||||
class Sprite;
|
||||
class Tag;
|
||||
class Tileset;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
@ -37,6 +38,7 @@ namespace app {
|
||||
, m_frame(0)
|
||||
, m_tag(nullptr)
|
||||
, m_slice(nullptr)
|
||||
, m_tileset(nullptr)
|
||||
, m_targetLayer(nullptr)
|
||||
, m_targetFrame(0) {
|
||||
}
|
||||
@ -51,6 +53,7 @@ namespace app {
|
||||
doc::frame_t frame() const { return m_frame; }
|
||||
doc::Tag* tag() const { return m_tag; }
|
||||
doc::Slice* slice() const { return m_slice; }
|
||||
doc::Tileset* tileset() const { return m_tileset; }
|
||||
const gfx::Region& region() const { return m_region; }
|
||||
|
||||
void sprite(doc::Sprite* sprite) { m_sprite = sprite; }
|
||||
@ -61,6 +64,7 @@ namespace app {
|
||||
void frame(doc::frame_t frame) { m_frame = frame; }
|
||||
void tag(doc::Tag* tag) { m_tag = tag; }
|
||||
void slice(doc::Slice* slice) { m_slice = slice; }
|
||||
void tileset(doc::Tileset* tileset) { m_tileset = tileset; }
|
||||
void region(const gfx::Region& rgn) { m_region = rgn; }
|
||||
|
||||
// Destination of the operation.
|
||||
@ -80,6 +84,7 @@ namespace app {
|
||||
doc::frame_t m_frame;
|
||||
doc::Tag* m_tag;
|
||||
doc::Slice* m_slice;
|
||||
doc::Tileset* m_tileset;
|
||||
gfx::Region m_region;
|
||||
|
||||
// For copy/move commands, the m_layer/m_frame are source of the
|
||||
|
@ -958,7 +958,11 @@ void DocExporter::captureSamples(Samples& samples,
|
||||
else if (m_ignoreEmptyCels)
|
||||
refColor = sprite->transparentColor();
|
||||
|
||||
if (!algorithm::shrink_bounds(sampleRender.get(), spriteBounds, frameBounds, refColor)) {
|
||||
if (!algorithm::shrink_bounds(sampleRender.get(),
|
||||
refColor,
|
||||
nullptr, // layer
|
||||
spriteBounds, // startBounds
|
||||
frameBounds)) { // output bounds
|
||||
// If shrink_bounds() returns false, it's because the whole
|
||||
// image is transparent (equal to the mask color).
|
||||
|
||||
|
@ -80,6 +80,9 @@ namespace app {
|
||||
// Slices
|
||||
virtual void onSliceNameChange(DocEvent& ev) { }
|
||||
|
||||
// The tileset has changed.
|
||||
virtual void onTilesetChanged(DocEvent& ev) { }
|
||||
|
||||
// Called to destroy the observable. (Here you could call "delete this".)
|
||||
virtual void dispose() { }
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -20,21 +21,29 @@ ExtraCel::ExtraCel()
|
||||
{
|
||||
}
|
||||
|
||||
void ExtraCel::create(doc::Sprite* sprite,
|
||||
void ExtraCel::create(const TilemapMode tilemapMode,
|
||||
doc::Sprite* sprite,
|
||||
const gfx::Rect& bounds,
|
||||
doc::frame_t frame,
|
||||
int opacity)
|
||||
const gfx::Size& imageSize,
|
||||
const doc::frame_t frame,
|
||||
const int opacity)
|
||||
{
|
||||
ASSERT(sprite);
|
||||
|
||||
doc::PixelFormat pixelFormat;
|
||||
if (tilemapMode == TilemapMode::Tiles)
|
||||
pixelFormat = doc::IMAGE_TILEMAP;
|
||||
else
|
||||
pixelFormat = sprite->pixelFormat();
|
||||
|
||||
if (!m_image ||
|
||||
m_image->pixelFormat() != sprite->pixelFormat() ||
|
||||
m_image->width() != bounds.w ||
|
||||
m_image->height() != bounds.h) {
|
||||
m_image->pixelFormat() != pixelFormat ||
|
||||
m_image->width() != imageSize.w ||
|
||||
m_image->height() != imageSize.h) {
|
||||
if (!m_imageBuffer)
|
||||
m_imageBuffer.reset(new doc::ImageBuffer(1));
|
||||
doc::Image* newImage = doc::Image::create(sprite->pixelFormat(),
|
||||
bounds.w, bounds.h,
|
||||
doc::Image* newImage = doc::Image::create(pixelFormat,
|
||||
imageSize.w, imageSize.h,
|
||||
m_imageBuffer);
|
||||
m_image.reset(newImage);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -9,6 +9,7 @@
|
||||
#define APP_EXTRA_CEL_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/tilemap_mode.h"
|
||||
#include "base/disable_copying.h"
|
||||
#include "doc/blend_mode.h"
|
||||
#include "doc/cel.h"
|
||||
@ -30,7 +31,12 @@ namespace app {
|
||||
public:
|
||||
ExtraCel();
|
||||
|
||||
void create(doc::Sprite* sprite, const gfx::Rect& bounds, doc::frame_t frame, int opacity);
|
||||
void create(const TilemapMode tilemapMode,
|
||||
doc::Sprite* sprite,
|
||||
const gfx::Rect& bounds,
|
||||
const gfx::Size& imageSize,
|
||||
const doc::frame_t frame,
|
||||
const int opacity);
|
||||
|
||||
render::ExtraType type() const { return m_type; }
|
||||
void setType(render::ExtraType type) { m_type = type; }
|
||||
|
@ -82,6 +82,58 @@ private:
|
||||
doc::Sprite* m_sprite;
|
||||
};
|
||||
|
||||
class ScanlinesGen {
|
||||
public:
|
||||
virtual ~ScanlinesGen() { }
|
||||
virtual gfx::Size getImageSize() = 0;
|
||||
virtual int getScanlineSize() = 0;
|
||||
virtual const uint8_t* getScanlineAddress(int y) = 0;
|
||||
};
|
||||
|
||||
class ImageScanlines : public ScanlinesGen {
|
||||
const Image* m_image;
|
||||
public:
|
||||
ImageScanlines(const Image* image) : m_image(image) { }
|
||||
gfx::Size getImageSize() override {
|
||||
return gfx::Size(m_image->width(),
|
||||
m_image->height());
|
||||
}
|
||||
int getScanlineSize() override {
|
||||
return doc::calculate_rowstride_bytes(
|
||||
m_image->pixelFormat(),
|
||||
m_image->width());
|
||||
}
|
||||
const uint8_t* getScanlineAddress(int y) override {
|
||||
return m_image->getPixelAddress(0, y);
|
||||
}
|
||||
};
|
||||
|
||||
class TilesetScanlines : public ScanlinesGen {
|
||||
const Tileset* m_tileset;
|
||||
public:
|
||||
TilesetScanlines(const Tileset* tileset) : m_tileset(tileset) { }
|
||||
gfx::Size getImageSize() override {
|
||||
return gfx::Size(m_tileset->grid().tileSize().w,
|
||||
m_tileset->grid().tileSize().h * m_tileset->size());
|
||||
}
|
||||
int getScanlineSize() override {
|
||||
return doc::calculate_rowstride_bytes(
|
||||
m_tileset->sprite()->pixelFormat(),
|
||||
m_tileset->grid().tileSize().w);
|
||||
}
|
||||
const uint8_t* getScanlineAddress(int y) override {
|
||||
const int h = m_tileset->grid().tileSize().h;
|
||||
const tile_index ti = (y / h);
|
||||
ASSERT(ti >= 0 && ti < m_tileset->size());
|
||||
ImageRef image = m_tileset->get(ti);
|
||||
ASSERT(image);
|
||||
if (image)
|
||||
return image->getPixelAddress(0, y % h);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static void ase_file_prepare_header(FILE* f, dio::AsepriteHeader* header, const Sprite* sprite,
|
||||
@ -129,6 +181,19 @@ static void ase_file_write_slice_chunks(FILE* f, dio::AsepriteFrameHeader* frame
|
||||
static void ase_file_write_slice_chunk(FILE* f, dio::AsepriteFrameHeader* frame_header, Slice* slice,
|
||||
const frame_t fromFrame, const frame_t toFrame);
|
||||
static void ase_file_write_user_data_chunk(FILE* f, dio::AsepriteFrameHeader* frame_header, const UserData* userData);
|
||||
static void ase_file_write_external_files_chunk(FILE* f,
|
||||
dio::AsepriteFrameHeader* frame_header,
|
||||
dio::AsepriteExternalFiles& ext_files,
|
||||
const Sprite* sprite);
|
||||
static void ase_file_write_tileset_chunks(FILE* f, FileOp* fop,
|
||||
dio::AsepriteFrameHeader* frame_header,
|
||||
const dio::AsepriteExternalFiles& ext_files,
|
||||
const Tilesets* tilesets);
|
||||
static void ase_file_write_tileset_chunk(FILE* f, FileOp* fop,
|
||||
dio::AsepriteFrameHeader* frame_header,
|
||||
const dio::AsepriteExternalFiles& ext_files,
|
||||
const Tileset* tileset,
|
||||
const tileset_index si);
|
||||
static bool ase_has_groups(LayerGroup* group);
|
||||
static void ase_ungroup_all(LayerGroup* group);
|
||||
|
||||
@ -276,6 +341,7 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
|
||||
// Write frames
|
||||
int outputFrame = 0;
|
||||
dio::AsepriteExternalFiles ext_files;
|
||||
for (frame_t frame : fop->roi().selectedFrames()) {
|
||||
// Prepare the frame header
|
||||
dio::AsepriteFrameHeader frame_header;
|
||||
@ -284,9 +350,14 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
// Frame duration
|
||||
frame_header.duration = sprite->frameDuration(frame);
|
||||
|
||||
// Save color profile in first frame
|
||||
if (outputFrame == 0 && fop->preserveColorProfile())
|
||||
ase_file_write_color_profile(f, &frame_header, sprite);
|
||||
if (outputFrame == 0) {
|
||||
// Check if we need the "external files" chunk
|
||||
ase_file_write_external_files_chunk(f, &frame_header, ext_files, sprite);
|
||||
|
||||
// Save color profile in first frame
|
||||
if (fop->preserveColorProfile())
|
||||
ase_file_write_color_profile(f, &frame_header, sprite);
|
||||
}
|
||||
|
||||
// is the first frame or did the palette change?
|
||||
Palette* pal = sprite->palette(frame);
|
||||
@ -307,6 +378,10 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
|
||||
// Write extra chunks in the first frame
|
||||
if (frame == fop->roi().fromFrame()) {
|
||||
// Write tilesets
|
||||
ase_file_write_tileset_chunks(f, fop, &frame_header, ext_files,
|
||||
sprite->tilesets());
|
||||
|
||||
// Write layer chunks
|
||||
for (Layer* child : sprite->root()->layers())
|
||||
ase_file_write_layers(f, &frame_header, child, 0);
|
||||
@ -317,7 +392,7 @@ bool AseFormat::onSave(FileOp* fop)
|
||||
fop->roi().fromFrame(),
|
||||
fop->roi().toFrame());
|
||||
|
||||
// Writer slice chunks
|
||||
// Write slice chunks
|
||||
ase_file_write_slice_chunks(f, &frame_header,
|
||||
sprite->slices(),
|
||||
fop->roi().fromFrame(),
|
||||
@ -587,8 +662,15 @@ static void ase_file_write_layer_chunk(FILE* f, dio::AsepriteFrameHeader* frame_
|
||||
static_cast<int>(doc::LayerFlags::PersistentFlagsMask), f);
|
||||
|
||||
// Layer type
|
||||
fputw((layer->isImage() ? ASE_FILE_LAYER_IMAGE:
|
||||
(layer->isGroup() ? ASE_FILE_LAYER_GROUP: -1)), f);
|
||||
int layerType = ASE_FILE_LAYER_IMAGE;
|
||||
if (layer->isImage()) {
|
||||
if (layer->isTilemap())
|
||||
layerType = ASE_FILE_LAYER_TILEMAP;
|
||||
}
|
||||
else if (layer->isGroup()) {
|
||||
layerType = ASE_FILE_LAYER_GROUP;
|
||||
}
|
||||
fputw(layerType, f);
|
||||
|
||||
// Layer child level
|
||||
fputw(child_level, f);
|
||||
@ -604,6 +686,10 @@ static void ase_file_write_layer_chunk(FILE* f, dio::AsepriteFrameHeader* frame_
|
||||
|
||||
// Layer name
|
||||
ase_file_write_string(f, layer->name());
|
||||
|
||||
// Tileset index
|
||||
if (layer->isTilemap())
|
||||
fputl(static_cast<const LayerTilemap*>(layer)->tilesetIndex(), f);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -626,14 +712,12 @@ public:
|
||||
fputc(rgba_getb(c), f);
|
||||
fputc(rgba_geta(c), f);
|
||||
}
|
||||
void write_scanline(RgbTraits::address_t address, int w, uint8_t* buffer)
|
||||
{
|
||||
for (int x=0; x<w; ++x) {
|
||||
void write_scanline(RgbTraits::address_t address, int w, uint8_t* buffer) {
|
||||
for (int x=0; x<w; ++x, ++address) {
|
||||
*(buffer++) = rgba_getr(*address);
|
||||
*(buffer++) = rgba_getg(*address);
|
||||
*(buffer++) = rgba_getb(*address);
|
||||
*(buffer++) = rgba_geta(*address);
|
||||
++address;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -645,12 +729,10 @@ public:
|
||||
fputc(graya_getv(c), f);
|
||||
fputc(graya_geta(c), f);
|
||||
}
|
||||
void write_scanline(GrayscaleTraits::address_t address, int w, uint8_t* buffer)
|
||||
{
|
||||
for (int x=0; x<w; ++x) {
|
||||
void write_scanline(GrayscaleTraits::address_t address, int w, uint8_t* buffer) {
|
||||
for (int x=0; x<w; ++x, ++address) {
|
||||
*(buffer++) = graya_getv(*address);
|
||||
*(buffer++) = graya_geta(*address);
|
||||
++address;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -661,12 +743,27 @@ public:
|
||||
void write_pixel(FILE* f, IndexedTraits::pixel_t c) {
|
||||
fputc(c, f);
|
||||
}
|
||||
void write_scanline(IndexedTraits::address_t address, int w, uint8_t* buffer)
|
||||
{
|
||||
void write_scanline(IndexedTraits::address_t address, int w, uint8_t* buffer) {
|
||||
memcpy(buffer, address, w);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class PixelIO<TilemapTraits> {
|
||||
public:
|
||||
void write_pixel(FILE* f, TilemapTraits::pixel_t c) {
|
||||
fputl(c, f);
|
||||
}
|
||||
void write_scanline(TilemapTraits::address_t address, int w, uint8_t* buffer) {
|
||||
for (int x=0; x<w; ++x, ++address) {
|
||||
*(buffer++) = ((*address) & 0x000000ffl);
|
||||
*(buffer++) = ((*address) & 0x0000ff00l) >> 8;
|
||||
*(buffer++) = ((*address) & 0x00ff0000l) >> 16;
|
||||
*(buffer++) = ((*address) & 0xff000000l) >> 24;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Raw Image
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -687,7 +784,7 @@ static void write_raw_image(FILE* f, const Image* image)
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename ImageTraits>
|
||||
static void write_compressed_image(FILE* f, const Image* image)
|
||||
static void write_compressed_image_templ(FILE* f, ScanlinesGen* gen)
|
||||
{
|
||||
PixelIO<ImageTraits> pixel_io;
|
||||
z_stream zstream;
|
||||
@ -700,18 +797,19 @@ static void write_compressed_image(FILE* f, const Image* image)
|
||||
if (err != Z_OK)
|
||||
throw base::Exception("ZLib error %d in deflateInit().", err);
|
||||
|
||||
std::vector<uint8_t> scanline(ImageTraits::getRowStrideBytes(image->width()));
|
||||
std::vector<uint8_t> scanline(gen->getScanlineSize());
|
||||
std::vector<uint8_t> compressed(4096);
|
||||
|
||||
for (y=0; y<image->height(); y++) {
|
||||
const gfx::Size imgSize = gen->getImageSize();
|
||||
for (y=0; y<imgSize.h; ++y) {
|
||||
typename ImageTraits::address_t address =
|
||||
(typename ImageTraits::address_t)image->getPixelAddress(0, y);
|
||||
(typename ImageTraits::address_t)gen->getScanlineAddress(y);
|
||||
|
||||
pixel_io.write_scanline(address, image->width(), &scanline[0]);
|
||||
pixel_io.write_scanline(address, imgSize.w, &scanline[0]);
|
||||
|
||||
zstream.next_in = (Bytef*)&scanline[0];
|
||||
zstream.avail_in = scanline.size();
|
||||
int flush = (y == image->height()-1 ? Z_FINISH: Z_NO_FLUSH);
|
||||
int flush = (y == imgSize.h-1 ? Z_FINISH: Z_NO_FLUSH);
|
||||
|
||||
do {
|
||||
zstream.next_out = (Bytef*)&compressed[0];
|
||||
@ -736,6 +834,27 @@ static void write_compressed_image(FILE* f, const Image* image)
|
||||
throw base::Exception("ZLib error %d in deflateEnd().", err);
|
||||
}
|
||||
|
||||
static void write_compressed_image(FILE* f, ScanlinesGen* gen, PixelFormat pixelFormat)
|
||||
{
|
||||
switch (pixelFormat) {
|
||||
case IMAGE_RGB:
|
||||
write_compressed_image_templ<RgbTraits>(f, gen);
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
write_compressed_image_templ<GrayscaleTraits>(f, gen);
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
write_compressed_image_templ<IndexedTraits>(f, gen);
|
||||
break;
|
||||
|
||||
case IMAGE_TILEMAP:
|
||||
write_compressed_image_templ<TilemapTraits>(f, gen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Cel Chunk
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -764,7 +883,9 @@ static void ase_file_write_cel_chunk(FILE* f, dio::AsepriteFrameHeader* frame_he
|
||||
link = nullptr;
|
||||
}
|
||||
|
||||
int cel_type = (link ? ASE_FILE_LINK_CEL: ASE_FILE_COMPRESSED_CEL);
|
||||
int cel_type = (link ? ASE_FILE_LINK_CEL:
|
||||
cel->layer()->isTilemap() ? ASE_FILE_COMPRESSED_TILEMAP:
|
||||
ASE_FILE_COMPRESSED_CEL);
|
||||
|
||||
fputw(layer_index, f);
|
||||
fputw(cel->x(), f);
|
||||
@ -797,6 +918,7 @@ static void ase_file_write_cel_chunk(FILE* f, dio::AsepriteFrameHeader* frame_he
|
||||
case IMAGE_INDEXED:
|
||||
write_raw_image<IndexedTraits>(f, image);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -814,27 +936,14 @@ static void ase_file_write_cel_chunk(FILE* f, dio::AsepriteFrameHeader* frame_he
|
||||
|
||||
case ASE_FILE_COMPRESSED_CEL: {
|
||||
const Image* image = cel->image();
|
||||
|
||||
ASSERT(image);
|
||||
if (image) {
|
||||
// Width and height
|
||||
fputw(image->width(), f);
|
||||
fputw(image->height(), f);
|
||||
|
||||
// Pixel data
|
||||
switch (image->pixelFormat()) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
write_compressed_image<RgbTraits>(f, image);
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
write_compressed_image<GrayscaleTraits>(f, image);
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
write_compressed_image<IndexedTraits>(f, image);
|
||||
break;
|
||||
}
|
||||
ImageScanlines scan(image);
|
||||
write_compressed_image(f, &scan, image->pixelFormat());
|
||||
}
|
||||
else {
|
||||
// Width and height
|
||||
@ -843,6 +952,24 @@ static void ase_file_write_cel_chunk(FILE* f, dio::AsepriteFrameHeader* frame_he
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ASE_FILE_COMPRESSED_TILEMAP: {
|
||||
const Image* image = cel->image();
|
||||
ASSERT(image);
|
||||
ASSERT(image->pixelFormat() == IMAGE_TILEMAP);
|
||||
|
||||
fputw(image->width(), f);
|
||||
fputw(image->height(), f);
|
||||
fputw(32, f); // TODO use different bpp when possible
|
||||
fputl(tile_i_mask, f);
|
||||
fputl(tile_f_flipx, f);
|
||||
fputl(tile_f_flipy, f);
|
||||
fputl(tile_f_90cw, f);
|
||||
ase_file_write_padding(f, 10);
|
||||
|
||||
ImageScanlines scan(image);
|
||||
write_compressed_image(f, &scan, IMAGE_TILEMAP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1087,6 +1214,101 @@ static void ase_file_write_slice_chunk(FILE* f, dio::AsepriteFrameHeader* frame_
|
||||
}
|
||||
}
|
||||
|
||||
static void ase_file_write_external_files_chunk(
|
||||
FILE* f,
|
||||
dio::AsepriteFrameHeader* frame_header,
|
||||
dio::AsepriteExternalFiles& ext_files,
|
||||
const Sprite* sprite)
|
||||
{
|
||||
for (const Tileset* tileset : *sprite->tilesets()) {
|
||||
if (!tileset->externalFilename().empty()) {
|
||||
auto id = ++ext_files.lastid;
|
||||
auto fn = tileset->externalFilename();
|
||||
ext_files.to_fn[id] = fn;
|
||||
ext_files.to_id[fn] = id;
|
||||
}
|
||||
}
|
||||
|
||||
// No external files to write
|
||||
if (ext_files.lastid == 0)
|
||||
return;
|
||||
|
||||
fputl(ext_files.to_fn.size(), f); // Number of entries
|
||||
ase_file_write_padding(f, 8);
|
||||
for (auto item : ext_files.to_fn) {
|
||||
fputl(item.first, f); // ID
|
||||
ase_file_write_padding(f, 8);
|
||||
ase_file_write_string(f, item.second); // Filename
|
||||
}
|
||||
}
|
||||
|
||||
static void ase_file_write_tileset_chunks(FILE* f, FileOp* fop,
|
||||
dio::AsepriteFrameHeader* frame_header,
|
||||
const dio::AsepriteExternalFiles& ext_files,
|
||||
const Tilesets* tilesets)
|
||||
{
|
||||
tileset_index si = 0;
|
||||
for (const Tileset* tileset : *tilesets) {
|
||||
ase_file_write_tileset_chunk(f, fop, frame_header, ext_files,
|
||||
tileset, si);
|
||||
++si;
|
||||
}
|
||||
}
|
||||
|
||||
static void ase_file_write_tileset_chunk(FILE* f, FileOp* fop,
|
||||
dio::AsepriteFrameHeader* frame_header,
|
||||
const dio::AsepriteExternalFiles& ext_files,
|
||||
const Tileset* tileset,
|
||||
const tileset_index si)
|
||||
{
|
||||
ChunkWriter chunk(f, frame_header, ASE_FILE_CHUNK_TILESET);
|
||||
int flags = 0;
|
||||
if (!tileset->externalFilename().empty())
|
||||
flags |= ASE_TILESET_FLAG_EXTERNAL_FILE;
|
||||
else
|
||||
flags |= ASE_TILESET_FLAG_EMBEDDED;
|
||||
|
||||
fputl(si, f); // Tileset ID
|
||||
fputl(flags, f); // Tileset Flags (2=include tiles inside file)
|
||||
fputl(tileset->size(), f);
|
||||
fputw(tileset->grid().tileSize().w, f);
|
||||
fputw(tileset->grid().tileSize().h, f);
|
||||
ase_file_write_padding(f, 16);
|
||||
ase_file_write_string(f, tileset->name()); // tileset name
|
||||
|
||||
// Flag 1 = external tileset
|
||||
if (flags & ASE_TILESET_FLAG_EXTERNAL_FILE) {
|
||||
auto it = ext_files.to_id.find(tileset->externalFilename());
|
||||
if (it != ext_files.to_id.end()) {
|
||||
auto file_id = it->second;
|
||||
fputl(file_id, f);
|
||||
fputl(tileset->externalTileset(), f);
|
||||
}
|
||||
else {
|
||||
ASSERT(false); // Impossible state (corrupted memory or we
|
||||
// forgot to add the tileset external file to
|
||||
// "ext_files")
|
||||
|
||||
fputl(0, f);
|
||||
fputl(0, f);
|
||||
fop->setError("Error writing tileset external reference.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Flag 2 = tileset
|
||||
if (flags & ASE_TILESET_FLAG_EMBEDDED) {
|
||||
size_t beg = ftell(f);
|
||||
fputl(0, f); // Field for compressed data length (completed later)
|
||||
TilesetScanlines gen(tileset);
|
||||
write_compressed_image(f, &gen, tileset->sprite()->pixelFormat());
|
||||
|
||||
size_t end = ftell(f);
|
||||
fseek(f, beg, SEEK_SET);
|
||||
fputl(end-beg-4, f); // Save the compressed data length
|
||||
fseek(f, end, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ase_has_groups(LayerGroup* group)
|
||||
{
|
||||
for (Layer* child : group->layers()) {
|
||||
|
@ -596,7 +596,8 @@ void FileOp::operate(IFileOpProgress* progress)
|
||||
auto add_image = [&]() {
|
||||
canvasSize |= m_seq.image->size();
|
||||
|
||||
m_seq.last_cel->data()->setImage(m_seq.image);
|
||||
m_seq.last_cel->data()->setImage(m_seq.image,
|
||||
m_seq.layer);
|
||||
m_seq.layer->addCel(m_seq.last_cel);
|
||||
|
||||
if (m_document->sprite()->palette(frame)
|
||||
|
@ -721,7 +721,7 @@ private:
|
||||
try {
|
||||
ImageRef celImage(Image::createCopy(m_currentImage.get()));
|
||||
try {
|
||||
cel->data()->setImage(celImage);
|
||||
cel->data()->setImage(celImage, m_layer);
|
||||
}
|
||||
catch (...) {
|
||||
throw;
|
||||
|
@ -388,6 +388,20 @@ int App_useTool(lua_State* L)
|
||||
params.freehandAlgorithm = get_value_from_lua<tools::FreehandAlgorithm>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Are we going to modify pixels or tiles?
|
||||
type = lua_getfield(L, 1, "tilemapMode");
|
||||
if (type != LUA_TNIL) {
|
||||
site.tilemapMode(TilemapMode(lua_tointeger(L, -1)));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
// How the tileset must be modified depending on this tool usage
|
||||
type = lua_getfield(L, 1, "tilesetMode");
|
||||
if (type != LUA_TNIL) {
|
||||
site.tilesetMode(TilesetMode(lua_tointeger(L, -1)));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Do the tool loop
|
||||
type = lua_getfield(L, 1, "points");
|
||||
if (type == LUA_TTABLE) {
|
||||
|
@ -135,11 +135,22 @@ app::Color Color_new(lua_State* L, int index)
|
||||
}
|
||||
else
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Convert { index } into a Color
|
||||
if (lua_getfield(L, index, "index") != LUA_TNIL) {
|
||||
color = app::Color::fromIndex(lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
return color;
|
||||
}
|
||||
else
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
// raw color into app color
|
||||
else if (!lua_isnone(L, index)) {
|
||||
if (lua_isinteger(L, index) && (index < 0 || lua_isnone(L, index+1))) {
|
||||
doc::color_t docColor = lua_tointeger(L, index);
|
||||
|
||||
// TODO depending on current pixel format?
|
||||
switch (app_get_current_pixel_format()) {
|
||||
case IMAGE_RGB:
|
||||
color = app::Color::fromRgb(doc::rgba_getr(docColor),
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "app/script/luacpp.h"
|
||||
#include "app/script/security.h"
|
||||
#include "app/sprite_sheet_type.h"
|
||||
#include "app/tilemap_mode.h"
|
||||
#include "app/tileset_mode.h"
|
||||
#include "app/tools/ink_type.h"
|
||||
#include "base/chrono.h"
|
||||
#include "base/file_handle.h"
|
||||
@ -156,6 +158,7 @@ void register_dialog_class(lua_State* L);
|
||||
#endif
|
||||
void register_frame_class(lua_State* L);
|
||||
void register_frames_class(lua_State* L);
|
||||
void register_grid_class(lua_State* L);
|
||||
void register_image_class(lua_State* L);
|
||||
void register_image_iterator_class(lua_State* L);
|
||||
void register_image_spec_class(lua_State* L);
|
||||
@ -177,6 +180,8 @@ void register_sprite_class(lua_State* L);
|
||||
void register_sprites_class(lua_State* L);
|
||||
void register_tag_class(lua_State* L);
|
||||
void register_tags_class(lua_State* L);
|
||||
void register_tileset_class(lua_State* L);
|
||||
void register_tilesets_class(lua_State* L);
|
||||
void register_tool_class(lua_State* L);
|
||||
void register_version_class(lua_State* L);
|
||||
|
||||
@ -255,6 +260,7 @@ Engine::Engine()
|
||||
setfield_integer(L, "GRAY", doc::ColorMode::GRAYSCALE);
|
||||
setfield_integer(L, "GRAYSCALE", doc::ColorMode::GRAYSCALE);
|
||||
setfield_integer(L, "INDEXED", doc::ColorMode::INDEXED);
|
||||
setfield_integer(L, "TILEMAP", doc::ColorMode::TILEMAP);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_newtable(L);
|
||||
@ -367,6 +373,21 @@ Engine::Engine()
|
||||
setfield_integer(L, "X2", (int)ui::kButtonX2);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, "TilemapMode");
|
||||
setfield_integer(L, "PIXELS", TilemapMode::Pixels);
|
||||
setfield_integer(L, "TILES", TilemapMode::Tiles);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, "TilesetMode");
|
||||
setfield_integer(L, "MANUAL", TilesetMode::Manual);
|
||||
setfield_integer(L, "AUTO", TilesetMode::Auto);
|
||||
setfield_integer(L, "STACK", TilesetMode::Stack);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Register classes/prototypes
|
||||
register_brush_class(L);
|
||||
register_cel_class(L);
|
||||
@ -378,6 +399,7 @@ Engine::Engine()
|
||||
#endif
|
||||
register_frame_class(L);
|
||||
register_frames_class(L);
|
||||
register_grid_class(L);
|
||||
register_image_class(L);
|
||||
register_image_iterator_class(L);
|
||||
register_image_spec_class(L);
|
||||
@ -399,6 +421,8 @@ Engine::Engine()
|
||||
register_sprites_class(L);
|
||||
register_tag_class(L);
|
||||
register_tags_class(L);
|
||||
register_tileset_class(L);
|
||||
register_tilesets_class(L);
|
||||
register_tool_class(L);
|
||||
register_version_class(L);
|
||||
|
||||
|
@ -45,6 +45,8 @@ namespace doc {
|
||||
class Palette;
|
||||
class Sprite;
|
||||
class Tag;
|
||||
class Tileset;
|
||||
class Tilesets;
|
||||
class WithUserData;
|
||||
}
|
||||
|
||||
@ -146,6 +148,9 @@ namespace app {
|
||||
void push_sprite_slices(lua_State* L, doc::Sprite* sprite);
|
||||
void push_sprite_tags(lua_State* L, doc::Sprite* sprite);
|
||||
void push_sprites(lua_State* L);
|
||||
void push_tileset(lua_State* L, doc::Tileset* tileset);
|
||||
void push_tileset_image(lua_State* L, doc::Tileset* tileset, doc::Image* image);
|
||||
void push_tilesets(lua_State* L, doc::Tilesets* tilesets);
|
||||
void push_tool(lua_State* L, app::tools::Tool* tool);
|
||||
void push_userdata(lua_State* L, doc::WithUserData* userData);
|
||||
void push_version(lua_State* L, const base::Version& ver);
|
||||
|
94
src/app/script/grid_class.cpp
Normal file
94
src/app/script/grid_class.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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/script/docobj.h"
|
||||
#include "app/script/engine.h"
|
||||
#include "app/script/luacpp.h"
|
||||
#include "doc/grid.h"
|
||||
|
||||
namespace app {
|
||||
namespace script {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
namespace {
|
||||
|
||||
doc::Grid Grid_new(lua_State* L, int index)
|
||||
{
|
||||
doc::Grid grid;
|
||||
// Copy other size
|
||||
if (auto grid2 = may_get_obj<doc::Grid>(L, index)) {
|
||||
grid = *grid2;
|
||||
}
|
||||
// Convert Rectangle into a Grid
|
||||
else if (lua_istable(L, index)) {
|
||||
gfx::Rect rect = convert_args_into_rect(L, index);
|
||||
doc::Grid grid(rect.size());
|
||||
grid.origin(rect.origin());
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
int Grid_new(lua_State* L)
|
||||
{
|
||||
push_obj(L, Grid_new(L, 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Grid_gc(lua_State* L)
|
||||
{
|
||||
get_obj<doc::Grid>(L, 1)->~Grid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Grid_get_tileSize(lua_State* L)
|
||||
{
|
||||
auto grid = get_obj<doc::Grid>(L, 1);
|
||||
push_obj(L, grid->tileSize());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Grid_get_origin(lua_State* L)
|
||||
{
|
||||
auto grid = get_obj<doc::Grid>(L, 1);
|
||||
push_obj(L, grid->origin());
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg Grid_methods[] = {
|
||||
{ "__gc", Grid_gc },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
const Property Grid_properties[] = {
|
||||
{ "tileSize", Grid_get_tileSize, nullptr },
|
||||
{ "origin", Grid_get_origin, nullptr },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
DEF_MTNAME(Grid);
|
||||
|
||||
void register_grid_class(lua_State* L)
|
||||
{
|
||||
using Grid = doc::Grid;
|
||||
REG_CLASS(L, Grid);
|
||||
REG_CLASS_NEW(L, Grid);
|
||||
REG_CLASS_PROPERTIES(L, Grid);
|
||||
}
|
||||
|
||||
doc::Grid convert_args_into_grid(lua_State* L, int index)
|
||||
{
|
||||
return Grid_new(L, index);
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
@ -43,6 +43,7 @@ namespace {
|
||||
struct ImageObj {
|
||||
doc::ObjectId imageId = 0;
|
||||
doc::ObjectId celId = 0;
|
||||
doc::ObjectId tilesetId = 0;
|
||||
ImageObj(doc::Image* image)
|
||||
: imageId(image->id()) {
|
||||
}
|
||||
@ -50,6 +51,10 @@ struct ImageObj {
|
||||
: imageId(cel->image()->id())
|
||||
, celId(cel->id()) {
|
||||
}
|
||||
ImageObj(doc::Tileset* tileset, doc::Image* image)
|
||||
: imageId(image->id())
|
||||
, tilesetId(tileset->id()) {
|
||||
}
|
||||
ImageObj(const ImageObj&) = delete;
|
||||
ImageObj& operator=(const ImageObj&) = delete;
|
||||
|
||||
@ -58,7 +63,7 @@ struct ImageObj {
|
||||
}
|
||||
|
||||
void gc(lua_State* L) {
|
||||
if (!celId)
|
||||
if (!celId && !tilesetId)
|
||||
delete this->image(L);
|
||||
imageId = 0;
|
||||
}
|
||||
@ -372,11 +377,11 @@ int Image_saveAs(lua_State* L)
|
||||
|
||||
std::unique_ptr<Sprite> sprite(Sprite::MakeStdSprite(img->spec(), 256));
|
||||
|
||||
std::vector<Image*> oneImage;
|
||||
std::vector<ImageRef> oneImage;
|
||||
sprite->getImages(oneImage);
|
||||
ASSERT(oneImage.size() == 1);
|
||||
if (!oneImage.empty())
|
||||
copy_image(oneImage.front(), img);
|
||||
copy_image(oneImage.front().get(), img);
|
||||
|
||||
if (pal)
|
||||
sprite->setPalette(pal, false);
|
||||
@ -568,6 +573,11 @@ void push_image(lua_State* L, doc::Image* image)
|
||||
push_new<ImageObj>(L, image);
|
||||
}
|
||||
|
||||
void push_tileset_image(lua_State* L, doc::Tileset* tileset, doc::Image* image)
|
||||
{
|
||||
push_new<ImageObj>(L, tileset, image);
|
||||
}
|
||||
|
||||
doc::Image* may_get_image_from_arg(lua_State* L, int index)
|
||||
{
|
||||
auto obj = may_get_obj<ImageObj>(L, index);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "app/tx.h"
|
||||
#include "base/clamp.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
@ -152,6 +153,13 @@ int Layer_get_isGroup(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Layer_get_isTilemap(lua_State* L)
|
||||
{
|
||||
auto layer = get_docobj<Layer>(L, 1);
|
||||
lua_pushboolean(L, layer->isTilemap());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Layer_get_isTransparent(lua_State* L)
|
||||
{
|
||||
auto layer = get_docobj<Layer>(L, 1);
|
||||
@ -208,6 +216,16 @@ int Layer_get_cels(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Layer_get_tileset(lua_State* L)
|
||||
{
|
||||
auto layer = get_docobj<Layer>(L, 1);
|
||||
if (layer->isTilemap())
|
||||
push_tileset(L, static_cast<doc::LayerTilemap*>(layer)->tileset());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Layer_set_name(lua_State* L)
|
||||
{
|
||||
auto layer = get_docobj<Layer>(L, 1);
|
||||
@ -370,6 +388,7 @@ const Property Layer_properties[] = {
|
||||
{ "blendMode", Layer_get_blendMode, Layer_set_blendMode },
|
||||
{ "isImage", Layer_get_isImage, nullptr },
|
||||
{ "isGroup", Layer_get_isGroup, nullptr },
|
||||
{ "isTilemap", Layer_get_isTilemap, nullptr },
|
||||
{ "isTransparent", Layer_get_isTransparent, nullptr },
|
||||
{ "isBackground", Layer_get_isBackground, nullptr },
|
||||
{ "isEditable", Layer_get_isEditable, Layer_set_isEditable },
|
||||
@ -380,6 +399,7 @@ const Property Layer_properties[] = {
|
||||
{ "cels", Layer_get_cels, nullptr },
|
||||
{ "color", UserData_get_color<Layer>, UserData_set_color<Layer> },
|
||||
{ "data", UserData_get_text<Layer>, UserData_set_text<Layer> },
|
||||
{ "tileset", Layer_get_tileset, nullptr },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "app/script/luacpp.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/tile.h"
|
||||
|
||||
namespace app {
|
||||
namespace script {
|
||||
@ -70,6 +71,26 @@ int PixelColor_grayaA(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PixelColor_tile(lua_State* L)
|
||||
{
|
||||
const int i = lua_tointeger(L, 1);
|
||||
const int f = lua_tointeger(L, 2);
|
||||
lua_pushinteger(L, doc::tile(i, f));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PixelColor_tileI(lua_State* L)
|
||||
{
|
||||
lua_pushinteger(L, doc::tile_geti(lua_tointeger(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PixelColor_tileF(lua_State* L)
|
||||
{
|
||||
lua_pushinteger(L, doc::tile_getf(lua_tointeger(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg PixelColor_methods[] = {
|
||||
{ "rgba", PixelColor_rgba },
|
||||
{ "rgbaR", PixelColor_rgbaR },
|
||||
@ -79,6 +100,9 @@ const luaL_Reg PixelColor_methods[] = {
|
||||
{ "graya", PixelColor_graya },
|
||||
{ "grayaV", PixelColor_grayaV },
|
||||
{ "grayaA", PixelColor_grayaA },
|
||||
{ "tile", PixelColor_tile },
|
||||
{ "tileI", PixelColor_tileI },
|
||||
{ "tileF", PixelColor_tileF },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/object_ids.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tile.h"
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
@ -36,6 +37,7 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
|
||||
std::vector<frame_t> frames;
|
||||
std::set<ObjectId> cels;
|
||||
std::vector<color_t> colors;
|
||||
std::vector<tile_index> tiles;
|
||||
|
||||
RangeObj(Site& site) {
|
||||
updateFromSite(site);
|
||||
@ -83,6 +85,9 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
|
||||
|
||||
if (site.selectedColors().picks() > 0)
|
||||
colors = site.selectedColors().toVectorOfIndexes();
|
||||
|
||||
if (site.selectedTiles().picks() > 0)
|
||||
tiles = site.selectedTiles().toVectorOfIndexes();
|
||||
}
|
||||
|
||||
Sprite* sprite(lua_State* L) { return check_docobj(L, doc::get<Sprite>(spriteId)); }
|
||||
@ -99,6 +104,9 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
|
||||
bool containsColor(const color_t color) const {
|
||||
return (std::find(colors.begin(), colors.end(), color) != colors.end());
|
||||
}
|
||||
bool containsTile(const tile_t tile) const {
|
||||
return (std::find(tiles.begin(), tiles.end(), tile) != tiles.end());
|
||||
}
|
||||
};
|
||||
|
||||
int Range_gc(lua_State* L)
|
||||
@ -142,11 +150,19 @@ int Range_contains(lua_State* L)
|
||||
int Range_containsColor(lua_State* L)
|
||||
{
|
||||
auto obj = get_obj<RangeObj>(L, 1);
|
||||
color_t color = lua_tointeger(L, 2);
|
||||
const color_t color = lua_tointeger(L, 2);
|
||||
lua_pushboolean(L, obj->containsColor(color));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Range_containsTile(lua_State* L)
|
||||
{
|
||||
auto obj = get_obj<RangeObj>(L, 1);
|
||||
const tile_index tile = lua_tointeger(L, 2);
|
||||
lua_pushboolean(L, obj->containsTile(tile));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Range_clear(lua_State* L)
|
||||
{
|
||||
auto obj = get_obj<RangeObj>(L, 1);
|
||||
@ -243,6 +259,18 @@ int Range_get_colors(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Range_get_tiles(lua_State* L)
|
||||
{
|
||||
auto obj = get_obj<RangeObj>(L, 1);
|
||||
lua_newtable(L);
|
||||
int j = 1;
|
||||
for (tile_index i : obj->tiles) {
|
||||
lua_pushinteger(L, i);
|
||||
lua_rawseti(L, -2, j++);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Range_set_layers(lua_State* L)
|
||||
{
|
||||
auto obj = get_obj<RangeObj>(L, 1);
|
||||
@ -308,10 +336,29 @@ int Range_set_colors(lua_State* L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Range_set_tiles(lua_State* L)
|
||||
{
|
||||
app::Context* ctx = App::instance()->context();
|
||||
doc::PalettePicks picks;
|
||||
if (lua_istable(L, 2)) {
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 2) != 0) {
|
||||
int i = lua_tointeger(L, -1);
|
||||
if (i >= picks.size())
|
||||
picks.resize(i+1);
|
||||
picks[i] = true;
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
ctx->setSelectedTiles(picks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg Range_methods[] = {
|
||||
{ "__gc", Range_gc },
|
||||
{ "contains", Range_contains },
|
||||
{ "containsColor", Range_containsColor },
|
||||
{ "containsTile", Range_containsTile },
|
||||
{ "clear", Range_clear },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
@ -326,6 +373,7 @@ const Property Range_properties[] = {
|
||||
{ "images", Range_get_images, nullptr },
|
||||
{ "editableImages", Range_get_editableImages, nullptr },
|
||||
{ "colors", Range_get_colors, Range_set_colors },
|
||||
{ "tiles", Range_get_tiles, Range_set_tiles },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -683,6 +683,13 @@ int Sprite_get_slices(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Sprite_get_tilesets(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
push_tilesets(L, sprite->tilesets());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Sprite_get_backgroundLayer(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
@ -820,6 +827,7 @@ const Property Sprite_properties[] = {
|
||||
{ "cels", Sprite_get_cels, nullptr },
|
||||
{ "tags", Sprite_get_tags, nullptr },
|
||||
{ "slices", Sprite_get_slices, nullptr },
|
||||
{ "tilesets", Sprite_get_tilesets, nullptr },
|
||||
{ "backgroundLayer", Sprite_get_backgroundLayer, nullptr },
|
||||
{ "transparentColor", Sprite_get_transparentColor, Sprite_set_transparentColor },
|
||||
{ "bounds", Sprite_get_bounds, nullptr },
|
||||
|
104
src/app/script/tileset_class.cpp
Normal file
104
src/app/script/tileset_class.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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/script/docobj.h"
|
||||
#include "app/script/engine.h"
|
||||
#include "app/script/luacpp.h"
|
||||
#include "doc/tileset.h"
|
||||
|
||||
namespace app {
|
||||
namespace script {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
namespace {
|
||||
|
||||
int Tileset_eq(lua_State* L)
|
||||
{
|
||||
const auto a = get_docobj<Tileset>(L, 1);
|
||||
const auto b = get_docobj<Tileset>(L, 2);
|
||||
lua_pushboolean(L, a->id() == b->id());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Tileset_len(lua_State* L)
|
||||
{
|
||||
auto tileset = get_docobj<Tileset>(L, 1);
|
||||
lua_pushinteger(L, tileset->size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Tileset_getTile(lua_State* L)
|
||||
{
|
||||
auto tileset = get_docobj<Tileset>(L, 1);
|
||||
tile_index i = lua_tointeger(L, 2);
|
||||
ImageRef image = tileset->get(i);
|
||||
if (image)
|
||||
push_tileset_image(L, tileset, image.get());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Tileset_get_name(lua_State* L)
|
||||
{
|
||||
auto tileset = get_docobj<Tileset>(L, 1);
|
||||
lua_pushstring(L, tileset->name().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Tileset_set_name(lua_State* L)
|
||||
{
|
||||
auto tileset = get_docobj<Tileset>(L, 1);
|
||||
if (const char* newName = lua_tostring(L, 2))
|
||||
tileset->setName(newName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Tileset_get_grid(lua_State* L)
|
||||
{
|
||||
auto tileset = get_docobj<Tileset>(L, 1);
|
||||
push_obj(L, tileset->grid());
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg Tileset_methods[] = {
|
||||
{ "__eq", Tileset_eq },
|
||||
{ "__len", Tileset_len },
|
||||
{ "getTile", Tileset_getTile },
|
||||
// TODO
|
||||
// { "setTile", Tileset_setTile },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
const Property Tileset_properties[] = {
|
||||
{ "name", Tileset_get_name, Tileset_set_name },
|
||||
{ "grid", Tileset_get_grid, nullptr },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
DEF_MTNAME(Tileset);
|
||||
|
||||
void register_tileset_class(lua_State* L)
|
||||
{
|
||||
using Tileset = doc::Tileset;
|
||||
REG_CLASS(L, Tileset);
|
||||
REG_CLASS_PROPERTIES(L, Tileset);
|
||||
}
|
||||
|
||||
void push_tileset(lua_State* L, Tileset* tileset)
|
||||
{
|
||||
push_docobj(L, tileset);
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
62
src/app/script/tilesets_class.cpp
Normal file
62
src/app/script/tilesets_class.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// 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/script/docobj.h"
|
||||
#include "app/script/engine.h"
|
||||
#include "app/script/luacpp.h"
|
||||
#include "doc/tilesets.h"
|
||||
|
||||
namespace app {
|
||||
namespace script {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
namespace {
|
||||
|
||||
int Tilesets_len(lua_State* L)
|
||||
{
|
||||
auto obj = get_docobj<Tilesets>(L, 1);
|
||||
lua_pushinteger(L, obj->size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Tilesets_index(lua_State* L)
|
||||
{
|
||||
auto obj = get_docobj<Tilesets>(L, 1);
|
||||
const int i = lua_tonumber(L, 2);
|
||||
if (i >= 1 && i <= int(obj->size()))
|
||||
push_docobj(L, *(obj->begin()+i-1));
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg Tilesets_methods[] = {
|
||||
{ "__len", Tilesets_len },
|
||||
{ "__index", Tilesets_index },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
DEF_MTNAME(Tilesets);
|
||||
|
||||
void register_tilesets_class(lua_State* L)
|
||||
{
|
||||
REG_CLASS(L, Tilesets);
|
||||
}
|
||||
|
||||
void push_tilesets(lua_State* L, Tilesets* tilesets)
|
||||
{
|
||||
push_docobj(L, tilesets);
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
@ -14,8 +14,11 @@
|
||||
#include "app/pref/preferences.h"
|
||||
#include "base/clamp.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/grid.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "ui/system.h"
|
||||
|
||||
namespace app {
|
||||
@ -32,15 +35,7 @@ RgbMap* Site::rgbMap() const
|
||||
return (m_sprite ? m_sprite->rgbMap(m_frame): nullptr);
|
||||
}
|
||||
|
||||
const Cel* Site::cel() const
|
||||
{
|
||||
if (m_layer)
|
||||
return m_layer->cel(m_frame);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Cel* Site::cel()
|
||||
Cel* Site::cel() const
|
||||
{
|
||||
if (m_layer)
|
||||
return m_layer->cel(m_frame);
|
||||
@ -79,8 +74,31 @@ void Site::range(const DocRange& range)
|
||||
}
|
||||
}
|
||||
|
||||
Grid Site::grid() const
|
||||
{
|
||||
if (m_layer && m_layer->isTilemap()) {
|
||||
doc::Grid grid = static_cast<LayerTilemap*>(m_layer)->tileset()->grid();
|
||||
if (const Cel* cel = m_layer->cel(m_frame))
|
||||
grid.origin(grid.origin() + cel->position());
|
||||
return grid;
|
||||
}
|
||||
|
||||
gfx::Rect rc = gridBounds();
|
||||
doc::Grid grid = Grid(rc.size());
|
||||
grid.origin(gfx::Point(rc.x % rc.w, rc.y % rc.h));
|
||||
return grid;
|
||||
}
|
||||
|
||||
gfx::Rect Site::gridBounds() const
|
||||
{
|
||||
if (m_layer && m_layer->isTilemap()) {
|
||||
const Grid& grid = static_cast<LayerTilemap*>(m_layer)->tileset()->grid();
|
||||
gfx::Point offset = grid.tileOffset();
|
||||
if (const Cel* cel = m_layer->cel(m_frame))
|
||||
offset += cel->bounds().origin();
|
||||
return gfx::Rect(offset, grid.tileSize());
|
||||
}
|
||||
|
||||
gfx::Rect bounds;
|
||||
if (m_sprite) {
|
||||
bounds = m_sprite->gridBounds();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -10,6 +10,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/doc_range.h"
|
||||
#include "app/tilemap_mode.h"
|
||||
#include "app/tileset_mode.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/palette_picks.h"
|
||||
#include "doc/selected_objects.h"
|
||||
@ -17,6 +19,7 @@
|
||||
|
||||
namespace doc {
|
||||
class Cel;
|
||||
class Grid;
|
||||
class Image;
|
||||
class Layer;
|
||||
class Palette;
|
||||
@ -49,7 +52,9 @@ namespace app {
|
||||
, m_document(nullptr)
|
||||
, m_sprite(nullptr)
|
||||
, m_layer(nullptr)
|
||||
, m_frame(0) { }
|
||||
, m_frame(0)
|
||||
, m_tilemapMode(TilemapMode::Pixels)
|
||||
, m_tilesetMode(TilesetMode::Manual) { }
|
||||
|
||||
const Focus focus() const { return m_focus; }
|
||||
bool inEditor() const { return m_focus == InEditor; }
|
||||
@ -59,16 +64,11 @@ namespace app {
|
||||
bool inColorBar() const { return m_focus == InColorBar; }
|
||||
bool inTimeline() const { return (inLayers() || inFrames() || inCels()); }
|
||||
|
||||
const Doc* document() const { return m_document; }
|
||||
const doc::Sprite* sprite() const { return m_sprite; }
|
||||
const doc::Layer* layer() const { return m_layer; }
|
||||
Doc* document() const { return m_document; }
|
||||
doc::Sprite* sprite() const { return m_sprite; }
|
||||
doc::Layer* layer() const { return m_layer; }
|
||||
doc::frame_t frame() const { return m_frame; }
|
||||
const doc::Cel* cel() const;
|
||||
|
||||
Doc* document() { return m_document; }
|
||||
doc::Sprite* sprite() { return m_sprite; }
|
||||
doc::Layer* layer() { return m_layer; }
|
||||
doc::Cel* cel();
|
||||
doc::Cel* cel() const;
|
||||
const DocRange& range() const { return m_range; }
|
||||
|
||||
void focus(Focus focus) { m_focus = focus; }
|
||||
@ -88,6 +88,13 @@ namespace app {
|
||||
m_selectedColors = colors;
|
||||
}
|
||||
|
||||
// Selected tiles selected in the ColorBar
|
||||
const doc::PalettePicks& selectedTiles() const { return m_selectedTiles; }
|
||||
doc::PalettePicks& selectedTiles() { return m_selectedTiles; }
|
||||
void selectedTiles(const doc::PalettePicks& tiles) {
|
||||
m_selectedTiles = tiles;
|
||||
}
|
||||
|
||||
const doc::SelectedObjects& selectedSlices() const { return m_selectedSlices; }
|
||||
doc::SelectedObjects& selectedSlices() { return m_selectedSlices; }
|
||||
void selectedSlices(const doc::SelectedObjects& set) {
|
||||
@ -99,8 +106,14 @@ namespace app {
|
||||
doc::Palette* palette() const;
|
||||
doc::RgbMap* rgbMap() const;
|
||||
|
||||
doc::Grid grid() const;
|
||||
gfx::Rect gridBounds() const;
|
||||
|
||||
void tilemapMode(const TilemapMode mode) { m_tilemapMode = mode; }
|
||||
void tilesetMode(const TilesetMode mode) { m_tilesetMode = mode; }
|
||||
TilemapMode tilemapMode() const { return m_tilemapMode; }
|
||||
TilesetMode tilesetMode() const { return m_tilesetMode; }
|
||||
|
||||
private:
|
||||
Focus m_focus;
|
||||
Doc* m_document;
|
||||
@ -109,7 +122,10 @@ namespace app {
|
||||
doc::frame_t m_frame;
|
||||
DocRange m_range;
|
||||
doc::PalettePicks m_selectedColors;
|
||||
doc::PalettePicks m_selectedTiles;
|
||||
doc::SelectedObjects m_selectedSlices;
|
||||
TilemapMode m_tilemapMode;
|
||||
TilesetMode m_tilesetMode;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -43,7 +43,7 @@ os::SurfaceRef get_cel_thumbnail(const doc::Cel* cel,
|
||||
|
||||
render::Render render;
|
||||
render::Projection proj(cel->sprite()->pixelRatio(),
|
||||
render::Zoom(newSize.w, cel->image()->width()));
|
||||
render::Zoom(newSize.w, cel->bounds().w));
|
||||
render.setProjection(proj);
|
||||
|
||||
const doc::Palette* palette = cel->sprite()->palette(cel->frame());
|
||||
|
21
src/app/tilemap_mode.h
Normal file
21
src/app/tilemap_mode.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_TILEMAP_MODE_H_INCLUDED
|
||||
#define APP_TILEMAP_MODE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace app {
|
||||
|
||||
// Should we edit the pixels or the tiles of the tilemap?
|
||||
enum class TilemapMode {
|
||||
Pixels, // Edit tile pixels
|
||||
Tiles, // Edit tiles
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user