diff --git a/data/strings/en.ini b/data/strings/en.ini
index 0f9c9ddb1..6edbfd005 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -1142,6 +1142,7 @@ octree = Octree
title = Notice
description = Do you want to load the following files as an animation?
repeat = Do the same for other files
+duration = Duration
agree = &Agree
skip = &Skip
diff --git a/data/widgets/open_sequence.xml b/data/widgets/open_sequence.xml
index be47668f0..3bb5d985c 100644
--- a/data/widgets/open_sequence.xml
+++ b/data/widgets/open_sequence.xml
@@ -8,6 +8,10 @@
+
+
+
+
diff --git a/src/app/file/bmp_format.cpp b/src/app/file/bmp_format.cpp
index b2bf636e9..552397adf 100644
--- a/src/app/file/bmp_format.cpp
+++ b/src/app/file/bmp_format.cpp
@@ -874,32 +874,36 @@ static void read_rle4_compressed_image(FILE *f, Image *image, const BITMAPINFOHE
}
}
+static uint32_t calc_shift(const uint32_t channelMask, int& channelBits)
+{
+ uint32_t channelShift = 0;
+ uint32_t mask = 0;
+ if (channelMask) {
+ mask = ~channelMask;
+ while (mask & 1) {
+ ++channelShift;
+ mask >>= 1;
+ }
+ if (mask) {
+ mask = ~mask;
+ while (mask & 1) {
+ channelBits++;
+ mask >>= 1;
+ }
+ }
+ else
+ channelBits = 32 - channelShift;
+ }
+ else
+ channelBits = 8;
+ return channelShift;
+}
+
static int read_bitfields_image(FILE *f, Image *image, BITMAPINFOHEADER *infoheader,
uint32_t rmask, uint32_t gmask, uint32_t bmask,
uint32_t amask, bool& withAlpha)
{
-#define CALC_SHIFT(c) \
- c##shift = 0; \
- if (c##mask) { \
- mask = ~c##mask; \
- while (mask & 1) { \
- ++c##shift; \
- mask >>= 1; \
- } \
- if (mask) { \
- mask = ~mask; \
- while (mask & 1) { \
- c##bits++; \
- mask >>= 1; \
- } \
- } \
- else \
- c##bits = 32 - c##shift; \
- } \
- else \
- c##bits = 8; \
-
- uint32_t buffer, mask, rshift, gshift, bshift, ashift;
+ uint32_t buffer, rshift, gshift, bshift, ashift;
int rbits = 0, gbits = 0, bbits = 0, abits = 0;
int i, j, k, line, height, dir, r, g, b, a;
int bits_per_pixel;
@@ -911,10 +915,10 @@ static int read_bitfields_image(FILE *f, Image *image, BITMAPINFOHEADER *infohea
height = ABS(height);
/* calculate shifts */
- CALC_SHIFT(r);
- CALC_SHIFT(g);
- CALC_SHIFT(b);
- CALC_SHIFT(a);
+ rshift = calc_shift(rmask, rbits);
+ gshift = calc_shift(gmask, gbits);
+ bshift = calc_shift(bmask, bbits);
+ ashift = calc_shift(amask, abits);
/* calculate bits-per-pixel and bytes-per-pixel */
bits_per_pixel = infoheader->biBitCount;
diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp
index 6fa7beb98..1a3891131 100644
--- a/src/app/file/file.cpp
+++ b/src/app/file/file.cpp
@@ -388,6 +388,18 @@ FileOp* FileOp::createLoadDocumentOperation(Context* context,
window.files()->getSelectedChild() != nullptr);
});
+ window.duration()->setTextf("%d", fop->m_seq.duration);
+ window.duration()->Change.connect(
+ [&]() {
+ fop->m_seq.duration = window.duration()->textInt();
+ // If the animation duration is changed we'll prefer to
+ // agree on loading the sequence if the user press Enter.
+ //
+ // TODO maybe the "Agree" button should be the default
+ // focus magnet in this dialog
+ window.agree()->setFocusMagnet(true);
+ });
+
window.openWindowInForeground();
// Don't show this alert again.
@@ -836,6 +848,8 @@ void FileOp::operate(IFileOpProgress* progress)
#endif
}
+ m_document->sprite()->setFrameDuration(frame, m_seq.duration);
+
++frame;
m_seq.progress_offset += m_seq.progress_fraction;
}
@@ -1422,6 +1436,7 @@ FileOp::FileOp(FileOpType type,
m_seq.frame = frame_t(0);
m_seq.layer = nullptr;
m_seq.last_cel = nullptr;
+ m_seq.duration = 100;
m_seq.flags = 0;
}
diff --git a/src/app/file/file.h b/src/app/file/file.h
index 7f99e05cd..b880365df 100644
--- a/src/app/file/file.h
+++ b/src/app/file/file.h
@@ -314,6 +314,7 @@ namespace app {
bool has_alpha;
LayerImage* layer;
Cel* last_cel;
+ int duration;
// Flags after the user choose what to do with the sequence.
int flags;
} m_seq;
diff --git a/src/app/script/api_version.h b/src/app/script/api_version.h
index c5269d02f..ee813acec 100644
--- a/src/app/script/api_version.h
+++ b/src/app/script/api_version.h
@@ -10,6 +10,6 @@
// Increment this value if the scripting API is modified between two
// released Aseprite versions.
-#define API_VERSION 19
+#define API_VERSION 20
#endif
diff --git a/src/app/script/range_class.cpp b/src/app/script/range_class.cpp
index 29a42fef8..165d902ae 100644
--- a/src/app/script/range_class.cpp
+++ b/src/app/script/range_class.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2021 Igara Studio S.A.
+// Copyright (C) 2018-2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@@ -11,14 +11,18 @@
#include "app/app.h"
#include "app/context.h"
#include "app/doc_range.h"
+#include "app/modules/editors.h"
#include "app/script/docobj.h"
#include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/site.h"
+#include "app/ui/editor/editor.h"
#include "app/util/range_utils.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/object_ids.h"
+#include "doc/slice.h"
+#include "doc/slices.h"
#include "doc/sprite.h"
#include "doc/tile.h"
@@ -33,9 +37,10 @@ namespace {
struct RangeObj { // This is like DocRange but referencing objects with IDs
DocRange::Type type;
ObjectId spriteId;
- std::set layers;
+ doc::SelectedObjects layers;
std::vector frames;
- std::set cels;
+ doc::SelectedObjects cels;
+ doc::SelectedObjects slices;
std::vector colors;
std::vector tiles;
@@ -88,18 +93,23 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
if (site.selectedTiles().picks() > 0)
tiles = site.selectedTiles().toVectorOfIndexes();
+
+ slices = site.selectedSlices();
}
Sprite* sprite(lua_State* L) { return check_docobj(L, doc::get(spriteId)); }
bool contains(const Layer* layer) const {
- return layers.find(layer->id()) != layers.end();
+ return layers.contains(layer->id());
}
bool contains(const frame_t frame) const {
return std::find(frames.begin(), frames.end(), frame) != frames.end();
}
bool contains(const Cel* cel) const {
- return cels.find(cel->id()) != cels.end();
+ return cels.contains(cel->id());
+ }
+ bool contains(const Slice* slice) const {
+ return slices.contains(slice->id());
}
bool containsColor(const color_t color) const {
return (std::find(colors.begin(), colors.end(), color) != colors.end());
@@ -139,6 +149,9 @@ int Range_contains(lua_State* L)
else if (Cel* cel = may_get_docobj(L, 2)) {
result = obj->contains(cel);
}
+ else if (Slice* slice = may_get_docobj(L, 2)) {
+ result = obj->contains(slice);
+ }
else {
frame_t frame = get_frame_number_from_arg(L, 2);
result = obj->contains(frame);
@@ -176,6 +189,13 @@ int Range_clear(lua_State* L)
doc::PalettePicks picks;
ctx->setSelectedColors(picks);
+#ifdef ENABLE_UI
+ // Empty selected slices in the current editor
+ // TODO add a new function to Context class for this
+ if (current_editor)
+ current_editor->clearSlicesSelection();
+#endif
+
obj->updateFromSite(ctx->activeSite());
return 0;
}
@@ -275,6 +295,18 @@ int Range_get_tiles(lua_State* L)
return 1;
}
+int Range_get_slices(lua_State* L)
+{
+ auto obj = get_obj(L, 1);
+ lua_newtable(L);
+ int j = 1;
+ for (auto sliceId : obj->slices) {
+ push_docobj(L, sliceId);
+ lua_rawseti(L, -2, j++);
+ }
+ return 1;
+}
+
int Range_set_layers(lua_State* L)
{
auto obj = get_obj(L, 1);
@@ -355,6 +387,29 @@ int Range_set_tiles(lua_State* L)
}
}
ctx->setSelectedTiles(picks);
+ obj->updateFromSite(ctx->activeSite());
+ return 0;
+}
+
+int Range_set_slices(lua_State* L)
+{
+ auto obj = get_obj(L, 1);
+ app::Context* ctx = App::instance()->context();
+
+ // TODO we should add support to CLI scripts
+#ifdef ENABLE_UI
+ if (current_editor) {
+ current_editor->clearSlicesSelection();
+ const int len = luaL_len(L, 2);
+ for (int i = 1; i <= len; i++) {
+ if (lua_geti(L, 2, i) != LUA_TNIL)
+ current_editor->selectSlice(get_docobj(L, -1));
+ lua_pop(L, 1);
+ }
+ }
+#endif
+
+ obj->updateFromSite(ctx->activeSite());
return 0;
}
@@ -378,6 +433,7 @@ const Property Range_properties[] = {
{ "editableImages", Range_get_editableImages, nullptr },
{ "colors", Range_get_colors, Range_set_colors },
{ "tiles", Range_get_tiles, Range_set_tiles },
+ { "slices", Range_get_slices, Range_set_slices },
{ nullptr, nullptr, nullptr }
};