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 } };