mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Merge branch 'master' into tilemap-editor
This commit is contained in:
commit
bd1723313a
19
data/extensions/hardware-palettes/msx1.gpl
Normal file
19
data/extensions/hardware-palettes/msx1.gpl
Normal file
@ -0,0 +1,19 @@
|
||||
GIMP Palette
|
||||
Channels: RGBA
|
||||
#
|
||||
0 0 0 0 Transparent
|
||||
1 1 1 255 Black
|
||||
62 184 73 255 Medium green
|
||||
116 208 125 255 Light green
|
||||
89 85 224 255 Dark blue
|
||||
128 118 241 255 Light blue
|
||||
185 94 81 255 Dark red
|
||||
101 219 239 255 Cyan
|
||||
219 101 89 255 Medium red
|
||||
255 137 125 255 Light red
|
||||
204 195 94 255 Dark yellow
|
||||
222 208 135 255 Light yellow
|
||||
58 162 65 255 Dark green
|
||||
183 102 181 255 Magenta
|
||||
204 204 204 255 Gray
|
||||
255 255 255 255 White
|
19
data/extensions/hardware-palettes/msx2.gpl
Normal file
19
data/extensions/hardware-palettes/msx2.gpl
Normal file
@ -0,0 +1,19 @@
|
||||
GIMP Palette
|
||||
Channels: RGBA
|
||||
#
|
||||
0 0 0 0 Transparent
|
||||
1 1 1 255 Black
|
||||
36 219 36 255 Medium green
|
||||
109 255 109 255 Light green
|
||||
36 36 255 255 Dark blue
|
||||
73 109 255 255 Light blue
|
||||
182 36 36 255 Dark red
|
||||
73 219 255 255 Cyan
|
||||
255 36 36 255 Medium red
|
||||
255 109 109 255 Light red
|
||||
219 219 36 255 Dark yellow
|
||||
219 219 146 255 Light yellow
|
||||
36 146 36 255 Dark green
|
||||
219 73 182 255 Magenta
|
||||
182 182 182 255 Gray
|
||||
255 255 255 255 White
|
@ -26,6 +26,8 @@
|
||||
{ "id": "Game Boy", "path": "./gameboy.gpl" },
|
||||
{ "id": "Game Boy Color Type1", "path": "./gameboy-color-type1.gpl" },
|
||||
{ "id": "Master System", "path": "./master-system.gpl" },
|
||||
{ "id": "MSX1", "path": "./msx1.gpl" },
|
||||
{ "id": "MSX2", "path": "./msx2.gpl" },
|
||||
{ "id": "NES", "path": "./nes.gpl" },
|
||||
{ "id": "NES NTSC", "path": "./nes-ntsc.gpl" },
|
||||
{ "id": "Teletext", "path": "./teletext.gpl" },
|
||||
|
54
data/extensions/software-palettes/minecraft.gpl
Normal file
54
data/extensions/software-palettes/minecraft.gpl
Normal file
@ -0,0 +1,54 @@
|
||||
GIMP Palette
|
||||
Name: Minecraft
|
||||
#
|
||||
# Based on Minecraft 1.14 default texture
|
||||
# Order: wool, concrete, terracotta
|
||||
#
|
||||
234 237 237 white_wool
|
||||
241 119 22 orange_wool
|
||||
190 70 181 magenta_wool
|
||||
60 176 218 light_blue_wool
|
||||
249 198 41 yellow_wool
|
||||
113 186 26 lime_wool
|
||||
238 144 173 pink_wool
|
||||
63 69 72 gray_wool
|
||||
142 143 135 light_gray_wool
|
||||
21 138 145 cyan_wool
|
||||
123 43 173 purple_wool
|
||||
53 58 158 blue_wool
|
||||
115 72 41 brown_wool
|
||||
85 110 28 green_wool
|
||||
161 40 35 red_wool
|
||||
22 22 27 black_wool
|
||||
207 213 214 white_concrete
|
||||
224 97 1 orange_concrete
|
||||
169 48 159 magenta_concrete
|
||||
36 137 199 light_blue_concrete
|
||||
241 175 21 yellow_concrete
|
||||
94 169 25 lime_concrete
|
||||
214 101 143 pink_concrete
|
||||
55 58 62 gray_concrete
|
||||
125 125 115 light_gray_concrete
|
||||
21 119 136 cyan_concrete
|
||||
100 32 156 purple_concrete
|
||||
45 47 143 blue_concrete
|
||||
96 60 32 brown_concrete
|
||||
73 91 36 green_concrete
|
||||
142 33 33 red_concrete
|
||||
8 10 15 black_concrete
|
||||
210 178 161 white_terracotta
|
||||
162 84 38 orange_terracotta
|
||||
150 88 109 magenta_terracotta
|
||||
114 109 138 light_blue_terracotta
|
||||
186 133 35 yellow_terracotta
|
||||
104 118 53 lime_terracotta
|
||||
162 78 79 pink_terracotta
|
||||
58 42 36 gray_terracotta
|
||||
135 107 98 light_gray_terracotta
|
||||
87 91 91 cyan_terracotta
|
||||
118 70 86 purple_terracotta
|
||||
74 60 91 blue_terracotta
|
||||
77 51 36 brown_terracotta
|
||||
76 83 42 green_terracotta
|
||||
143 61 47 red_terracotta
|
||||
37 23 16 black_terracotta
|
@ -10,6 +10,7 @@
|
||||
"contributes": {
|
||||
"palettes": [
|
||||
{ "id": "Google UI", "path": "./google-ui.gpl" },
|
||||
{ "id": "Minecraft", "path": "./minecraft.gpl" },
|
||||
{ "id": "Monokai", "path": "./monokai.gpl" },
|
||||
{ "id": "SmileBASIC", "path": "./smile-basic.gpl" },
|
||||
{ "id": "Solarized", "path": "./solarized.gpl" },
|
||||
|
@ -21,7 +21,6 @@
|
||||
<key command="ExportSpriteSheet" shortcut="Ctrl+E" mac="Cmd+E" />
|
||||
<key command="RepeatLastExport" shortcut="Ctrl+Shift+X" mac="Cmd+Shift+X" />
|
||||
<key command="AdvancedMode" shortcut="Ctrl+F" />
|
||||
<key command="DeveloperConsole" shortcut="F12" />
|
||||
<key command="Exit" win="Ctrl+Q" linux="Ctrl+Q" mac="Cmd+Q" />
|
||||
<key command="Exit" win="Alt+F4" />
|
||||
<key command="Cancel" shortcut="Esc">
|
||||
@ -527,6 +526,9 @@
|
||||
<param name="save" value="true" />
|
||||
<param name="srgb" value="false" />
|
||||
</key>
|
||||
<key command="Screenshot" shortcut="F12">
|
||||
<param name="steam" value="true" />
|
||||
</key>
|
||||
</commands>
|
||||
|
||||
<!-- Keyboard shortcuts to select tools -->
|
||||
|
@ -143,6 +143,7 @@
|
||||
<option id="timeline_layer_panel_width" type="int" default="100" />
|
||||
<option id="show_menu_bar" type="bool" default="true" />
|
||||
<option id="recent_items" type="int" default="16" />
|
||||
<option id="osx_async_view" type="bool" default="true" />
|
||||
</section>
|
||||
<section id="undo" text="Undo">
|
||||
<option id="size_limit" type="int" default="0" />
|
||||
|
@ -13,6 +13,7 @@ sprite_without_profile = The sprite doesn't contain a color profile.
|
||||
|
||||
[statusbar_tips]
|
||||
all_layers_are_locked = All selected layers are locked
|
||||
layer_locked = Layer '{0}' is locked
|
||||
|
||||
[alerts]
|
||||
applying_filter = FX<<Applying effect...||&Cancel
|
||||
@ -433,6 +434,7 @@ SavePalette = Save Palette
|
||||
Screenshot = Screenshot
|
||||
Screenshot_Open = Take & Open Screenshot
|
||||
Screenshot_Save = Take & Save Screenshot
|
||||
Screenshot_Steam = Take & Add Screenshot to Steam
|
||||
Screenshot_sRGB = (sRGB Color Profile)
|
||||
Screenshot_DisplayCS = (Display Color Profile)
|
||||
Scroll = Scroll {0}
|
||||
@ -473,6 +475,7 @@ SpriteProperties = Sprite Properties
|
||||
SpriteSize = Sprite Size
|
||||
Stroke = Stroke Selection Borders with Foreground Color
|
||||
SwitchColors = Switch Colors
|
||||
SwapCheckerboardColors = Swap Checkerboard Background Colors
|
||||
SwitchNonactiveLayersOpacity = Switch Nonactive Layers Opacity
|
||||
SymmetryMode = Symmetry Mode
|
||||
TiledMode = Tiled Mode
|
||||
|
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit af0f8e7b53b9e3e689b5fa4e5ce1466c42c9e2aa
|
||||
Subproject commit 9c6ecadd9cbcfd2b87b7cfa61e01466013da3797
|
@ -32,7 +32,7 @@ because they don't depend on any other component.
|
||||
|
||||
## Level 2
|
||||
|
||||
* [doc](doc/) (base, fixmath, gfx, os): Document model library.
|
||||
* [doc](doc/) (base, fixmath, gfx): Document model library.
|
||||
* [ui](ui/) (base, gfx, os): Portable UI library (buttons, windows, text fields, etc.)
|
||||
* [updater](updater/) (base, cfg, net): Component to check for updates.
|
||||
|
||||
|
@ -286,6 +286,7 @@ if(ENABLE_UI)
|
||||
commands/cmd_show.cpp
|
||||
commands/cmd_slice_properties.cpp
|
||||
commands/cmd_sprite_properties.cpp
|
||||
commands/cmd_swap_checkerboard_colors.cpp
|
||||
commands/cmd_switch_colors.cpp
|
||||
commands/cmd_symmetry_mode.cpp
|
||||
commands/cmd_tiled_mode.cpp
|
||||
@ -611,6 +612,7 @@ add_library(app-lib
|
||||
util/autocrop.cpp
|
||||
util/buffer_region.cpp
|
||||
util/cel_ops.cpp
|
||||
util/conversion_to_surface.cpp
|
||||
util/expand_cel_canvas.cpp
|
||||
util/filetoks.cpp
|
||||
util/freetype_utils.cpp
|
||||
|
@ -69,6 +69,10 @@
|
||||
#include "ui/ui.h"
|
||||
#include "ver/info.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "os/osx/system.h"
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
@ -239,6 +243,11 @@ int App::initialize(const AppOptions& options)
|
||||
system->setTabletAPI(os::TabletAPI::Wintab);
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (!preferences().general.osxAsyncView())
|
||||
os::osx_set_async_view(false);
|
||||
#endif
|
||||
|
||||
system->setAppName(get_app_name());
|
||||
system->setAppMode(m_isGui ? os::AppMode::GUI:
|
||||
os::AppMode::CLI);
|
||||
|
@ -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
|
||||
@ -134,7 +134,11 @@ void CopyCel::onFireNotifications()
|
||||
{
|
||||
CmdSequence::onFireNotifications();
|
||||
|
||||
ASSERT(m_srcLayer.layer());
|
||||
// The m_srcLayer can be nullptr now because the layer from where we
|
||||
// copied this cel might not exist anymore (e.g. if we copied the
|
||||
// cel from another document that is already closed)
|
||||
//ASSERT(m_srcLayer.layer());
|
||||
|
||||
ASSERT(m_dstLayer.layer());
|
||||
|
||||
static_cast<Doc*>(m_dstLayer.layer()->sprite()->document())
|
||||
|
@ -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,12 +9,11 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cmd/background_from_layer.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/doc_api.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/tx.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
@ -56,9 +55,8 @@ void BackgroundFromLayerCommand::onExecute(Context* context)
|
||||
Doc* document(writer.document());
|
||||
|
||||
{
|
||||
Tx tx(writer.context(), "Background from Layer");
|
||||
document->getApi(tx).backgroundFromLayer(
|
||||
static_cast<LayerImage*>(writer.layer()));
|
||||
Tx tx(writer.context(), friendlyName());
|
||||
tx(new cmd::BackgroundFromLayer(static_cast<LayerImage*>(writer.layer())));
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -21,7 +21,7 @@
|
||||
#include "app/ui/editor/editor_render.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
@ -231,7 +231,7 @@ protected:
|
||||
break;
|
||||
}
|
||||
|
||||
doc::convert_image_to_surface(m_doublebuf.get(), m_pal,
|
||||
convert_image_to_surface(m_doublebuf.get(), m_pal,
|
||||
m_doublesur, 0, 0, 0, 0, m_doublebuf->width(), m_doublebuf->height());
|
||||
g->blit(m_doublesur, 0, 0, 0, 0, m_doublesur->width(), m_doublesur->height());
|
||||
}
|
||||
|
@ -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,14 +9,13 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cmd/layer_from_background.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/doc_api.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/tx.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -51,8 +51,8 @@ void LayerFromBackgroundCommand::onExecute(Context* context)
|
||||
ContextWriter writer(context);
|
||||
Doc* document(writer.document());
|
||||
{
|
||||
Tx tx(writer.context(), "Layer from Background");
|
||||
document->getApi(tx).layerFromBackground(writer.layer());
|
||||
Tx tx(writer.context(), friendlyName());
|
||||
tx(new cmd::LayerFromBackground(writer.layer()));
|
||||
tx.commit();
|
||||
}
|
||||
#ifdef ENABLE_UI
|
||||
|
53
src/app/commands/cmd_swap_checkerboard_colors.cpp
Normal file
53
src/app/commands/cmd_swap_checkerboard_colors.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "ui/base.h"
|
||||
#include "app/context.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class SwapCheckerboardColorsCommand : public Command {
|
||||
public:
|
||||
SwapCheckerboardColorsCommand();
|
||||
|
||||
protected:
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
};
|
||||
|
||||
SwapCheckerboardColorsCommand::SwapCheckerboardColorsCommand()
|
||||
: Command(CommandId::SwapCheckerboardColors(), CmdUIOnlyFlag)
|
||||
{
|
||||
}
|
||||
|
||||
bool SwapCheckerboardColorsCommand::onEnabled(Context* context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void SwapCheckerboardColorsCommand::onExecute(Context* context)
|
||||
{
|
||||
DocumentPreferences& docPref = Preferences::instance().document(context->activeDocument());
|
||||
app::Color c1 = docPref.bg.color1();
|
||||
app::Color c2 = docPref.bg.color2();
|
||||
|
||||
docPref.bg.color1(c2);
|
||||
docPref.bg.color2(c1);
|
||||
}
|
||||
Command* CommandFactory::createSwapCheckerboardColorsCommand()
|
||||
{
|
||||
return new SwapCheckerboardColorsCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -153,6 +153,7 @@ FOR_EACH_COMMAND(SliceProperties)
|
||||
FOR_EACH_COMMAND(SnapToGrid)
|
||||
FOR_EACH_COMMAND(SpriteProperties)
|
||||
FOR_EACH_COMMAND(Stroke)
|
||||
FOR_EACH_COMMAND(SwapCheckerboardColors)
|
||||
FOR_EACH_COMMAND(SwitchColors)
|
||||
FOR_EACH_COMMAND(SymmetryMode)
|
||||
FOR_EACH_COMMAND(TiledMode)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -17,15 +17,23 @@
|
||||
#include "app/file/file.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "base/buffer.h"
|
||||
#include "base/fs.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "fmt/format.h"
|
||||
#include "os/display.h"
|
||||
#include "os/surface.h"
|
||||
#include "ui/alert.h"
|
||||
#include "ui/manager.h"
|
||||
#include "ui/scale.h"
|
||||
|
||||
#ifdef ENABLE_STEAM
|
||||
#include "steam/steam.h"
|
||||
#endif
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -34,6 +42,9 @@ using namespace ui;
|
||||
struct ScreenshotParams : public NewParams {
|
||||
Param<bool> save { this, false, "save" };
|
||||
Param<bool> srgb { this, true, "srgb" };
|
||||
#ifdef ENABLE_STEAM
|
||||
Param<bool> steam { this, false, "steam" };
|
||||
#endif
|
||||
};
|
||||
|
||||
class ScreenshotCommand : public CommandWithNewParams<ScreenshotParams> {
|
||||
@ -85,9 +96,11 @@ void ScreenshotCommand::onExecute(Context* ctx)
|
||||
|
||||
doc::Cel* cel = spr->firstLayer()->cel(0);
|
||||
doc::Image* img = cel->image();
|
||||
const int w = img->width();
|
||||
const int h = img->height();
|
||||
|
||||
for (int y=0; y<img->height(); ++y) {
|
||||
for (int x=0; x<img->width(); ++x) {
|
||||
for (int y=0; y<h; ++y) {
|
||||
for (int x=0; x<w; ++x) {
|
||||
gfx::Color c = surface->getPixel(x, y);
|
||||
|
||||
img->putPixel(x, y, doc::rgba(gfx::getr(c),
|
||||
@ -103,6 +116,34 @@ void ScreenshotCommand::onExecute(Context* ctx)
|
||||
if (params().srgb())
|
||||
cmd::convert_color_profile(spr, gfx::ColorSpace::MakeSRGB());
|
||||
|
||||
#ifdef ENABLE_STEAM
|
||||
if (params().steam()) {
|
||||
if (auto steamAPI = steam::SteamAPI::instance()) {
|
||||
// Get image again (cmd::convert_color_profile() might have changed it)
|
||||
img = cel->image();
|
||||
|
||||
const int scale = display->scale();
|
||||
base::buffer rgbBuffer(3*w*h*scale*scale);
|
||||
int c = 0;
|
||||
doc::LockImageBits<RgbTraits> bits(img);
|
||||
for (int y=0; y<h; ++y) {
|
||||
for (int i=0; i<scale; ++i) {
|
||||
for (int x=0; x<w; ++x) {
|
||||
color_t color = get_pixel_fast<RgbTraits>(img, x, y);
|
||||
for (int j=0; j<scale; ++j) {
|
||||
rgbBuffer[c++] = doc::rgba_getr(color);
|
||||
rgbBuffer[c++] = doc::rgba_getg(color);
|
||||
rgbBuffer[c++] = doc::rgba_getb(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (steamAPI->writeScreenshot(&rgbBuffer[0], rgbBuffer.size(), w*scale, h*scale))
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (params().save()) {
|
||||
save_document(ctx, doc.get());
|
||||
}
|
||||
@ -115,6 +156,11 @@ void ScreenshotCommand::onExecute(Context* ctx)
|
||||
std::string ScreenshotCommand::onGetFriendlyName() const
|
||||
{
|
||||
std::string name;
|
||||
#ifdef ENABLE_STEAM
|
||||
if (params().steam())
|
||||
name = Strings::commands_Screenshot_Steam();
|
||||
else
|
||||
#endif
|
||||
if (params().save())
|
||||
name = Strings::commands_Screenshot_Save();
|
||||
else
|
||||
|
@ -37,7 +37,7 @@ void ContextFlags::update(Context* context)
|
||||
if (document) {
|
||||
m_flags |= HasActiveDocument;
|
||||
|
||||
if (document->lock(Doc::ReadLock, 0)) {
|
||||
if (document->readLock(0)) {
|
||||
m_flags |= ActiveDocumentIsReadable;
|
||||
|
||||
if (document->isMaskVisible())
|
||||
|
@ -367,14 +367,18 @@ private:
|
||||
}
|
||||
|
||||
// Read color space
|
||||
gfx::ColorSpacePtr colorSpace = readColorSpace(s);
|
||||
if (colorSpace)
|
||||
spr->setColorSpace(colorSpace);
|
||||
if (!s.eof()) {
|
||||
gfx::ColorSpacePtr colorSpace = readColorSpace(s);
|
||||
if (colorSpace)
|
||||
spr->setColorSpace(colorSpace);
|
||||
}
|
||||
|
||||
// Read grid bounds
|
||||
gfx::Rect gridBounds = readGridBounds(s);
|
||||
if (!gridBounds.isEmpty())
|
||||
spr->setGridBounds(gridBounds);
|
||||
if (!s.eof()) {
|
||||
gfx::Rect gridBounds = readGridBounds(s);
|
||||
if (!gridBounds.isEmpty())
|
||||
spr->setGridBounds(gridBounds);
|
||||
}
|
||||
|
||||
return spr.release();
|
||||
}
|
||||
@ -384,6 +388,12 @@ private:
|
||||
const gfx::ColorSpace::Flag flags = (gfx::ColorSpace::Flag)read16(s);
|
||||
const double gamma = fixmath::fixtof(read32(s));
|
||||
const size_t n = read32(s);
|
||||
|
||||
// If the color space file is to big, it's because the sprite file
|
||||
// is invalid or or from an old session without color spcae.
|
||||
if (n > 1024*1024*64) // 64 MB is too much for an ICC file
|
||||
return nullptr;
|
||||
|
||||
std::vector<uint8_t> buf(n);
|
||||
if (n)
|
||||
s.read((char*)&buf[0], n);
|
||||
|
@ -95,6 +95,46 @@ void Doc::setContext(Context* ctx)
|
||||
onContextChanged();
|
||||
}
|
||||
|
||||
bool Doc::canWriteLockFromRead() const
|
||||
{
|
||||
return m_rwLock.canWriteLockFromRead();
|
||||
}
|
||||
|
||||
bool Doc::readLock(int timeout)
|
||||
{
|
||||
return m_rwLock.lock(base::RWLock::ReadLock, timeout);
|
||||
}
|
||||
|
||||
bool Doc::writeLock(int timeout)
|
||||
{
|
||||
return m_rwLock.lock(base::RWLock::WriteLock, timeout);
|
||||
}
|
||||
|
||||
bool Doc::upgradeToWrite(int timeout)
|
||||
{
|
||||
return m_rwLock.upgradeToWrite(timeout);
|
||||
}
|
||||
|
||||
void Doc::downgradeToRead()
|
||||
{
|
||||
m_rwLock.downgradeToRead();
|
||||
}
|
||||
|
||||
void Doc::unlock()
|
||||
{
|
||||
m_rwLock.unlock();
|
||||
}
|
||||
|
||||
bool Doc::weakLock(base::RWLock::WeakLock* weak_lock_flag)
|
||||
{
|
||||
return m_rwLock.weakLock(weak_lock_flag);
|
||||
}
|
||||
|
||||
void Doc::weakUnlock()
|
||||
{
|
||||
m_rwLock.weakUnlock();
|
||||
}
|
||||
|
||||
void Doc::setTransaction(Transaction* transaction)
|
||||
{
|
||||
if (transaction) {
|
||||
@ -188,7 +228,7 @@ void Doc::notifyLayerMergedDown(Layer* srcLayer, Layer* targetLayer)
|
||||
void Doc::notifyCelMoved(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame)
|
||||
{
|
||||
DocEvent ev(this);
|
||||
ev.sprite(fromLayer->sprite());
|
||||
ev.sprite(toLayer->sprite());
|
||||
ev.layer(fromLayer);
|
||||
ev.frame(fromFrame);
|
||||
ev.targetLayer(toLayer);
|
||||
@ -199,8 +239,8 @@ void Doc::notifyCelMoved(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, fr
|
||||
void Doc::notifyCelCopied(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame)
|
||||
{
|
||||
DocEvent ev(this);
|
||||
ev.sprite(fromLayer->sprite());
|
||||
ev.layer(fromLayer);
|
||||
ev.sprite(toLayer->sprite());
|
||||
ev.layer(fromLayer); // From layer can be nullptr
|
||||
ev.frame(fromFrame);
|
||||
ev.targetLayer(toLayer);
|
||||
ev.targetFrame(toFrame);
|
||||
|
@ -58,7 +58,6 @@ namespace app {
|
||||
// An application document. It is the class used to contain one file
|
||||
// opened and being edited by the user (a sprite).
|
||||
class Doc : public doc::Document,
|
||||
public base::RWLock,
|
||||
public obs::observable<DocObserver> {
|
||||
enum Flags {
|
||||
kAssociatedToFile = 1, // This sprite is associated to a file in the file-system
|
||||
@ -73,6 +72,17 @@ namespace app {
|
||||
Context* context() const { return m_ctx; }
|
||||
void setContext(Context* ctx);
|
||||
|
||||
// Lock/unlock API (RWLock wrapper)
|
||||
bool canWriteLockFromRead() const;
|
||||
bool readLock(int timeout);
|
||||
bool writeLock(int timeout);
|
||||
bool upgradeToWrite(int timeout);
|
||||
void downgradeToRead();
|
||||
void unlock();
|
||||
|
||||
bool weakLock(base::RWLock::WeakLock* weak_lock_flag);
|
||||
void weakUnlock();
|
||||
|
||||
// Sets active/running transaction.
|
||||
void setTransaction(Transaction* transaction);
|
||||
Transaction* transaction() { return m_transaction; }
|
||||
@ -216,9 +226,15 @@ namespace app {
|
||||
void removeFromContext();
|
||||
void updateOSColorSpace(bool appWideSignal);
|
||||
|
||||
// The document is in the collection of documents of this context.
|
||||
Context* m_ctx;
|
||||
|
||||
// Internal states of the document.
|
||||
int m_flags;
|
||||
|
||||
// Read-Write locks.
|
||||
base::RWLock m_rwLock;
|
||||
|
||||
// Undo and redo information about the document.
|
||||
std::unique_ptr<DocUndo> m_undo;
|
||||
|
||||
|
@ -82,13 +82,13 @@ namespace app {
|
||||
|
||||
explicit DocReader(Doc* doc, int timeout)
|
||||
: DocAccess(doc) {
|
||||
if (m_doc && !m_doc->lock(Doc::ReadLock, timeout))
|
||||
if (m_doc && !m_doc->readLock(timeout))
|
||||
throw CannotReadDocException();
|
||||
}
|
||||
|
||||
explicit DocReader(const DocReader& copy, int timeout)
|
||||
: DocAccess(copy) {
|
||||
if (m_doc && !m_doc->lock(Doc::ReadLock, timeout))
|
||||
if (m_doc && !m_doc->readLock(timeout))
|
||||
throw CannotReadDocException();
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ namespace app {
|
||||
, m_from_reader(false)
|
||||
, m_locked(false) {
|
||||
if (m_doc) {
|
||||
if (!m_doc->lock(Doc::WriteLock, timeout))
|
||||
if (!m_doc->writeLock(timeout))
|
||||
throw CannotWriteDocException();
|
||||
|
||||
m_locked = true;
|
||||
|
@ -14,13 +14,11 @@
|
||||
#include "app/cmd/add_cel.h"
|
||||
#include "app/cmd/add_frame.h"
|
||||
#include "app/cmd/add_layer.h"
|
||||
#include "app/cmd/background_from_layer.h"
|
||||
#include "app/cmd/clear_cel.h"
|
||||
#include "app/cmd/clear_image.h"
|
||||
#include "app/cmd/copy_cel.h"
|
||||
#include "app/cmd/copy_frame.h"
|
||||
#include "app/cmd/flip_image.h"
|
||||
#include "app/cmd/layer_from_background.h"
|
||||
#include "app/cmd/move_cel.h"
|
||||
#include "app/cmd/move_layer.h"
|
||||
#include "app/cmd/remove_cel.h"
|
||||
@ -678,16 +676,6 @@ void DocApi::restackLayerBefore(Layer* layer, LayerGroup* parent, Layer* beforeT
|
||||
restackLayerAfter(layer, parent, afterThis);
|
||||
}
|
||||
|
||||
void DocApi::backgroundFromLayer(Layer* layer)
|
||||
{
|
||||
m_transaction.execute(new cmd::BackgroundFromLayer(layer));
|
||||
}
|
||||
|
||||
void DocApi::layerFromBackground(Layer* layer)
|
||||
{
|
||||
m_transaction.execute(new cmd::LayerFromBackground(layer));
|
||||
}
|
||||
|
||||
Layer* DocApi::duplicateLayerAfter(Layer* sourceLayer, LayerGroup* parent, Layer* afterLayer)
|
||||
{
|
||||
ASSERT(parent);
|
||||
|
@ -97,8 +97,6 @@ namespace app {
|
||||
void removeLayer(Layer* layer);
|
||||
void restackLayerAfter(Layer* layer, LayerGroup* parent, Layer* afterThis);
|
||||
void restackLayerBefore(Layer* layer, LayerGroup* parent, Layer* beforeThis);
|
||||
void backgroundFromLayer(Layer* layer);
|
||||
void layerFromBackground(Layer* layer);
|
||||
Layer* duplicateLayerAfter(Layer* sourceLayer, LayerGroup* parent, Layer* afterLayer);
|
||||
Layer* duplicateLayerBefore(Layer* sourceLayer, LayerGroup* parent, Layer* beforeLayer);
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "render/render.h"
|
||||
#include "ver/info.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
@ -1361,68 +1362,86 @@ void DocExporter::createDataFile(const Samples& samples,
|
||||
|
||||
// meta.layers
|
||||
if (m_listLayers) {
|
||||
os << ",\n"
|
||||
<< " \"layers\": [";
|
||||
|
||||
bool firstLayer = true;
|
||||
LayerList metaLayers;
|
||||
for (auto& item : m_documents) {
|
||||
Doc* doc = item.doc;
|
||||
Sprite* sprite = doc->sprite();
|
||||
LayerList layers;
|
||||
Layer* root = sprite->root();
|
||||
|
||||
LayerList layers;
|
||||
if (item.selLayers)
|
||||
layers = item.selLayers->toLayerList();
|
||||
else
|
||||
layers = sprite->allVisibleLayers();
|
||||
|
||||
for (Layer* layer : layers) {
|
||||
if (firstLayer)
|
||||
firstLayer = false;
|
||||
else
|
||||
os << ",";
|
||||
os << "\n { \"name\": \"" << escape_for_json(layer->name()) << "\"";
|
||||
|
||||
if (layer->parent() != layer->sprite()->root())
|
||||
os << ", \"group\": \"" << escape_for_json(layer->parent()->name()) << "\"";
|
||||
|
||||
if (LayerImage* layerImg = dynamic_cast<LayerImage*>(layer)) {
|
||||
os << ", \"opacity\": " << layerImg->opacity()
|
||||
<< ", \"blendMode\": \"" << blend_mode_to_string(layerImg->blendMode()) << "\"";
|
||||
// If this layer is inside a group, check that the group will
|
||||
// be included in the meta data too.
|
||||
Layer* group = layer->parent();
|
||||
int pos = int(metaLayers.size());
|
||||
while (group && group != root) {
|
||||
if (std::find(metaLayers.begin(), metaLayers.end(), group) == metaLayers.end()) {
|
||||
metaLayers.insert(metaLayers.begin()+pos, group);
|
||||
}
|
||||
group = group->parent();
|
||||
}
|
||||
os << layer->userData();
|
||||
// Insert the layer
|
||||
if (std::find(metaLayers.begin(), metaLayers.end(), layer) == metaLayers.end()) {
|
||||
metaLayers.push_back(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cels
|
||||
CelList cels;
|
||||
layer->getCels(cels);
|
||||
bool someCelWithData = false;
|
||||
bool firstLayer = true;
|
||||
os << ",\n"
|
||||
<< " \"layers\": [";
|
||||
for (Layer* layer : metaLayers) {
|
||||
if (firstLayer)
|
||||
firstLayer = false;
|
||||
else
|
||||
os << ",";
|
||||
os << "\n { \"name\": \"" << escape_for_json(layer->name()) << "\"";
|
||||
|
||||
if (layer->parent() != layer->sprite()->root())
|
||||
os << ", \"group\": \"" << escape_for_json(layer->parent()->name()) << "\"";
|
||||
|
||||
if (LayerImage* layerImg = dynamic_cast<LayerImage*>(layer)) {
|
||||
os << ", \"opacity\": " << layerImg->opacity()
|
||||
<< ", \"blendMode\": \"" << blend_mode_to_string(layerImg->blendMode()) << "\"";
|
||||
}
|
||||
os << layer->userData();
|
||||
|
||||
// Cels
|
||||
CelList cels;
|
||||
layer->getCels(cels);
|
||||
bool someCelWithData = false;
|
||||
for (const Cel* cel : cels) {
|
||||
if (!cel->data()->userData().isEmpty()) {
|
||||
someCelWithData = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (someCelWithData) {
|
||||
bool firstCel = true;
|
||||
|
||||
os << ", \"cels\": [";
|
||||
for (const Cel* cel : cels) {
|
||||
if (!cel->data()->userData().isEmpty()) {
|
||||
someCelWithData = true;
|
||||
break;
|
||||
if (firstCel)
|
||||
firstCel = false;
|
||||
else
|
||||
os << ", ";
|
||||
|
||||
os << "{ \"frame\": " << cel->frame()
|
||||
<< cel->data()->userData()
|
||||
<< " }";
|
||||
}
|
||||
}
|
||||
|
||||
if (someCelWithData) {
|
||||
bool firstCel = true;
|
||||
|
||||
os << ", \"cels\": [";
|
||||
for (const Cel* cel : cels) {
|
||||
if (!cel->data()->userData().isEmpty()) {
|
||||
if (firstCel)
|
||||
firstCel = false;
|
||||
else
|
||||
os << ", ";
|
||||
|
||||
os << "{ \"frame\": " << cel->frame()
|
||||
<< cel->data()->userData()
|
||||
<< " }";
|
||||
}
|
||||
}
|
||||
os << "]";
|
||||
}
|
||||
|
||||
os << " }";
|
||||
os << "]";
|
||||
}
|
||||
|
||||
os << " }";
|
||||
}
|
||||
os << "\n ]";
|
||||
}
|
||||
|
@ -50,6 +50,12 @@ void DocRange::clearRange()
|
||||
m_flags = kNone;
|
||||
m_selectedLayers.clear();
|
||||
m_selectedFrames.clear();
|
||||
|
||||
// Reset the starting point of a previous startRange/endRange(), we
|
||||
// don't want to store a pointer to an invalid
|
||||
// "m_selectingFromLayer" layer.
|
||||
m_selectingFromLayer = nullptr;
|
||||
m_selectingFromFrame = -1;
|
||||
}
|
||||
|
||||
void DocRange::startRange(Layer* fromLayer, frame_t fromFrame, Type type)
|
||||
@ -100,6 +106,14 @@ void DocRange::eraseAndAdjust(const Layer* layer)
|
||||
if (!enabled())
|
||||
return;
|
||||
|
||||
// Check that the sprite of m_selectingFromLayer is the same than
|
||||
// the given layer. In the past if we stored an invalid
|
||||
// "m_selectingFromLayer" for too much time this could fail (even
|
||||
// more, "m_selectingFromLayer" could be pointing to an already
|
||||
// closed/deleted sprite).
|
||||
ASSERT(!m_selectingFromLayer || !layer ||
|
||||
m_selectingFromLayer->sprite() == layer->sprite());
|
||||
|
||||
if (m_selectingFromLayer)
|
||||
m_selectingFromLayer = candidate_if_layer_is_deleted(m_selectingFromLayer, layer);
|
||||
|
||||
|
@ -93,7 +93,7 @@ bool SvgFormat::onSave(FileOp* fop)
|
||||
fprintf(f, "/>\n");
|
||||
};
|
||||
fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
|
||||
fprintf(f, "<svg version=\"1.1\" width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\">\n",
|
||||
fprintf(f, "<svg version=\"1.1\" width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\" shape-rendering=\"crispEdges\">\n",
|
||||
image->width()*pixelScaleValue, image->height()*pixelScaleValue);
|
||||
|
||||
switch (image->pixelFormat()) {
|
||||
|
@ -48,6 +48,10 @@
|
||||
#include "ui/intern.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#ifdef ENABLE_STEAM
|
||||
#include "steam/steam.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
@ -366,6 +370,11 @@ void defer_invalid_rect(const gfx::Rect& rc)
|
||||
// Manager event handler.
|
||||
bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
{
|
||||
#ifdef ENABLE_STEAM
|
||||
if (auto steamAPI = steam::SteamAPI::instance())
|
||||
steamAPI->runCallbacks();
|
||||
#endif
|
||||
|
||||
switch (msg->type()) {
|
||||
|
||||
case kCloseDisplayMessage: {
|
||||
|
@ -16,12 +16,12 @@
|
||||
#include "app/doc.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/file_system.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/clamp.h"
|
||||
#include "base/scoped_lock.h"
|
||||
#include "base/thread.h"
|
||||
#include "doc/algorithm/rotate.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
|
@ -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
|
||||
// Copyright (C) 2016 Carlo Caputo
|
||||
//
|
||||
@ -10,9 +10,9 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "doc/blend_mode.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "os/surface.h"
|
||||
|
@ -460,6 +460,7 @@ public:
|
||||
|
||||
void setFinalStep(ToolLoop* loop, bool state) override {
|
||||
m_modify_selection = state;
|
||||
int modifiers = int(loop->getModifiers());
|
||||
|
||||
if (state) {
|
||||
m_maxBounds = loop->getMask()->bounds();
|
||||
@ -467,11 +468,16 @@ public:
|
||||
m_mask.copyFrom(loop->getMask());
|
||||
m_mask.freeze();
|
||||
m_mask.reserve(loop->sprite()->bounds());
|
||||
|
||||
if ((modifiers & int(ToolLoopModifiers::kIntersectSelection)) != 0) {
|
||||
m_intersectMask.clear();
|
||||
m_intersectMask.reserve(loop->sprite()->bounds());
|
||||
}
|
||||
}
|
||||
else {
|
||||
int modifiers = int(loop->getModifiers());
|
||||
if ((modifiers & int(ToolLoopModifiers::kIntersectSelection)) != 0) {
|
||||
m_mask.intersect(m_intersectMask);
|
||||
m_intersectMask.clear();
|
||||
}
|
||||
|
||||
// We can intersect the used bounds in inkHline() calls to
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -26,10 +26,10 @@
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "gfx/border.h"
|
||||
|
@ -51,7 +51,6 @@
|
||||
#include "base/fs.h"
|
||||
#include "base/scoped_value.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/remap.h"
|
||||
@ -990,7 +989,11 @@ public:
|
||||
|
||||
if (!m_popup) {
|
||||
m_popup.reset(new DynamicsPopup(this));
|
||||
m_popup->Close.connect([this](CloseEvent&){ deselectItems(); });
|
||||
m_popup->Close.connect(
|
||||
[this](CloseEvent&){
|
||||
deselectItems();
|
||||
m_dynamics = m_popup->getDynamics();
|
||||
});
|
||||
}
|
||||
|
||||
const gfx::Rect bounds = this->bounds();
|
||||
@ -1000,11 +1003,10 @@ public:
|
||||
m_popup->openWindow();
|
||||
}
|
||||
|
||||
tools::DynamicsOptions getDynamics() {
|
||||
if (m_popup)
|
||||
return m_popup->getDynamics();
|
||||
else
|
||||
return tools::DynamicsOptions();
|
||||
const tools::DynamicsOptions& getDynamics() const {
|
||||
if (m_popup && m_popup->isVisible())
|
||||
m_dynamics = m_popup->getDynamics();
|
||||
return m_dynamics;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1023,11 +1025,13 @@ private:
|
||||
Preferences::instance().tool(tool).brush.angle(angle);
|
||||
}
|
||||
|
||||
// ButtonSet overrides
|
||||
void onItemChange(Item* item) override {
|
||||
ButtonSet::onItemChange(item);
|
||||
switchPopup();
|
||||
}
|
||||
|
||||
// Widget overrides
|
||||
void onInitTheme(InitThemeEvent& ev) override {
|
||||
ButtonSet::onInitTheme(ev);
|
||||
if (m_popup)
|
||||
@ -1036,6 +1040,7 @@ private:
|
||||
|
||||
std::unique_ptr<DynamicsPopup> m_popup;
|
||||
ContextBar* m_ctxBar;
|
||||
mutable tools::DynamicsOptions m_dynamics;
|
||||
};
|
||||
|
||||
class ContextBar::FreehandAlgorithmField : public CheckBox {
|
||||
@ -2220,7 +2225,7 @@ render::GradientType ContextBar::gradientType()
|
||||
return m_gradientType->gradientType();
|
||||
}
|
||||
|
||||
tools::DynamicsOptions ContextBar::getDynamics()
|
||||
const tools::DynamicsOptions& ContextBar::getDynamics() const
|
||||
{
|
||||
return m_dynamics->getDynamics();
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ namespace app {
|
||||
render::GradientType gradientType();
|
||||
|
||||
// For freehand with dynamics
|
||||
tools::DynamicsOptions getDynamics();
|
||||
const tools::DynamicsOptions& getDynamics() const;
|
||||
|
||||
// Signals
|
||||
obs::signal<void()> BrushChange;
|
||||
|
@ -15,8 +15,8 @@
|
||||
#include "app/extensions.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "base/bind.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/primitives.h"
|
||||
@ -113,8 +113,8 @@ private:
|
||||
}
|
||||
|
||||
m_preview = os::instance()->createRgbaSurface(w, h);
|
||||
doc::convert_image_to_surface(image2.get(), palette, m_preview,
|
||||
0, 0, 0, 0, w, h);
|
||||
convert_image_to_surface(image2.get(), palette, m_preview,
|
||||
0, 0, 0, 0, w, h);
|
||||
|
||||
m_palId = palette->id();
|
||||
m_palMods = palette->getModifications();
|
||||
|
@ -121,6 +121,7 @@ void BrushPreview::show(const gfx::Point& screenPos)
|
||||
tools::Ink* ink = m_editor->getCurrentEditorInk();
|
||||
|
||||
const bool isFloodfill = m_editor->getCurrentEditorTool()->getPointShape(0)->isFloodFill();
|
||||
const auto& dynamics = App::instance()->contextBar()->getDynamics();
|
||||
|
||||
// Setup the cursor type depending on several factors (current tool,
|
||||
// foreground color, layer transparency, brush size, etc.).
|
||||
@ -128,6 +129,16 @@ void BrushPreview::show(const gfx::Point& screenPos)
|
||||
color_t brush_color = getBrushColor(sprite, layer);
|
||||
color_t mask_index = sprite->transparentColor();
|
||||
|
||||
if (brush->type() != doc::kImageBrushType &&
|
||||
(dynamics.size != tools::DynamicSensor::Static ||
|
||||
dynamics.angle != tools::DynamicSensor::Static)) {
|
||||
brush.reset(
|
||||
new Brush(
|
||||
brush->type(),
|
||||
(dynamics.size != tools::DynamicSensor::Static ? dynamics.minSize: brush->size()),
|
||||
(dynamics.angle != tools::DynamicSensor::Static ? dynamics.minAngle: brush->angle())));
|
||||
}
|
||||
|
||||
if (ink->isSelection() || ink->isSlice()) {
|
||||
m_type = SELECTION_CROSSHAIR;
|
||||
}
|
||||
|
@ -51,12 +51,12 @@
|
||||
#include "app/ui/timeline/timeline.h"
|
||||
#include "app/ui/toolbar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "app/util/layer_utils.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/chrono.h"
|
||||
#include "base/clamp.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/doc.h"
|
||||
#include "doc/mask_boundaries.h"
|
||||
#include "doc/slice.h"
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/clipboard.h"
|
||||
#include "app/util/layer_utils.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/gcd.h"
|
||||
#include "base/pi.h"
|
||||
@ -298,6 +299,9 @@ bool MovingPixelsState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
getTransformation(editor));
|
||||
|
||||
if (handle != NoHandle) {
|
||||
if (layer_is_locked(editor))
|
||||
return true;
|
||||
|
||||
// Re-catch the image
|
||||
m_pixelsMovement->catchImageAgain(
|
||||
editor->screenToEditor(msg->position()), handle);
|
||||
@ -311,6 +315,9 @@ bool MovingPixelsState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
// right-click can be used to deselect/subtract selection, so we
|
||||
// should drop the selection in this later case.
|
||||
if (editor->isInsideSelection() && msg->left()) {
|
||||
if (layer_is_locked(editor))
|
||||
return true;
|
||||
|
||||
// In case that the user is pressing the copy-selection keyboard shortcut.
|
||||
EditorCustomizationDelegate* customization = editor->getCustomizationDelegate();
|
||||
if ((customization) &&
|
||||
@ -532,9 +539,22 @@ void MovingPixelsState::onBeforeCommandExecution(CommandExecutionEvent& ev)
|
||||
if (!isActiveEditor())
|
||||
return;
|
||||
|
||||
if (layer_is_locked(m_editor) &&
|
||||
(command->id() == CommandId::Flip() ||
|
||||
command->id() == CommandId::Cut() ||
|
||||
command->id() == CommandId::Clear() ||
|
||||
command->id() == CommandId::Rotate())) {
|
||||
ev.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to drop the pixels if a MoveMaskCommand of Content is executed.
|
||||
if (MoveMaskCommand* moveMaskCmd = dynamic_cast<MoveMaskCommand*>(command)) {
|
||||
if (moveMaskCmd->getTarget() == MoveMaskCommand::Content) {
|
||||
if (layer_is_locked(m_editor)) {
|
||||
ev.cancel();
|
||||
return;
|
||||
}
|
||||
gfx::Point delta = moveMaskCmd->getMoveThing().getDelta(UIContext::instance());
|
||||
// Verify Shift condition of the MoveMaskCommand (i.e. wrap = true)
|
||||
if (moveMaskCmd->isWrap()) {
|
||||
|
@ -588,6 +588,14 @@ void PixelsMovement::stampImage(bool finalStamp)
|
||||
cels.push_back(currentCel);
|
||||
}
|
||||
|
||||
if (currentCel && currentCel->layer() &&
|
||||
currentCel->layer()->isImage() &&
|
||||
!currentCel->layer()->isEditableHierarchy()) {
|
||||
Transformation initialCelPos(gfx::Rect(m_initialMask0->bounds()));
|
||||
redrawExtraImage(&initialCelPos);
|
||||
stampExtraCelImage();
|
||||
}
|
||||
|
||||
for (Cel* target : cels) {
|
||||
// We'll re-create the transformation for the other cels
|
||||
if (target != currentCel) {
|
||||
@ -995,14 +1003,15 @@ CelList PixelsMovement::getEditableCels()
|
||||
// TODO This case is used in paste too, where the cel() can be
|
||||
// nullptr (e.g. we paste the clipboard image into an empty
|
||||
// cel).
|
||||
cels.push_back(m_site.cel());
|
||||
if (m_site.layer() && m_site.layer()->isEditableHierarchy())
|
||||
cels.push_back(m_site.cel());
|
||||
return cels;
|
||||
}
|
||||
|
||||
// Current cel (m_site.cel()) can be nullptr when we paste in an
|
||||
// empty cel (Ctrl+V) and cut (Ctrl+X) the floating pixels.
|
||||
if (m_site.cel() &&
|
||||
m_site.cel()->layer()->isEditable()) {
|
||||
m_site.cel()->layer()->isEditableHierarchy()) {
|
||||
auto it = std::find(cels.begin(), cels.end(), m_site.cel());
|
||||
if (it != cels.end())
|
||||
cels.erase(it);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/doc_range.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/tools/active_tool.h"
|
||||
@ -47,6 +48,7 @@
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui/timeline/timeline.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/layer_utils.h"
|
||||
#include "app/util/new_image_from_mask.h"
|
||||
#include "app/util/readable_time.h"
|
||||
#include "base/bind.h"
|
||||
@ -197,7 +199,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
}
|
||||
else if (!layer->isMovable() || !layer->isEditableHierarchy()) {
|
||||
StatusBar::instance()->showTip(
|
||||
1000, fmt::format("Layer '{}' is locked", layer->name()));
|
||||
1000, fmt::format(Strings::statusbar_tips_layer_locked(), layer->name()));
|
||||
}
|
||||
else {
|
||||
MovingCelCollect collect(editor, layer);
|
||||
@ -300,12 +302,6 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
int x, y, opacity;
|
||||
Image* image = site.image(&x, &y, &opacity);
|
||||
if (layer && image) {
|
||||
if (!layer->isEditableHierarchy()) {
|
||||
StatusBar::instance()->showTip(
|
||||
1000, fmt::format("Layer '{}' is locked", layer->name()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Change to MovingPixelsState
|
||||
transformSelection(editor, msg, handle);
|
||||
}
|
||||
@ -321,12 +317,6 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
|
||||
// Move selected pixels
|
||||
if (layer && editor->canStartMovingSelectionPixels() && msg->left()) {
|
||||
if (!layer->isEditableHierarchy()) {
|
||||
StatusBar::instance()->showTip(
|
||||
1000, fmt::format("Layer '{}' is locked", layer->name()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Change to MovingPixelsState
|
||||
transformSelection(editor, msg, MovePixelsHandle);
|
||||
return true;
|
||||
@ -759,6 +749,9 @@ void StandbyState::transformSelection(Editor* editor, MouseMessage* msg, HandleT
|
||||
return;
|
||||
}
|
||||
|
||||
if (layer_is_locked(editor))
|
||||
return;
|
||||
|
||||
try {
|
||||
// Clear brush preview, as the extra cel will be replaced with the
|
||||
// transformed image.
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/expand_cel_canvas.h"
|
||||
#include "app/util/layer_utils.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
@ -736,9 +737,7 @@ tools::ToolLoop* create_tool_loop(
|
||||
return nullptr;
|
||||
}
|
||||
// If the active layer is read-only.
|
||||
else if (!layer->isEditableHierarchy()) {
|
||||
StatusBar::instance()->showTip(
|
||||
1000, fmt::format("Layer '{}' is locked", layer->name()));
|
||||
else if (layer_is_locked(editor)) {
|
||||
return nullptr;
|
||||
}
|
||||
// If the active layer is reference.
|
||||
|
@ -19,11 +19,11 @@
|
||||
#include "app/ui/search_entry.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "app/util/freetype_utils.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/string.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "os/surface.h"
|
||||
|
@ -26,11 +26,11 @@
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/clipboard.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
#include "app/util/pal_ops.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/clamp.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/palette.h"
|
||||
@ -293,8 +293,8 @@ public:
|
||||
int w = tileImage->width();
|
||||
int h = tileImage->height();
|
||||
os::Surface* surface = os::instance()->createRgbaSurface(w, h);
|
||||
doc::convert_image_to_surface(tileImage.get(), get_current_palette(),
|
||||
surface, 0, 0, 0, 0, w, h);
|
||||
convert_image_to_surface(tileImage.get(), get_current_palette(),
|
||||
surface, 0, 0, 0, 0, w, h);
|
||||
g->drawRgbaSurface(surface, gfx::Rect(0, 0, w, h), box);
|
||||
surface->dispose();
|
||||
}
|
||||
|
@ -393,10 +393,17 @@ void Timeline::detachDocument()
|
||||
m_thumbnailsPrefConn.disconnect();
|
||||
m_document->remove_observer(this);
|
||||
m_document = nullptr;
|
||||
m_sprite = nullptr;
|
||||
m_layer = nullptr;
|
||||
}
|
||||
|
||||
// Reset all pointers to this document, even DocRanges, we don't
|
||||
// want to store a pointer to a layer of a document that we are not
|
||||
// observing anymore (because the document might be deleted soon).
|
||||
m_sprite = nullptr;
|
||||
m_layer = nullptr;
|
||||
m_range.clearRange();
|
||||
m_startRange.clearRange();
|
||||
m_dropRange.clearRange();
|
||||
|
||||
if (m_editor) {
|
||||
m_editor->remove_observer(this);
|
||||
m_editor = nullptr;
|
||||
@ -1057,7 +1064,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
// we shouldn't change the hot (so the separator can be
|
||||
// tracked to the mouse's released).
|
||||
if (m_clk.part == PART_SEPARATOR) {
|
||||
m_separator_x = std::max(0, mousePos.x);
|
||||
setSeparatorX(mousePos.x);
|
||||
layout();
|
||||
return true;
|
||||
}
|
||||
@ -1516,13 +1523,15 @@ void Timeline::onResize(ui::ResizeEvent& ev)
|
||||
{
|
||||
gfx::Rect rc = ev.bounds();
|
||||
setBoundsQuietly(rc);
|
||||
setSeparatorX(m_separator_x);
|
||||
|
||||
gfx::Size sz = m_aniControls.sizeHint();
|
||||
m_aniControls.setBounds(
|
||||
gfx::Rect(
|
||||
rc.x,
|
||||
rc.y+(visibleTagBands()-1)*oneTagHeight(),
|
||||
std::min(sz.w, m_separator_x),
|
||||
(!m_sprite || m_sprite->tags().empty() ? std::min(sz.w, rc.w):
|
||||
std::min(sz.w, m_separator_x)),
|
||||
oneTagHeight()));
|
||||
|
||||
updateScrollBars();
|
||||
@ -2965,6 +2974,8 @@ void Timeline::regenerateRows()
|
||||
|
||||
void Timeline::regenerateTagBands()
|
||||
{
|
||||
const bool oldEmptyTagBand = m_tagBand.empty();
|
||||
|
||||
// TODO improve this implementation
|
||||
std::vector<unsigned char> tagsPerFrame(m_sprite->totalFrames(), 0);
|
||||
std::vector<Tag*> bands(4, nullptr);
|
||||
@ -3002,8 +3013,13 @@ void Timeline::regenerateTagBands()
|
||||
if (m_tagFocusBand >= m_tagBands)
|
||||
m_tagFocusBand = -1;
|
||||
|
||||
if (oldVisibleBands != visibleTagBands())
|
||||
if (oldVisibleBands != visibleTagBands() ||
|
||||
// This case is to re-layout the timeline when the AniControl
|
||||
// can use more/less space because there weren't tags and now
|
||||
// there tags, or viceversa.
|
||||
oldEmptyTagBand != m_tagBand.empty()) {
|
||||
layout();
|
||||
}
|
||||
}
|
||||
|
||||
int Timeline::visibleTagBands() const
|
||||
@ -4217,4 +4233,9 @@ void Timeline::setLayerCollapsedFlag(const layer_t l, const bool state)
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::setSeparatorX(int newValue)
|
||||
{
|
||||
m_separator_x = base::clamp(newValue, headerBoxWidth(), bounds().w-guiscale());
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -357,6 +357,8 @@ namespace app {
|
||||
void setLayerContinuousFlag(const layer_t layer, const bool state);
|
||||
void setLayerCollapsedFlag(const layer_t layer, const bool state);
|
||||
|
||||
void setSeparatorX(int newValue);
|
||||
|
||||
ui::ScrollBar m_hbar;
|
||||
ui::ScrollBar m_vbar;
|
||||
gfx::Rect m_viewportArea;
|
||||
|
@ -1,14 +1,15 @@
|
||||
// Aseprite Document Library
|
||||
// Aseprite
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "app/util/conversion_to_surface.h"
|
||||
|
||||
#include "base/24bits.h"
|
||||
#include "doc/algo.h"
|
||||
@ -22,7 +23,9 @@
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace doc {
|
||||
namespace app {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
namespace {
|
||||
|
||||
@ -131,8 +134,14 @@ void convert_image_to_surface_selector(const Image* image, os::Surface* surface,
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void convert_image_to_surface(const Image* image, const Palette* palette,
|
||||
os::Surface* surface, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
|
||||
|
||||
void convert_image_to_surface(
|
||||
const doc::Image* image,
|
||||
const doc::Palette* palette,
|
||||
os::Surface* surface,
|
||||
int src_x, int src_y,
|
||||
int dst_x, int dst_y,
|
||||
int w, int h)
|
||||
{
|
||||
gfx::Rect srcBounds(src_x, src_y, w, h);
|
||||
srcBounds = srcBounds.createIntersection(image->bounds());
|
||||
@ -198,4 +207,4 @@ void convert_image_to_surface(const Image* image, const Palette* palette,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
} // namespace app
|
33
src/app/util/conversion_to_surface.h
Normal file
33
src/app/util/conversion_to_surface.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UTIL_CONVERSION_TO_SURFACE_H_INCLUDED
|
||||
#define APP_UTIL_CONVERSION_TO_SURFACE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Palette;
|
||||
}
|
||||
|
||||
namespace os {
|
||||
class Surface;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
void convert_image_to_surface(
|
||||
const doc::Image* image,
|
||||
const doc::Palette* palette,
|
||||
os::Surface* surface,
|
||||
int src_x, int src_y,
|
||||
int dst_x, int dst_y,
|
||||
int w, int h);
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -6,8 +6,12 @@
|
||||
|
||||
#include "app/util/layer_utils.h"
|
||||
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -38,4 +42,18 @@ Layer* candidate_if_layer_is_deleted(
|
||||
return const_cast<Layer*>(layerToSelect);
|
||||
}
|
||||
|
||||
bool layer_is_locked(Editor* editor)
|
||||
{
|
||||
Layer* layer = editor->layer();
|
||||
if (layer && !layer->isEditableHierarchy()) {
|
||||
#ifdef ENABLE_UI
|
||||
if (auto statusBar = StatusBar::instance())
|
||||
statusBar->showTip(
|
||||
1000, fmt::format(Strings::statusbar_tips_layer_locked(), layer->name()));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -14,6 +14,8 @@ namespace doc {
|
||||
|
||||
namespace app {
|
||||
|
||||
class Editor;
|
||||
|
||||
// Calculates a possible candidate to be selected in case that we
|
||||
// have a specific "selectedLayer" and are going to delete the given
|
||||
// "layerToDelete".
|
||||
@ -21,6 +23,10 @@ namespace app {
|
||||
const doc::Layer* selectedLayer,
|
||||
const doc::Layer* layerToDelete);
|
||||
|
||||
// True if the active layer is locked (itself or its hierarchy),
|
||||
// also, it sends a tip to the user 'Layer ... is locked'
|
||||
bool layer_is_locked(Editor* editor);
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
||||
|
@ -39,7 +39,7 @@ static CelList get_cels_templ(const Sprite* sprite,
|
||||
for (Layer* layer : range.selectedLayers()) {
|
||||
if (!layer ||
|
||||
!layer->isImage() ||
|
||||
(onlyUnlockedCel && !layer->isEditable()))
|
||||
(onlyUnlockedCel && !layer->isEditableHierarchy()))
|
||||
continue;
|
||||
|
||||
LayerImage* layerImage = static_cast<LayerImage*>(layer);
|
||||
|
2
src/clip
2
src/clip
@ -1 +1 @@
|
||||
Subproject commit 8b026c048d775a2e3139e1059636fc5e65ef45b1
|
||||
Subproject commit e6a1f9bf69480882cf9ee8df2b3a5f361238b691
|
@ -31,7 +31,6 @@ add_library(doc-lib
|
||||
cels_range.cpp
|
||||
color.cpp
|
||||
compressed_image.cpp
|
||||
conversion_to_surface.cpp
|
||||
document.cpp
|
||||
file/act_file.cpp
|
||||
file/col_file.cpp
|
||||
@ -76,10 +75,7 @@ add_library(doc-lib
|
||||
tilesets.cpp
|
||||
user_data_io.cpp)
|
||||
|
||||
# TODO Remove 'os' as dependency and move conversion_to_surface.cpp/h files
|
||||
# to other library/layer (render-lib? new conversion-lib?)
|
||||
target_link_libraries(doc-lib
|
||||
laf-os
|
||||
laf-gfx
|
||||
fixmath-lib
|
||||
laf-base)
|
||||
laf-base
|
||||
fixmath-lib)
|
||||
|
@ -1,25 +0,0 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_CONVERSION_TO_SURFACE_H_INCLUDED
|
||||
#define DOC_CONVERSION_TO_SURFACE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
class Surface;
|
||||
}
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Palette;
|
||||
|
||||
void convert_image_to_surface(const Image* image, const Palette* palette,
|
||||
os::Surface* surface,
|
||||
int src_x, int src_y, int dst_x, int dst_y, int w, int h);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -53,7 +53,7 @@ namespace doc {
|
||||
|
||||
public:
|
||||
inline address_t address(int x, int y) const {
|
||||
return (address_t)(m_rows[y] + x / (Traits::pixels_per_byte == 0 ? 1 : Traits::pixels_per_byte));
|
||||
return (address_t)(getLineAddress(y) + x / (Traits::pixels_per_byte == 0 ? 1 : Traits::pixels_per_byte));
|
||||
}
|
||||
|
||||
ImageImpl(const ImageSpec& spec,
|
||||
@ -222,15 +222,15 @@ namespace doc {
|
||||
|
||||
template<>
|
||||
inline void ImageImpl<IndexedTraits>::clear(color_t color) {
|
||||
std::fill(m_bits,
|
||||
m_bits + width()*height(),
|
||||
std::fill(getBitsAddress(),
|
||||
getBitsAddress() + width()*height(),
|
||||
color);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ImageImpl<BitmapTraits>::clear(color_t color) {
|
||||
std::fill(m_bits,
|
||||
m_bits + BitmapTraits::getRowStrideBytes(width()) * height(),
|
||||
std::fill(getBitsAddress(),
|
||||
getBitsAddress() + BitmapTraits::getRowStrideBytes(width()) * height(),
|
||||
(color ? 0xff: 0x00));
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ namespace doc {
|
||||
ASSERT(y >= 0 && y < height());
|
||||
|
||||
std::div_t d = std::div(x, 8);
|
||||
return ((*(m_rows[y] + d.quot)) & (1<<d.rem)) ? 1: 0;
|
||||
return ((*(getLineAddress(y) + d.quot)) & (1<<d.rem)) ? 1: 0;
|
||||
}
|
||||
|
||||
template<>
|
||||
@ -250,9 +250,9 @@ namespace doc {
|
||||
|
||||
std::div_t d = std::div(x, 8);
|
||||
if (color)
|
||||
(*(m_rows[y] + d.quot)) |= (1 << d.rem);
|
||||
(*(getLineAddress(y) + d.quot)) |= (1 << d.rem);
|
||||
else
|
||||
(*(m_rows[y] + d.quot)) &= ~(1 << d.rem);
|
||||
(*(getLineAddress(y) + d.quot)) &= ~(1 << d.rem);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
@ -71,7 +71,7 @@ LayerList SelectedLayers::toAllLayersList() const
|
||||
|
||||
for (Layer* layer = (*begin())->sprite()->firstLayer();
|
||||
layer != nullptr;
|
||||
layer = layer->getNext()) {
|
||||
layer = layer->getNextInWholeHierarchy()) {
|
||||
if (contains(layer))
|
||||
output.push_back(layer);
|
||||
}
|
||||
|
2
src/flic
2
src/flic
@ -1 +1 @@
|
||||
Subproject commit 5f44047529b003f976ff87bd48e56a8c5b42c394
|
||||
Subproject commit 5cc35476177355e035d2ea4b8f7465bb1addb93c
|
@ -1 +1 @@
|
||||
Subproject commit ccc2a2d80853671334f1c9f0b2ed771a22f77562
|
||||
Subproject commit 62da6c4279528505a0fb0e7e31afb9b82cd5aea9
|
@ -1,3 +1,4 @@
|
||||
Copyright (c) 2020 Igara Studio S.A.
|
||||
Copyright (c) 2016 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Steam Wrapper
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -10,15 +11,59 @@
|
||||
|
||||
#include "steam/steam.h"
|
||||
|
||||
#include "base/convert_to.h"
|
||||
#include "base/dll.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/ints.h"
|
||||
#include "base/launcher.h"
|
||||
#include "base/log.h"
|
||||
#include "base/string.h"
|
||||
|
||||
namespace steam {
|
||||
|
||||
typedef bool (*SteamAPI_Init_Func)();
|
||||
typedef void (*SteamAPI_Shutdown_Func)();
|
||||
typedef uint32_t HSteamPipe;
|
||||
typedef uint32_t HSteamUser;
|
||||
typedef uint32_t ScreenshotHandle;
|
||||
struct ISteamScreenshots;
|
||||
struct ISteamUtils;
|
||||
|
||||
enum {
|
||||
// Last callback received from Steam client when it's Steam is closed
|
||||
kSteamServersDisconnected = 103,
|
||||
kSteamUndocumentedLastCallback = 1009,
|
||||
// When a screenshot is ready in the library
|
||||
kScreenshotReady = 2301,
|
||||
};
|
||||
|
||||
struct CallbackMsg_t {
|
||||
HSteamUser steamUser;
|
||||
int callback;
|
||||
uint8_t* pubParam;
|
||||
int cubParam;
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define __cdecl
|
||||
#endif
|
||||
|
||||
// Steam main API
|
||||
typedef bool (__cdecl *SteamAPI_Init_Func)();
|
||||
typedef void (__cdecl *SteamAPI_Shutdown_Func)();
|
||||
typedef HSteamPipe (__cdecl *SteamAPI_GetHSteamPipe_Func)();
|
||||
|
||||
// Steam callbacks
|
||||
typedef void (__cdecl *SteamAPI_ManualDispatch_Init_Func)();
|
||||
typedef void (__cdecl *SteamAPI_ManualDispatch_RunFrame_Func)(HSteamPipe);
|
||||
typedef bool (__cdecl *SteamAPI_ManualDispatch_GetNextCallback_Func)(HSteamPipe, CallbackMsg_t*);
|
||||
typedef void (__cdecl *SteamAPI_ManualDispatch_FreeLastCallback_Func)(HSteamPipe);
|
||||
|
||||
// ISteamScreenshots
|
||||
typedef ISteamScreenshots* (__cdecl *SteamAPI_SteamScreenshots_v003_Func)();
|
||||
typedef ScreenshotHandle (__cdecl *SteamAPI_ISteamScreenshots_WriteScreenshot_Func)(ISteamScreenshots*, void*, uint32_t, int, int);
|
||||
|
||||
// ISteamUtils
|
||||
typedef ISteamUtils* (__cdecl *SteamAPI_SteamUtils_v009_Func)();
|
||||
typedef uint32_t (__cdecl *SteamAPI_ISteamUtils_GetAppID_Func)(ISteamUtils*);
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN64
|
||||
@ -32,9 +77,11 @@ typedef void (*SteamAPI_Shutdown_Func)();
|
||||
#define STEAM_API_DLL_FILENAME "libsteam_api.so"
|
||||
#endif
|
||||
|
||||
#define GETPROC(name) base::get_dll_proc<name##_Func>(m_steamLib, #name)
|
||||
|
||||
class SteamAPI::Impl {
|
||||
public:
|
||||
Impl() : m_initialized(false) {
|
||||
Impl() {
|
||||
m_steamLib = base::load_dll(
|
||||
base::join_path(base::get_file_path(base::get_app_path()),
|
||||
STEAM_API_DLL_FILENAME));
|
||||
@ -43,18 +90,34 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto SteamAPI_Init = base::get_dll_proc<SteamAPI_Init_Func>(m_steamLib, "SteamAPI_Init");
|
||||
auto SteamAPI_Init = GETPROC(SteamAPI_Init);
|
||||
if (!SteamAPI_Init) {
|
||||
LOG("STEAM: SteamAPI_Init not found...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Call SteamAPI_Init() to connect to Steam
|
||||
if (!SteamAPI_Init()) {
|
||||
LOG("STEAM: Steam is not initialized...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("STEAM: Steam initialized...\n");
|
||||
// Get functions to dispatch callbacks manually
|
||||
auto SteamAPI_ManualDispatch_Init = GETPROC(SteamAPI_ManualDispatch_Init);
|
||||
SteamAPI_ManualDispatch_RunFrame = GETPROC(SteamAPI_ManualDispatch_RunFrame);
|
||||
SteamAPI_ManualDispatch_GetNextCallback = GETPROC(SteamAPI_ManualDispatch_GetNextCallback);
|
||||
SteamAPI_ManualDispatch_FreeLastCallback = GETPROC(SteamAPI_ManualDispatch_FreeLastCallback);
|
||||
auto SteamAPI_GetHSteamPipe = GETPROC(SteamAPI_GetHSteamPipe);
|
||||
if (SteamAPI_ManualDispatch_Init &&
|
||||
SteamAPI_ManualDispatch_RunFrame &&
|
||||
SteamAPI_ManualDispatch_GetNextCallback &&
|
||||
SteamAPI_ManualDispatch_FreeLastCallback &&
|
||||
SteamAPI_GetHSteamPipe) {
|
||||
SteamAPI_ManualDispatch_Init();
|
||||
m_pipe = SteamAPI_GetHSteamPipe();
|
||||
}
|
||||
|
||||
LOG("STEAM: Steam initialized\n");
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
@ -62,32 +125,132 @@ public:
|
||||
if (!m_steamLib)
|
||||
return;
|
||||
|
||||
auto SteamAPI_Shutdown = base::get_dll_proc<SteamAPI_Shutdown_Func>(m_steamLib, "SteamAPI_Shutdown");
|
||||
auto SteamAPI_Shutdown = GETPROC(SteamAPI_Shutdown);
|
||||
if (SteamAPI_Shutdown) {
|
||||
LOG("STEAM: Steam shutdown...\n");
|
||||
SteamAPI_Shutdown();
|
||||
}
|
||||
|
||||
base::unload_dll(m_steamLib);
|
||||
unloadLib();
|
||||
}
|
||||
|
||||
bool initialized() const {
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void runCallbacks() {
|
||||
if (!m_pipe)
|
||||
return;
|
||||
|
||||
ASSERT(SteamAPI_ManualDispatch_RunFrame);
|
||||
ASSERT(SteamAPI_ManualDispatch_GetNextCallback);
|
||||
ASSERT(SteamAPI_ManualDispatch_FreeLastCallback);
|
||||
|
||||
SteamAPI_ManualDispatch_RunFrame(m_pipe);
|
||||
|
||||
CallbackMsg_t msg;
|
||||
if (SteamAPI_ManualDispatch_GetNextCallback(m_pipe, &msg)) {
|
||||
//TRACEARGS("SteamAPI_ManualDispatch_GetNextCallback", msg.callback);
|
||||
|
||||
bool disconnected = false;
|
||||
switch (msg.callback) {
|
||||
case kSteamServersDisconnected:
|
||||
case kSteamUndocumentedLastCallback:
|
||||
disconnected = true;
|
||||
break;
|
||||
|
||||
// When a screenshot is ready, we open the Steam library of screenshots
|
||||
case kScreenshotReady: {
|
||||
std::string url = "steam://open/screenshots/";
|
||||
|
||||
auto SteamAPI_SteamUtils_v009 = GETPROC(SteamAPI_SteamUtils_v009);
|
||||
auto SteamAPI_ISteamUtils_GetAppID = GETPROC(SteamAPI_ISteamUtils_GetAppID);
|
||||
if (SteamAPI_SteamUtils_v009 &&
|
||||
SteamAPI_ISteamUtils_GetAppID) {
|
||||
ISteamUtils* utils = SteamAPI_SteamUtils_v009();
|
||||
if (utils) {
|
||||
int appId = SteamAPI_ISteamUtils_GetAppID(utils);
|
||||
url += base::convert_to<std::string>(appId);
|
||||
}
|
||||
}
|
||||
base::launcher::open_url(url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SteamAPI_ManualDispatch_FreeLastCallback(m_pipe);
|
||||
|
||||
// If the Steam client is closed, we have to unload the DLL and
|
||||
// don't use the pipe or any Steam API at all, in other case we
|
||||
// would crash.
|
||||
if (disconnected) {
|
||||
LOG("STEAM: Disconnected\n");
|
||||
unloadLib();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool writeScreenshot(void* rgbBuffer,
|
||||
uint32_t sizeInBytes,
|
||||
int width, int height) {
|
||||
if (!m_initialized)
|
||||
return false;
|
||||
|
||||
auto SteamScreenshots = GETPROC(SteamAPI_SteamScreenshots_v003);
|
||||
auto WriteScreenshot = GETPROC(SteamAPI_ISteamScreenshots_WriteScreenshot);
|
||||
if (!SteamScreenshots || !WriteScreenshot) {
|
||||
LOG("STEAM: Error getting Steam Screenshot API functions\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto screenshots = SteamScreenshots();
|
||||
if (!screenshots) {
|
||||
LOG("STEAM: Error getting Steam Screenshot API instance\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
WriteScreenshot(screenshots, rgbBuffer, sizeInBytes, width, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
base::dll m_steamLib;
|
||||
bool m_initialized;
|
||||
void unloadLib() {
|
||||
base::unload_dll(m_steamLib);
|
||||
m_steamLib = nullptr;
|
||||
m_initialized = false;
|
||||
m_pipe = 0;
|
||||
}
|
||||
|
||||
bool m_initialized = false;
|
||||
base::dll m_steamLib = nullptr;
|
||||
|
||||
// To handle callbacks manually
|
||||
HSteamPipe m_pipe = 0;
|
||||
SteamAPI_ManualDispatch_RunFrame_Func SteamAPI_ManualDispatch_RunFrame = nullptr;
|
||||
SteamAPI_ManualDispatch_GetNextCallback_Func SteamAPI_ManualDispatch_GetNextCallback = nullptr;
|
||||
SteamAPI_ManualDispatch_FreeLastCallback_Func SteamAPI_ManualDispatch_FreeLastCallback = nullptr;
|
||||
};
|
||||
|
||||
SteamAPI* g_instance = nullptr;
|
||||
|
||||
// static
|
||||
SteamAPI* SteamAPI::instance()
|
||||
{
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
SteamAPI::SteamAPI()
|
||||
: m_impl(new Impl)
|
||||
{
|
||||
ASSERT(g_instance == nullptr);
|
||||
g_instance = this;
|
||||
}
|
||||
|
||||
SteamAPI::~SteamAPI()
|
||||
{
|
||||
delete m_impl;
|
||||
|
||||
ASSERT(g_instance == this);
|
||||
g_instance = nullptr;
|
||||
}
|
||||
|
||||
bool SteamAPI::initialized() const
|
||||
@ -95,4 +258,16 @@ bool SteamAPI::initialized() const
|
||||
return m_impl->initialized();
|
||||
}
|
||||
|
||||
void SteamAPI::runCallbacks()
|
||||
{
|
||||
m_impl->runCallbacks();
|
||||
}
|
||||
|
||||
bool SteamAPI::writeScreenshot(void* rgbBuffer,
|
||||
uint32_t sizeInBytes,
|
||||
int width, int height)
|
||||
{
|
||||
return m_impl->writeScreenshot(rgbBuffer, sizeInBytes, width, height);
|
||||
}
|
||||
|
||||
} // namespace steam
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Steam Wrapper
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -12,10 +13,17 @@ namespace steam {
|
||||
|
||||
class SteamAPI {
|
||||
public:
|
||||
static SteamAPI* instance();
|
||||
|
||||
SteamAPI();
|
||||
~SteamAPI();
|
||||
|
||||
bool initialized() const;
|
||||
void runCallbacks();
|
||||
|
||||
bool writeScreenshot(void* rgbBuffer,
|
||||
uint32_t sizeInBytes,
|
||||
int width, int height);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
|
2
src/tga
2
src/tga
@ -1 +1 @@
|
||||
Subproject commit 1838abea0025288d636d3f367c692caae0f551d8
|
||||
Subproject commit db4237e672d9e7370f074a273b49644588754d82
|
2
src/undo
2
src/undo
@ -1 +1 @@
|
||||
Subproject commit cd8970dc2f6f2ad24604801c3a1d18cd67d7904f
|
||||
Subproject commit 64bc33ab67c42330cc6cedd528c23eb33c445d64
|
1
third_party/CMakeLists.txt
vendored
1
third_party/CMakeLists.txt
vendored
@ -33,6 +33,7 @@ if(NOT USE_SHARED_GIFLIB)
|
||||
endif()
|
||||
|
||||
if(WITH_WEBP_SUPPORT)
|
||||
set(WEBP_BUILD_EXTRAS OFF CACHE BOOL "Build extras.")
|
||||
add_subdirectory(libwebp)
|
||||
endif()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user