Example #1
0
        internal static RectangleShape Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            IAnimatableValue <Vector2?, Vector2?> position = null;
            AnimatablePointValue size        = null;
            AnimatableFloatValue roundedness = null;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "p":
                    position = AnimatablePathValueParser.ParseSplitPath(reader, composition);
                    break;

                case "s":
                    size = AnimatableValueParser.ParsePoint(reader, composition);
                    break;

                case "r":
                    roundedness = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new RectangleShape(name, position, size, roundedness));
        }
Example #2
0
        internal static PolystarShape Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            var    type = PolystarShape.Type.Polygon;
            AnimatableFloatValue points = null;
            IAnimatableValue <Vector2?, Vector2?> position = null;
            AnimatableFloatValue rotation         = null;
            AnimatableFloatValue outerRadius      = null;
            AnimatableFloatValue outerRoundedness = null;
            AnimatableFloatValue innerRadius      = null;
            AnimatableFloatValue innerRoundedness = null;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "sy":
                    type = (PolystarShape.Type)reader.NextInt();
                    break;

                case "pt":
                    points = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                case "p":
                    position = AnimatablePathValueParser.ParseSplitPath(reader, composition);
                    break;

                case "r":
                    rotation = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                case "or":
                    outerRadius = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "os":
                    outerRoundedness = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                case "ir":
                    innerRadius = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "is":
                    innerRoundedness = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new PolystarShape(name, type, points, position, rotation, innerRadius, outerRadius, innerRoundedness, outerRoundedness));
        }
Example #3
0
        internal static ShapePath Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            var    ind  = 0;
            AnimatableShapeValue shape = null;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "ind":
                    ind = reader.NextInt();
                    break;

                case "ks":
                    shape = AnimatableValueParser.ParseShapeData(reader, composition);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new ShapePath(name, ind, shape));
        }
        internal static Repeater Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            AnimatableFloatValue copies    = null;
            AnimatableFloatValue offset    = null;
            AnimatableTransform  transform = null;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "c":
                    copies = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "o":
                    offset = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "tr":
                    transform = AnimatableTransformParser.Parse(reader, composition);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new Repeater(name, copies, offset, transform));
        }
Example #5
0
        /// <summary>
        /// Returns either an <see cref="AnimatablePathValue"/> or an <see cref="AnimatableSplitDimensionPathValue"/>.
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="composition"></param>
        /// <returns></returns>
        internal static IAnimatableValue <Vector2?, Vector2?> ParseSplitPath(JsonReader reader, LottieComposition composition)
        {
            AnimatablePathValue  pathAnimation = null;
            AnimatableFloatValue xAnimation    = null;
            AnimatableFloatValue yAnimation    = null;

            var hasExpressions = false;

            reader.BeginObject();
            while (reader.Peek() != JsonToken.EndObject)
            {
                switch (reader.NextName())
                {
                case "k":
                    pathAnimation = Parse(reader, composition);
                    break;

                case "x":
                    if (reader.Peek() == JsonToken.String)
                    {
                        hasExpressions = true;
                        reader.SkipValue();
                    }
                    else
                    {
                        xAnimation = AnimatableValueParser.ParseFloat(reader, composition);
                    }
                    break;

                case "y":
                    if (reader.Peek() == JsonToken.String)
                    {
                        hasExpressions = true;
                        reader.SkipValue();
                    }
                    else
                    {
                        yAnimation = AnimatableValueParser.ParseFloat(reader, composition);
                    }
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }
            reader.EndObject();

            if (hasExpressions)
            {
                composition.AddWarning("Lottie doesn't support expressions.");
            }

            if (pathAnimation != null)
            {
                return(pathAnimation);
            }
            return(new AnimatableSplitDimensionPathValue(xAnimation, yAnimation));
        }
Example #6
0
        internal static Mask Parse(JsonReader reader, LottieComposition composition)
        {
            var maskMode = Mask.MaskMode.MaskModeAdd;
            AnimatableShapeValue   maskPath = null;
            AnimatableIntegerValue opacity  = null;

            reader.BeginObject();
            while (reader.HasNext())
            {
                var mode = reader.NextName();
                switch (mode)
                {
                case "mode":
                    switch (reader.NextString())
                    {
                    case "a":
                        maskMode = Mask.MaskMode.MaskModeAdd;
                        break;

                    case "s":
                        maskMode = Mask.MaskMode.MaskModeSubtract;
                        break;

                    case "i":
                        composition.AddWarning(
                            "Animation contains intersect masks. They are not supported but will be treated like add masks.");
                        maskMode = Mask.MaskMode.MaskModeIntersect;
                        break;

                    default:
                        Debug.WriteLine($"Unknown mask mode {mode}. Defaulting to Add.", LottieLog.Tag);
                        maskMode = Mask.MaskMode.MaskModeAdd;
                        break;
                    }

                    break;

                case "pt":
                    maskPath = AnimatableValueParser.ParseShapeData(reader, composition);
                    break;

                case "o":
                    opacity = AnimatableValueParser.ParseInteger(reader, composition);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            reader.EndObject();

            return(new Mask(maskMode, maskPath, opacity));
        }
Example #7
0
        internal static ShapeFill Parse(JsonReader reader, LottieComposition composition)
        {
            AnimatableColorValue color     = null;
            var fillEnabled                = false;
            AnimatableIntegerValue opacity = null;
            string name        = null;
            var    fillTypeInt = 1;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "c":
                    color = AnimatableValueParser.ParseColor(reader, composition);
                    break;

                case "o":
                    opacity = AnimatableValueParser.ParseInteger(reader, composition);
                    break;

                case "fillEnabled":
                    fillEnabled = reader.NextBoolean();
                    break;

                case "r":
                    fillTypeInt = reader.NextInt();
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            var fillType = fillTypeInt == 1 ? PathFillType.Winding : PathFillType.EvenOdd;

            return(new ShapeFill(name, fillEnabled, fillType, color, opacity));
        }
        internal static ShapeTrimPath Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            var    type = ShapeTrimPath.Type.Simultaneously;
            AnimatableFloatValue start  = null;
            AnimatableFloatValue end    = null;
            AnimatableFloatValue offset = null;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "s":
                    start = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "e":
                    end = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "o":
                    offset = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "nm":
                    name = reader.NextString();
                    break;

                case "m":
                    type = (ShapeTrimPath.Type)reader.NextInt();
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new ShapeTrimPath(name, type, start, end, offset));
        }
        private static AnimatableTextProperties ParseAnimatableTextProperties(JsonReader reader,
                                                                              LottieComposition composition)
        {
            AnimatableColorValue color       = null;
            AnimatableColorValue stroke      = null;
            AnimatableFloatValue strokeWidth = null;
            AnimatableFloatValue tracking    = null;

            reader.BeginObject();
            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "fc":
                    color = AnimatableValueParser.ParseColor(reader, composition);
                    break;

                case "sc":
                    stroke = AnimatableValueParser.ParseColor(reader, composition);
                    break;

                case "sw":
                    strokeWidth = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "t":
                    tracking = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            reader.EndObject();

            return(new AnimatableTextProperties(color, stroke, strokeWidth, tracking));
        }
        internal static CircleShape Parse(JsonReader reader, LottieComposition composition, int d)
        {
            string name = null;
            IAnimatableValue <Vector?, Vector?> position = null;
            AnimatablePointValue size = null;
            var reversed = d == 3;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "p":
                    position = AnimatablePathValueParser.ParseSplitPath(reader, composition);
                    break;

                case "s":
                    size = AnimatableValueParser.ParsePoint(reader, composition);
                    break;

                case "d":
                    // "d" is 2 for normal and 3 for reversed.
                    reversed = reader.NextInt() == 3;
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new CircleShape(name, position, size, reversed));
        }
Example #11
0
        internal static GradientStroke Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            AnimatableGradientColorValue color   = null;
            AnimatableIntegerValue       opacity = null;
            var gradientType = GradientType.Linear;
            AnimatablePointValue startPoint = null;
            AnimatablePointValue endPoint   = null;
            AnimatableFloatValue width      = null;
            var capType  = ShapeStroke.LineCapType.Unknown;
            var joinType = ShapeStroke.LineJoinType.Round;
            AnimatableFloatValue offset = null;
            var miterLimit = 0f;

            var lineDashPattern = new List <AnimatableFloatValue>();

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "g":
                    var points = -1;
                    reader.BeginObject();
                    while (reader.HasNext())
                    {
                        switch (reader.NextName())
                        {
                        case "p":
                            points = reader.NextInt();
                            break;

                        case "k":
                            color = AnimatableValueParser.ParseGradientColor(reader, composition, points);
                            break;

                        default:
                            reader.SkipValue();
                            break;
                        }
                    }
                    reader.EndObject();
                    break;

                case "o":
                    opacity = AnimatableValueParser.ParseInteger(reader, composition);
                    break;

                case "t":
                    gradientType = reader.NextInt() == 1 ? GradientType.Linear : GradientType.Radial;
                    break;

                case "s":
                    startPoint = AnimatableValueParser.ParsePoint(reader, composition);
                    break;

                case "e":
                    endPoint = AnimatableValueParser.ParsePoint(reader, composition);
                    break;

                case "w":
                    width = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "lc":
                    capType = (ShapeStroke.LineCapType)(reader.NextInt() - 1);
                    break;

                case "lj":
                    joinType = (ShapeStroke.LineJoinType)(reader.NextInt() - 1);
                    break;

                case "ml":
                    miterLimit = reader.NextDouble();
                    break;

                case "d":
                    reader.BeginArray();
                    while (reader.HasNext())
                    {
                        String n = null;
                        AnimatableFloatValue val = null;
                        reader.BeginObject();
                        while (reader.HasNext())
                        {
                            switch (reader.NextName())
                            {
                            case "n":
                                n = reader.NextString();
                                break;

                            case "v":
                                val = AnimatableValueParser.ParseFloat(reader, composition);
                                break;

                            default:
                                reader.SkipValue();
                                break;
                            }
                        }
                        reader.EndObject();

                        if (n.Equals("o"))
                        {
                            offset = val;
                        }
                        else if (n.Equals("d") || n.Equals("g"))
                        {
                            lineDashPattern.Add(val);
                        }
                    }
                    reader.EndArray();
                    if (lineDashPattern.Count == 1)
                    {
                        // If there is only 1 value then it is assumed to be equal parts on and off.
                        lineDashPattern.Add(lineDashPattern[0]);
                    }
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new GradientStroke(
                       name, gradientType, color, opacity, startPoint, endPoint, width, capType, joinType,
                       miterLimit, lineDashPattern, offset));
        }
Example #12
0
        public static AnimatableTransform Parse(JsonReader reader, LottieComposition composition)
        {
            AnimatablePathValue anchorPoint = null;
            IAnimatableValue <Vector2?, Vector2?> position = null;
            AnimatableScaleValue   scale        = null;
            AnimatableFloatValue   rotation     = null;
            AnimatableIntegerValue opacity      = null;
            AnimatableFloatValue   startOpacity = null;
            AnimatableFloatValue   endOpacity   = null;

            var isObject = reader.Peek() == JsonToken.StartObject;

            if (isObject)
            {
                reader.BeginObject();
            }
            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "a":
                    reader.BeginObject();
                    while (reader.HasNext())
                    {
                        if (reader.NextName().Equals("k"))
                        {
                            anchorPoint = AnimatablePathValueParser.Parse(reader, composition);
                        }
                        else
                        {
                            reader.SkipValue();
                        }
                    }
                    reader.EndObject();
                    break;

                case "p":
                    position = AnimatablePathValueParser.ParseSplitPath(reader, composition);
                    break;

                case "s":
                    scale = AnimatableValueParser.ParseScale(reader, composition);
                    break;

                case "rz":
                    composition.AddWarning("Lottie doesn't support 3D layers.");
                    rotation = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                case "r":
                    rotation = AnimatableValueParser.ParseFloat(reader, composition, false);
                    if (rotation.Keyframes.Count == 0)
                    {
                        rotation.Keyframes.Add(new Keyframe <float?>(composition, 0f, 0f, null, 0f, composition.EndFrame));
                    }
                    else if (rotation.Keyframes[0].StartValue == null)
                    {
                        rotation.Keyframes[0] = new Keyframe <float?>(composition, 0f, 0f, null, 0f, composition.EndFrame);
                    }
                    break;

                case "o":
                    opacity = AnimatableValueParser.ParseInteger(reader, composition);
                    break;

                case "so":
                    startOpacity = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                case "eo":
                    endOpacity = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }
            if (isObject)
            {
                reader.EndObject();
            }

            if (anchorPoint == null)
            {
                // Cameras don't have an anchor point property. Although we don't support them, at least
                // we won't crash.
                Debug.WriteLine("Layer has no transform property. You may be using an unsupported layer type such as a camera.", LottieLog.Tag);
                anchorPoint = new AnimatablePathValue();
            }

            if (scale == null)
            {
                // Somehow some community animations don't have scale in the transform.
                scale = new AnimatableScaleValue(new ScaleXy(1f, 1f));
            }

            if (opacity == null)
            {
                // Repeaters have start/end opacity instead of opacity
                opacity = new AnimatableIntegerValue();
            }

            return(new AnimatableTransform(
                       anchorPoint, position, scale, rotation, opacity, startOpacity, endOpacity));
        }
        internal static GradientFill Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            AnimatableGradientColorValue color   = null;
            AnimatableIntegerValue       opacity = null;
            var gradientType = GradientType.Linear;
            AnimatablePointValue startPoint = null;
            AnimatablePointValue endPoint   = null;
            var fillType = PathFillType.EvenOdd;
            AnimatableFloatValue highlightAngle  = null;
            AnimatableFloatValue highlightLength = null;

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "g":
                    var points = -1;
                    reader.BeginObject();
                    while (reader.HasNext())
                    {
                        switch (reader.NextName())
                        {
                        case "p":
                            points = reader.NextInt();
                            break;

                        case "k":
                            color = AnimatableValueParser.ParseGradientColor(reader, composition, points);
                            break;

                        default:
                            reader.SkipValue();
                            break;
                        }
                    }

                    reader.EndObject();
                    break;

                case "o":
                    opacity = AnimatableValueParser.ParseInteger(reader, composition);
                    break;

                case "t":
                    gradientType = reader.NextInt() == 1 ? GradientType.Linear : GradientType.Radial;
                    break;

                case "s":
                    startPoint = AnimatableValueParser.ParsePoint(reader, composition);
                    break;

                case "e":
                    endPoint = AnimatableValueParser.ParsePoint(reader, composition);
                    break;

                case "r":
                    fillType = reader.NextInt() == 1 ? PathFillType.Winding : PathFillType.EvenOdd;
                    break;

                case "h":
                    highlightLength = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "a":
                    highlightAngle = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new GradientFill(
                       name, gradientType, fillType, color, opacity, startPoint, endPoint, highlightLength, highlightAngle));
        }
Example #14
0
        internal static ShapeStroke Parse(JsonReader reader, LottieComposition composition)
        {
            string name = null;
            AnimatableColorValue   color   = null;
            AnimatableFloatValue   width   = null;
            AnimatableIntegerValue opacity = null;
            var capType  = ShapeStroke.LineCapType.Unknown;
            var joinType = ShapeStroke.LineJoinType.Round;
            AnimatableFloatValue offset = null;
            var miterLimit = 0f;

            var lineDashPattern = new List <AnimatableFloatValue>();

            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    name = reader.NextString();
                    break;

                case "c":
                    color = AnimatableValueParser.ParseColor(reader, composition);
                    break;

                case "w":
                    width = AnimatableValueParser.ParseFloat(reader, composition);
                    break;

                case "o":
                    opacity = AnimatableValueParser.ParseInteger(reader, composition);
                    break;

                case "lc":
                    capType = (ShapeStroke.LineCapType)(reader.NextInt() - 1);
                    break;

                case "lj":
                    joinType = (ShapeStroke.LineJoinType)(reader.NextInt() - 1);
                    break;

                case "ml":
                    miterLimit = reader.NextDouble();
                    break;

                case "d":
                    reader.BeginArray();
                    while (reader.HasNext())
                    {
                        String n = null;
                        AnimatableFloatValue val = null;

                        reader.BeginObject();
                        while (reader.HasNext())
                        {
                            switch (reader.NextName())
                            {
                            case "n":
                                n = reader.NextString();
                                break;

                            case "v":
                                val = AnimatableValueParser.ParseFloat(reader, composition);
                                break;

                            default:
                                reader.SkipValue();
                                break;
                            }
                        }
                        reader.EndObject();

                        switch (n)
                        {
                        case "o":
                            offset = val;
                            break;

                        case "d":
                        case "g":
                            lineDashPattern.Add(val);
                            break;
                        }
                    }
                    reader.EndArray();

                    if (lineDashPattern.Count == 1)
                    {
                        // If there is only 1 value then it is assumed to be equal parts on and off.
                        lineDashPattern.Add(lineDashPattern[0]);
                    }
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }

            return(new ShapeStroke(name, offset, lineDashPattern, color, opacity, width, capType, joinType, miterLimit));
        }
Example #15
0
        public static Layer Parse(JsonReader reader, LottieComposition composition)
        {
            string layerName     = null;
            var    layerType     = Layer.LayerType.Unknown;
            string refId         = null;
            long   layerId       = 0;
            var    solidWidth    = 0;
            var    solidHeight   = 0;
            var    solidColor    = Colors.Transparent;
            var    preCompWidth  = 0;
            var    preCompHeight = 0;
            long   parentId      = -1;
            var    timeStretch   = 1f;
            var    startFrame    = 0f;
            var    inFrame       = 0f;
            var    outFrame      = 0f;
            string cl            = null;
            var    hidden        = false;

            var matteType = Layer.MatteType.None;
            AnimatableTransform      transform      = null;
            AnimatableTextFrame      text           = null;
            AnimatableTextProperties textProperties = null;
            AnimatableFloatValue     timeRemapping  = null;

            var masks  = new List <Mask>();
            var shapes = new List <IContentModel>();

            reader.BeginObject();
            while (reader.HasNext())
            {
                switch (reader.NextName())
                {
                case "nm":
                    layerName = reader.NextString();
                    break;

                case "ind":
                    layerId = reader.NextInt();
                    break;

                case "refId":
                    refId = reader.NextString();
                    break;

                case "ty":
                    var layerTypeInt = reader.NextInt();
                    if (layerTypeInt < (int)Layer.LayerType.Unknown)
                    {
                        layerType = (Layer.LayerType)layerTypeInt;
                    }
                    else
                    {
                        layerType = Layer.LayerType.Unknown;
                    }
                    break;

                case "parent":
                    parentId = reader.NextInt();
                    break;

                case "sw":
                    solidWidth = (int)(reader.NextInt() * Utils.Utils.DpScale());
                    break;

                case "sh":
                    solidHeight = (int)(reader.NextInt() * Utils.Utils.DpScale());
                    break;

                case "sc":
                    solidColor = Utils.Utils.GetSolidColorBrush(reader.NextString());
                    break;

                case "ks":
                    transform = AnimatableTransformParser.Parse(reader, composition);
                    break;

                case "tt":
                    matteType = (Layer.MatteType)reader.NextInt();
                    break;

                case "masksProperties":
                    reader.BeginArray();
                    while (reader.HasNext())
                    {
                        masks.Add(MaskParser.Parse(reader, composition));
                    }
                    reader.EndArray();
                    break;

                case "shapes":
                    reader.BeginArray();
                    while (reader.HasNext())
                    {
                        var shape = ContentModelParser.Parse(reader, composition);
                        if (shape != null)
                        {
                            shapes.Add(shape);
                        }
                    }
                    reader.EndArray();
                    break;

                case "t":
                    reader.BeginObject();
                    while (reader.HasNext())
                    {
                        switch (reader.NextName())
                        {
                        case "d":
                            text = AnimatableValueParser.ParseDocumentData(reader, composition);
                            break;

                        case "a":
                            reader.BeginArray();
                            if (reader.HasNext())
                            {
                                textProperties = AnimatableTextPropertiesParser.Parse(reader, composition);
                            }
                            while (reader.HasNext())
                            {
                                reader.SkipValue();
                            }
                            reader.EndArray();
                            break;

                        default:
                            reader.SkipValue();
                            break;
                        }
                    }
                    reader.EndObject();
                    break;

                case "ef":
                    reader.BeginArray();
                    var effectNames = new List <string>();
                    while (reader.HasNext())
                    {
                        reader.BeginObject();
                        while (reader.HasNext())
                        {
                            switch (reader.NextName())
                            {
                            case "nm":
                                effectNames.Add(reader.NextString());
                                break;

                            default:
                                reader.SkipValue();
                                break;
                            }
                        }
                        reader.EndObject();
                    }
                    reader.EndArray();
                    composition.AddWarning("Lottie doesn't support layer effects. If you are using them for " +
                                           " fills, strokes, trim paths etc. then try adding them directly as contents " +
                                           " in your shape. Found: " + effectNames);
                    break;

                case "sr":
                    timeStretch = reader.NextDouble();
                    break;

                case "st":
                    startFrame = reader.NextDouble();
                    break;

                case "w":
                    preCompWidth = (int)(reader.NextInt() * Utils.Utils.DpScale());
                    break;

                case "h":
                    preCompHeight = (int)(reader.NextInt() * Utils.Utils.DpScale());
                    break;

                case "ip":
                    inFrame = reader.NextDouble();
                    break;

                case "op":
                    outFrame = reader.NextDouble();
                    break;

                case "tm":
                    timeRemapping = AnimatableValueParser.ParseFloat(reader, composition, false);
                    break;

                case "cl":
                    cl = reader.NextString();
                    break;

                case "hd":
                    hidden = reader.NextBoolean();
                    break;

                default:
                    reader.SkipValue();
                    break;
                }
            }
            reader.EndObject();

            // Bodymovin pre-scales the in frame and out frame by the time stretch. However, that will
            // cause the stretch to be double counted since the in out animation gets treated the same
            // as all other animations and will have stretch applied to it again.
            inFrame  /= timeStretch;
            outFrame /= timeStretch;

            var inOutKeyframes = new List <Keyframe <float?> >();

            // Before the in frame
            if (inFrame > 0)
            {
                var preKeyframe = new Keyframe <float?>(composition, 0f, 0f, null, 0f, inFrame);
                inOutKeyframes.Add(preKeyframe);
            }

            // The + 1 is because the animation should be visible on the out frame itself.
            outFrame = (outFrame > 0 ? outFrame : composition.EndFrame);
            var visibleKeyframe = new Keyframe <float?>(composition, 1f, 1f, null, inFrame, outFrame);

            inOutKeyframes.Add(visibleKeyframe);

            var outKeyframe = new Keyframe <float?>(composition, 0f, 0f, null, outFrame, float.MaxValue);

            inOutKeyframes.Add(outKeyframe);

            if (layerName.EndsWith(".ai") || "ai".Equals(cl))
            {
                composition.AddWarning("Convert your Illustrator layers to shape layers.");
            }

            return(new Layer(shapes, composition, layerName, layerId, layerType, parentId, refId, masks, transform, solidWidth, solidHeight, solidColor, timeStretch, startFrame, preCompWidth, preCompHeight, text, textProperties, inOutKeyframes, matteType, timeRemapping, hidden));
        }