@@ -138,61 +138,57 @@ public static LayerTranslator TranslateMatteLayer(
138
138
139
139
// Walk the collection of layer data and for each pair of matte layer and matted layer, compose them and return a visual
140
140
// with the composed result. All other items are not touched.
141
- public static IEnumerable < LayerTranslator > ComposeMattedLayers ( CompositionContext context , IEnumerable < ( LayerTranslator translatedLayer , Layer layer ) > items )
141
+ public static IEnumerable < LayerTranslator > ComposeMattedLayers ( CompositionContext context , IList < ( LayerTranslator translatedLayer , Layer layer ) > items )
142
142
{
143
- // Save off the visual for the layer to be matted when we encounter it. The very next
144
- // layer is the matte layer.
145
- Visual ? mattedVisual = null ;
146
- Layer . MatteType matteType = Layer . MatteType . None ;
147
-
143
+ // After Effects has a concept named "matte layers" which is very similar to masks.
144
+ // If there are two adjacent layers in After Effects and first of them has LayerMatteType not equal to MatteType.None
145
+ // then we should use the second layer as a "matte" for the first one "mattedLayer". "Matte" layer will not be visible but
146
+ // it will define visible region for the "mattedLayer".
147
+ //
148
148
// NOTE: The items appear in reverse order from how they appear in the original Lottie file.
149
149
// This means that the layer to be matted appears right before the layer that is the matte.
150
- foreach ( var ( translatedLayer , layer ) in items )
150
+ for ( int i = 0 ; i < items . Count ; i ++ )
151
151
{
152
- var layerIsMattedLayer = false ;
153
- layerIsMattedLayer = layer . LayerMatteType != Layer . MatteType . None ;
154
-
155
- Visual ? visual = null ;
152
+ var ( translatedLayer , layer ) = items [ i ] ;
153
+ var layerIsMattedLayer = layer . LayerMatteType != Layer . MatteType . None ;
156
154
157
- if ( translatedLayer . IsShape )
155
+ if ( layerIsMattedLayer )
158
156
{
159
- // If the layer is a shape then we need to wrap it
160
- // in a shape visual so that it can be used for matte
161
- // composition.
162
- if ( layerIsMattedLayer || mattedVisual is not null )
157
+ if ( i + 1 < items . Count )
163
158
{
164
- visual = translatedLayer . GetVisualRoot ( context ) ;
165
- }
166
- }
167
- else
168
- {
169
- visual = translatedLayer . GetVisualRoot ( context ) ;
170
- }
159
+ // Move index to the next layer.
160
+ i ++ ;
171
161
172
- if ( visual is not null )
173
- {
174
- // The layer to be matted comes first. The matte layer is the very next layer.
175
- if ( layerIsMattedLayer )
176
- {
177
- mattedVisual = visual ;
178
- matteType = layer . LayerMatteType ;
179
- }
180
- else if ( mattedVisual is not null )
181
- {
182
- var compositedMatteVisual = Masks . TranslateMatteLayer ( context , visual , mattedVisual , matteType == Layer . MatteType . Invert ) ;
183
- mattedVisual = null ;
184
- matteType = Layer . MatteType . None ;
185
- yield return compositedMatteVisual ;
186
- }
187
- else
188
- {
189
- // Return the visual that was not a matte layer or a layer to be matted.
190
- yield return new LayerTranslator . FromVisual ( visual ) ;
162
+ var ( translatedLayerNext , _) = items [ i ] ;
163
+
164
+ Visual ? mattedVisual = translatedLayer . GetVisualRoot ( context ) ;
165
+ Visual ? matte = translatedLayerNext . GetVisualRoot ( context ) ;
166
+
167
+ if ( mattedVisual is not null && matte is not null )
168
+ {
169
+ // Both matte and mattedLayer are not null -> both are visible, compose them into one visual.
170
+ var compositedMatteVisual = TranslateMatteLayer ( context , matte , mattedVisual , layer . LayerMatteType == Layer . MatteType . Invert ) ;
171
+ yield return compositedMatteVisual ;
172
+ }
173
+ else
174
+ {
175
+ context . Issues . MatteLayerIsNeverVisible ( ) ;
176
+
177
+ if ( mattedVisual is not null && matte is null && layer . LayerMatteType == Layer . MatteType . Invert )
178
+ {
179
+ // Matte layer is null, which means that it is never visible and LayerMatteType is equal to Invert.
180
+ // In this case we should just return mattedVisual because matte layer is effectively a no-op.
181
+ // This is how it works in After Effects.
182
+ yield return new LayerTranslator . FromVisual ( mattedVisual ) ;
183
+ }
184
+ }
191
185
}
186
+
187
+ // We do not have matte layer or both mattedLayer and matte were null so we can just skip them.
192
188
}
193
189
else
194
190
{
195
- // Return the shape which does not participate in mattes.
191
+ // Just a regular layer, no mattes applied .
196
192
yield return translatedLayer ;
197
193
}
198
194
}
0 commit comments