File indexing completed on 2025-02-02 04:11:06
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "layer.hpp" 0008 0009 #include <QPainter> 0010 0011 #include "model/assets/composition.hpp" 0012 #include "model/document.hpp" 0013 0014 GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Layer) 0015 0016 void glaxnimate::model::Layer::ChildLayerIterator::find_first() 0017 { 0018 while ( index < comp->size() && (*comp)[index]->docnode_group_parent() != parent ) 0019 ++index; 0020 } 0021 0022 glaxnimate::model::VisualNode* glaxnimate::model::Layer::ChildLayerIterator::operator*() const 0023 { 0024 return (*comp)[index]; 0025 } 0026 0027 glaxnimate::model::VisualNode* glaxnimate::model::Layer::ChildLayerIterator::operator->() const 0028 { 0029 return (*comp)[index]; 0030 } 0031 0032 glaxnimate::model::VisualNode * glaxnimate::model::Layer::docnode_group_parent() const 0033 { 0034 return parent.get(); 0035 } 0036 0037 glaxnimate::model::VisualNode * glaxnimate::model::Layer::docnode_group_child(int index) const 0038 { 0039 ChildLayerIterator iter(owner(), this, 0); 0040 std::advance(iter, index); 0041 return *iter; 0042 } 0043 0044 int glaxnimate::model::Layer::docnode_group_child_count() const 0045 { 0046 if ( !owner() ) 0047 return 0; 0048 int sz = 0; 0049 for ( const auto& sib : *owner() ) 0050 if ( sib->docnode_group_parent() == this ) 0051 sz++; 0052 return sz; 0053 } 0054 0055 std::vector<glaxnimate::model::DocumentNode*> glaxnimate::model::Layer::valid_parents() const 0056 { 0057 std::vector<glaxnimate::model::DocumentNode*> refs; 0058 refs.push_back(nullptr); 0059 0060 if ( is_top_level() ) 0061 { 0062 for ( const auto& sh : *owner() ) 0063 { 0064 if ( auto lay = qobject_cast<glaxnimate::model::Layer*>(sh.get()) ) 0065 if ( !is_ancestor_of(lay) ) 0066 refs.push_back(lay); 0067 } 0068 } 0069 0070 return refs; 0071 } 0072 0073 bool glaxnimate::model::Layer::is_valid_parent(glaxnimate::model::DocumentNode* node) const 0074 { 0075 if ( node == nullptr ) 0076 return true; 0077 0078 if ( is_top_level() ) 0079 { 0080 if ( Layer* layer = qobject_cast<Layer*>(node) ) 0081 return !is_ancestor_of(layer); 0082 } 0083 0084 return false; 0085 } 0086 0087 bool glaxnimate::model::Layer::is_ancestor_of ( const glaxnimate::model::Layer* other ) const 0088 { 0089 while ( other ) 0090 { 0091 if ( other == this ) 0092 return true; 0093 0094 other = other->parent.get(); 0095 } 0096 0097 return false; 0098 } 0099 0100 void glaxnimate::model::Layer::set_time(glaxnimate::model::FrameTime t) 0101 { 0102 Object::set_time(relative_time(t)); 0103 } 0104 0105 0106 bool glaxnimate::model::Layer::is_top_level() const 0107 { 0108 return qobject_cast<Composition*>(docnode_parent()); 0109 } 0110 0111 void glaxnimate::model::Layer::paint(QPainter* painter, FrameTime time, PaintMode mode, glaxnimate::model::Modifier* modifier) const 0112 { 0113 if ( !visible.get() || (mode == Render && !render.get()) ) 0114 return; 0115 0116 time = relative_time(time); 0117 if ( !animation->time_visible(time) ) 0118 return; 0119 0120 if ( mask->has_mask() ) 0121 { 0122 auto n_shapes = shapes.size(); 0123 if ( n_shapes <= 1 ) 0124 return; 0125 0126 painter->save(); 0127 auto transform = group_transform_matrix(time); 0128 painter->setTransform(transform, true); 0129 0130 if ( shapes[0]->visible.get() ) 0131 { 0132 QPainterPath clip = shapes[0]->to_clip(time); 0133 clip.setFillRule(Qt::WindingFill); 0134 if ( mask->inverted.get() ) 0135 { 0136 QPainterPath outer_clip; 0137 outer_clip.addPolygon( 0138 transform.inverted().map(owner_composition()->rect()) 0139 ); 0140 clip = outer_clip.subtracted(clip); 0141 } 0142 painter->setClipPath(clip, Qt::IntersectClip); 0143 } 0144 0145 0146 on_paint(painter, time, mode, modifier); 0147 0148 for ( int i = 1; i < n_shapes; i++ ) 0149 docnode_visual_child(i)->paint(painter, time, mode); 0150 0151 painter->restore(); 0152 } 0153 else 0154 { 0155 VisualNode::paint(painter, time, mode); 0156 } 0157 } 0158 0159 QPainterPath glaxnimate::model::Layer::to_clip(glaxnimate::model::FrameTime time) const 0160 { 0161 time = relative_time(time); 0162 if ( !animation->time_visible(time) || !render.get() ) 0163 return {}; 0164 0165 return Group::to_clip(time); 0166 } 0167 0168 QPainterPath glaxnimate::model::Layer::to_painter_path_impl(glaxnimate::model::FrameTime time) const 0169 { 0170 time = relative_time(time); 0171 if ( !animation->time_visible(time) || !render.get() ) 0172 return {}; 0173 0174 return Group::to_painter_path_impl(time); 0175 } 0176 0177 QIcon glaxnimate::model::Layer::tree_icon() const 0178 { 0179 return mask->has_mask() ? QIcon::fromTheme("path-clip-edit") : QIcon::fromTheme("folder"); 0180 } 0181 0182 QIcon glaxnimate::model::Layer::static_tree_icon() 0183 { 0184 return QIcon::fromTheme("folder"); 0185 } 0186 0187 std::unique_ptr<glaxnimate::model::ShapeElement> glaxnimate::model::Layer::to_path() const 0188 { 0189 auto clone = std::make_unique<glaxnimate::model::Layer>(document()); 0190 0191 for ( BaseProperty* prop : properties() ) 0192 { 0193 if ( prop != &shapes ) 0194 clone->get_property(prop->name())->assign_from(prop); 0195 } 0196 0197 for ( const auto& shape : shapes ) 0198 { 0199 clone->shapes.insert(shape->to_path()); 0200 if ( shape->is_instance<glaxnimate::model::Modifier>() ) 0201 break; 0202 } 0203 0204 return clone; 0205 }