mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-12 16:14:10 +00:00
Respect z-index layer ordering when there are empty cels
An empty cel must be counted as a layer for the z-index ordering, so the z-index refers to number of layers to move back/front, but number of non-empty cels in the specific frame. A this fix, a new issue appears: #3820
This commit is contained in:
parent
2962bb3676
commit
172e1a615b
@ -229,7 +229,8 @@ void ShaderRenderer::renderPlan(SkCanvas* canvas,
|
||||
const doc::frame_t frame,
|
||||
const gfx::ClipF& area)
|
||||
{
|
||||
for (const Cel* cel : plan.cels()) {
|
||||
for (const auto& item : plan.items()) {
|
||||
const Cel* cel = item.cel;
|
||||
const Layer* layer = cel->layer();
|
||||
|
||||
switch (layer->type()) {
|
||||
|
@ -26,9 +26,11 @@ void RenderPlan::addLayer(const Layer* layer,
|
||||
const frame_t frame)
|
||||
{
|
||||
// We cannot add new layers after using processZIndexes()/modified
|
||||
// m_cels array using z-indexes.
|
||||
// m_items array using z-indexes.
|
||||
ASSERT(m_processZIndex == true);
|
||||
|
||||
++m_order;
|
||||
|
||||
// We can't read this layer
|
||||
if (!layer->isVisible())
|
||||
return;
|
||||
@ -38,7 +40,7 @@ void RenderPlan::addLayer(const Layer* layer,
|
||||
case ObjectType::LayerImage:
|
||||
case ObjectType::LayerTilemap: {
|
||||
if (Cel* cel = layer->cel(frame)) {
|
||||
m_cels.push_back(cel);
|
||||
m_items.emplace_back(m_order, cel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -57,10 +59,10 @@ void RenderPlan::processZIndexes() const
|
||||
{
|
||||
m_processZIndex = false;
|
||||
|
||||
// If all cels has a z-index = 0, we can just use the m_cels as it is
|
||||
// If all cels has a z-index = 0, we can just use the m_items as it is
|
||||
bool noZIndex = true;
|
||||
for (int i=0; i<int(m_cels.size()); ++i) {
|
||||
const int z = m_cels[i]->zIndex();
|
||||
for (int i=0; i<int(m_items.size()); ++i) {
|
||||
const int z = m_items[i].cel->zIndex();
|
||||
if (z != 0) {
|
||||
noZIndex = false;
|
||||
break;
|
||||
@ -69,24 +71,15 @@ void RenderPlan::processZIndexes() const
|
||||
if (noZIndex)
|
||||
return;
|
||||
|
||||
// Order cels using its real index in the m_cels array + its z-index value
|
||||
std::vector<std::pair<int, Cel*>> indexedCels;
|
||||
const int n = m_cels.size();
|
||||
indexedCels.reserve(n);
|
||||
for (int i=0; i<n; ++i) {
|
||||
Cel* cel = m_cels[i];
|
||||
int z = cel->zIndex();
|
||||
indexedCels.push_back(std::make_pair(i+z, cel));
|
||||
}
|
||||
std::sort(indexedCels.begin(), indexedCels.end(),
|
||||
[](const auto& a, const auto& b){
|
||||
// Order cels using its "order" number in m_items array + cel z-index offset
|
||||
for (Item& item : m_items)
|
||||
item.order = item.order + item.cel->zIndex();
|
||||
std::sort(m_items.begin(), m_items.end(),
|
||||
[](const Item& a, const Item& b){
|
||||
return
|
||||
(a.first < b.first) ||
|
||||
(a.first == b.first && (a.second->zIndex() < b.second->zIndex()));
|
||||
(a.order < b.order) ||
|
||||
(a.order == b.order && (a.cel->zIndex() < b.cel->zIndex()));
|
||||
});
|
||||
for (int i=0; i<n; ++i) {
|
||||
m_cels[i] = indexedCels[i].second;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
@ -19,12 +19,20 @@ namespace doc {
|
||||
// layer/layers.
|
||||
class RenderPlan {
|
||||
public:
|
||||
struct Item {
|
||||
int order;
|
||||
Cel* cel;
|
||||
Item(int order = 0, Cel* cel = nullptr)
|
||||
: order(order), cel(cel) { }
|
||||
};
|
||||
using Items = std::vector<Item>;
|
||||
|
||||
RenderPlan();
|
||||
|
||||
const CelList& cels() const {
|
||||
const Items& items() const {
|
||||
if (m_processZIndex)
|
||||
processZIndexes();
|
||||
return m_cels;
|
||||
return m_items;
|
||||
}
|
||||
|
||||
void addLayer(const Layer* layer,
|
||||
@ -33,7 +41,8 @@ namespace doc {
|
||||
private:
|
||||
void processZIndexes() const;
|
||||
|
||||
mutable CelList m_cels;
|
||||
int m_order = 0;
|
||||
mutable Items m_items;
|
||||
mutable bool m_processZIndex = true;
|
||||
};
|
||||
|
||||
|
@ -29,11 +29,11 @@ using namespace doc;
|
||||
{ \
|
||||
RenderPlan plan; \
|
||||
plan.addLayer(spr->root(), 0); \
|
||||
const auto& cels = plan.cels(); \
|
||||
EXPECT_EQ(a, cels[0]) << HELPER_LOG(cels[0], a); \
|
||||
EXPECT_EQ(b, cels[1]) << HELPER_LOG(cels[1], b); \
|
||||
EXPECT_EQ(c, cels[2]) << HELPER_LOG(cels[2], c); \
|
||||
EXPECT_EQ(d, cels[3]) << HELPER_LOG(cels[3], d); \
|
||||
const auto& items = plan.items(); \
|
||||
EXPECT_EQ(a, items[0].cel) << HELPER_LOG(items[0].cel, a); \
|
||||
EXPECT_EQ(b, items[1].cel) << HELPER_LOG(items[1].cel, b); \
|
||||
EXPECT_EQ(c, items[2].cel) << HELPER_LOG(items[2].cel, c); \
|
||||
EXPECT_EQ(d, items[3].cel) << HELPER_LOG(items[3].cel, d); \
|
||||
}
|
||||
|
||||
TEST(RenderPlan, ZIndex)
|
||||
@ -91,6 +91,49 @@ TEST(RenderPlan, ZIndex)
|
||||
EXPECT_PLAN(b, d, c, a);
|
||||
}
|
||||
|
||||
TEST(RenderPlan, ZIndexBugWithEmptyCels)
|
||||
{
|
||||
#undef EXPECT_PLAN
|
||||
#define EXPECT_PLAN(a, b, c) \
|
||||
{ \
|
||||
RenderPlan plan; \
|
||||
plan.addLayer(spr->root(), 0); \
|
||||
const auto& items = plan.items(); \
|
||||
EXPECT_EQ(a, items[0].cel) << HELPER_LOG(items[0].cel, a); \
|
||||
EXPECT_EQ(b, items[1].cel) << HELPER_LOG(items[1].cel, b); \
|
||||
EXPECT_EQ(c, items[2].cel) << HELPER_LOG(items[2].cel, c); \
|
||||
}
|
||||
|
||||
auto doc = std::make_shared<Document>();
|
||||
ImageSpec spec(ColorMode::INDEXED, 2, 2);
|
||||
Sprite* spr;
|
||||
doc->sprites().add(spr = Sprite::MakeStdSprite(spec));
|
||||
|
||||
LayerImage
|
||||
*lay0 = static_cast<LayerImage*>(spr->root()->firstLayer()),
|
||||
*lay1 = new LayerImage(spr),
|
||||
*lay2 = new LayerImage(spr),
|
||||
*lay3 = new LayerImage(spr);
|
||||
|
||||
lay0->setName("a");
|
||||
lay1->setName("b");
|
||||
lay2->setName("c");
|
||||
lay3->setName("d");
|
||||
|
||||
Cel* a = lay0->cel(0), *b, *d;
|
||||
lay1->addCel(b = new Cel(0, ImageRef(Image::create(spec))));
|
||||
// lay2 has an empty cel
|
||||
lay3->addCel(d = new Cel(0, ImageRef(Image::create(spec))));
|
||||
|
||||
spr->root()->insertLayer(lay1, lay0);
|
||||
spr->root()->insertLayer(lay2, lay1);
|
||||
spr->root()->insertLayer(lay3, lay2);
|
||||
|
||||
d->setZIndex(-1); EXPECT_PLAN(a, b, d); // -1 is not enough to pass through lay2
|
||||
d->setZIndex(-2); EXPECT_PLAN(a, d, b);
|
||||
d->setZIndex(-3); EXPECT_PLAN(d, a, b);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
@ -632,9 +632,9 @@ void Sprite::pickCels(const gfx::PointF& pos,
|
||||
{
|
||||
// Iterate cels in reversed order (from the front-most to the
|
||||
// bottom-most) so we pick first visible cel in the given position.
|
||||
const CelList& planCels = plan.cels();
|
||||
for (auto it=planCels.rbegin(), end=planCels.rend(); it!=end; ++it) {
|
||||
Cel* cel = *it;
|
||||
const auto& planItems = plan.items();
|
||||
for (auto it=planItems.rbegin(), end=planItems.rend(); it!=end; ++it) {
|
||||
Cel* cel = it->cel;
|
||||
const Image* image = cel->image();
|
||||
if (!image)
|
||||
continue;
|
||||
|
@ -1006,7 +1006,8 @@ void Render::renderPlan(
|
||||
const BlendMode blendMode,
|
||||
bool isSelected)
|
||||
{
|
||||
for (const Cel* cel : plan.cels()) {
|
||||
for (const auto& item : plan.items()) {
|
||||
const Cel* cel = item.cel;
|
||||
const Layer* layer = cel->layer();
|
||||
|
||||
ASSERT(layer->isVisible()); // Hidden layers shouldn't be in the plan
|
||||
|
Loading…
x
Reference in New Issue
Block a user