Ejemplo n.º 1
0
        /// <summary>
        /// Merge all layer groups that we can merge while preserving the z-order of layers that overlap in time.
        /// </summary>
        public void MergeAllPossibleLayerGroups(MergeHelper mergeHelper)
        {
            foreach (var pair in GetCandidatesForMerging())
            {
                var a = pair.Item1;
                var b = pair.Item2;

                if (a.MergedWithNode is not null || b.MergedWithNode is not null)
                {
                    continue;
                }

                var mergeRes = mergeHelper.MergeLayerGroups(a.Group, b.Group);

                if (!mergeRes.Success)
                {
                    continue;
                }

                a.MergedGroup = b.MergedGroup = mergeRes.Value !;

                a.MergedWithNode = b;
                b.MergedWithNode = a;
            }
        }
        public static LottieComposition Optimize(LottieComposition composition)
        {
            List <Layer> layers = composition.Layers.GetLayersBottomToTop().ToList();

            // Sort layers by beginning of their visible time range.
            layers.Sort((a, b) => a.InPoint.CompareTo(b.InPoint));

            // Create merge helper instance. It can merge layers and will store all generated assets that are refrenced by new layers.
            var mergeHelper = new MergeHelper(composition);

            // Maximal distance between two animations (in frames).
            double maximalAllowedDistance = composition.OutPoint - composition.InPoint;

            // This optimizations works with the following heuristic:
            // 1. Let's fix minimal allowed score and allowed distance (distance between end and start point of two layers)
            // 2. Merge all layers that fit these constraints.
            // 3. Increase allowedDistance in inner loop
            // 4. Decrease minimalScore in outer loop
            // This heuristic is needed because in theory any two layers can be merged, but some of them can be merged with higher score.
            // And if we merge pairs with worse score first - it can result in worse optimization overall.
            // So we are trying to merge pairs of layers with the higher score first, while trying to merge pairs that are nerby
            // because if we merge two layers that are far away then we can't insert any other layer between them (for merging).
            // Example: we have three layers [0; 10] [30; 40] [70; 80]
            // It is better to merge [0; 10] and [30; 40] first (result is [0; 40]), and then merge [70; 80] with the result.
            // If we merge [0; 10] and [70; 80] first, we will get [0; 80] as the result, and we will not be able to merge it with [30; 40]
            // because they intersect.
            // TODO: there is surely a better heuristic, we need more experiments to find it.
            // TODO: Numbers here are chosen almost arbitrarily. Just made a few experiments with different values
            // and chose numbers that showed the best results while finishing in reasonable time. Need to come up with
            // a better way of determining those numbers.
            for (double minimalScore = 0.95; minimalScore > 0.1; minimalScore *= 0.8)
            {
                for (double allowedDistance = 1.0; allowedDistance < maximalAllowedDistance; allowedDistance *= 2.0)
                {
                    for (int i = 0; i < layers.Count; i++)
                    {
                        for (int j = i + 1; j < layers.Count; j++)
                        {
                            if (layers[j] is not PreCompLayer || layers[i] is not PreCompLayer)
                            {
                                continue;
                            }

                            if (layers[j].InPoint - layers[i].OutPoint > allowedDistance)
                            {
                                continue;
                            }

                            var mergeRes = mergeHelper.MergeLayers(layers[i], layers[j]);

                            // Score of merging two layers right now calculated as "intersection over minimum" of number of
                            // layers in layer collections. We can merge only PreCompLayers with LayerCollectionAsset's right now.
                            // If we have layer collection with 10 layers, and layer collection with 12 layers and after merge we get
                            // layer collection with 16 layers, then the score will be (10 + 12 - 16) / 10 = 0.6.
                            if (mergeRes.Success && minimalScore <= mergeRes.Score)
                            {
                                layers.RemoveAt(j);
                                layers.RemoveAt(i);

                                layers.Insert(i, mergeRes.Value !);
                                j--;
                            }
                        }
                    }
                }
            }

            List <Asset> usedAssets = new List <Asset>();

            // Add all generated and old assets, next optimizer will delte all unused assets.
            // TODO: detect and add only used assets. This is done to simplify the PR for now
            usedAssets.AddRange(mergeHelper.AssetsGenerated);
            usedAssets.AddRange(composition.Assets);

            return(new LottieComposition(
                       composition.Name,
                       composition.Width,
                       composition.Height,
                       composition.InPoint,
                       composition.OutPoint,
                       composition.FramesPerSecond,
                       composition.Is3d,
                       composition.Version,
                       new AssetCollection(usedAssets),
                       composition.Chars,
                       composition.Fonts,
                       new LayerCollection(layers),
                       composition.Markers,
                       composition.ExtraData));
        }