Skip to content

Commit 426710b

Browse files
authored
Fixed ComposeMatteLayers method to work for invisible layers (#479)
1 parent 6078607 commit 426710b

File tree

5 files changed

+54
-43
lines changed

5 files changed

+54
-43
lines changed

Lottie-Windows.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Issues", "Issues", "{07D6DF
132132
source\Issues\LT0041.md = source\Issues\LT0041.md
133133
source\Issues\LT0042.md = source\Issues\LT0042.md
134134
source\Issues\LT0043.md = source\Issues\LT0043.md
135+
source\Issues\LT0044.md = source\Issues\LT0044.md
135136
source\Issues\LV0001.md = source\Issues\LV0001.md
136137
source\Issues\LV0002.md = source\Issues\LV0002.md
137138
source\Issues\LV0003.md = source\Issues\LV0003.md

LottieGen/LottieGen.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Issues", "Issues", "{BDB88D
8787
..\source\Issues\LT0041.md = ..\source\Issues\LT0041.md
8888
..\source\Issues\LT0042.md = ..\source\Issues\LT0042.md
8989
..\source\Issues\LT0043.md = ..\source\Issues\LT0043.md
90+
..\source\Issues\LT0044.md = ..\source\Issues\LT0044.md
9091
..\source\Issues\LV0001.md = ..\source\Issues\LV0001.md
9192
..\source\Issues\LV0002.md = ..\source\Issues\LV0002.md
9293
..\source\Issues\LV0003.md = ..\source\Issues\LV0003.md

source/Issues/LT0044.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[comment]: # (name:MatteLayerIsNeverVisible)
2+
[comment]: # (text:Matte layer is never visible.)
3+
4+
# Lottie-Windows Warning LT0044
5+
6+
One of the matte layers is never visible. This does not affect the animation, but may be unintentional.
7+
8+
## Resources
9+
10+
* [Lottie-Windows repository](https://aka.ms/lottie)
11+
* [Questions and feedback via Github](https://github.com/windows-toolkit/Lottie-Windows/issues)

source/LottieToWinComp/Masks.cs

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -138,61 +138,57 @@ public static LayerTranslator TranslateMatteLayer(
138138

139139
// Walk the collection of layer data and for each pair of matte layer and matted layer, compose them and return a visual
140140
// 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)
142142
{
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+
//
148148
// NOTE: The items appear in reverse order from how they appear in the original Lottie file.
149149
// 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++)
151151
{
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;
156154

157-
if (translatedLayer.IsShape)
155+
if (layerIsMattedLayer)
158156
{
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)
163158
{
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++;
171161

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+
}
191185
}
186+
187+
// We do not have matte layer or both mattedLayer and matte were null so we can just skip them.
192188
}
193189
else
194190
{
195-
// Return the shape which does not participate in mattes.
191+
// Just a regular layer, no mattes applied.
196192
yield return translatedLayer;
197193
}
198194
}

source/LottieToWinComp/TranslationIssues.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ internal TranslationIssues(bool throwOnIssue)
124124

125125
internal void PathWithRoundCornersIsNotFullySupported() => Report("LT0043", "Using a path with rounded corners can lead to inaccurate results.");
126126

127+
internal void MatteLayerIsNeverVisible() => Report("LT0044", "One of the matte layers is never visible. It may be unintentional.");
128+
127129
void Report(string code, string description)
128130
{
129131
_issues.Add((code, description));

0 commit comments

Comments
 (0)