catalogue
- Animation and drawing process
- LayerView tree
- ShapeLayer analysis
- Advantages and disadvantages of Lottie and introduction of rLottie and PAG
- data
- harvest
In the last article, we learned to analyze The json parsing part of Lottie This is the animation and rendering part of our analysis.
Key points of analysis: how to organize the relationship between multi-layer layers and control the drawing and animation of different layers.
1, Animation and drawing process
We analyze through the entry API functions (LottieDrawable#setComposition, LottieDrawable#playAnimation).
1.1 LottieDrawable#setComposition process
public boolean setComposition(LottieComposition composition) { //...... clearComposition(); this.composition = composition; //The function of constructing a layer composition layer is a bit like the ViewGroup in the Android View tree, which can contain other views and viewgroups //To complete the initialization of CompositionLayer and ContentGroup, there are two main functions: TransformKeyframeAnimation buildCompositionLayer(); //Trigger notifyUpdate, and then trigger the recalculation of the progress of layers and the callback of draw (of course, the progress is 0 at this time, and the drawlayer of composition will not be triggered after various judgments) animator.setComposition(composition); //Sets the progress of the current animation setProgress(animator.getAnimatedFraction()); ...... }
You can see that setComposition mainly calls buildCompositionLayer and animator setComposition to initialize CompositionLayer and other layers (corresponding layers field in json), ContentGroup, TransformKeyframeAnimation, etc. The most used layers in Lottie animation are CompositionLayer, ShapeLayer, and ImageLayer.
Think: so what are content group, TransformKeyframeAnimation, and their relationship with layer? (we will try to analyze and answer later)
1.2 LottieDrawable#playAnimation process
1. LottieDrawable.playAnimation 2. LottieValueAnimator.playAnimation 3. LottieValueAnimator.setFrame 4. BaseLottieAnimator.notifyUpdate 5.The callback is then triggered(LottieDrawable.progressUpdateListener)AnimatorUpdateListener.onAnimationUpdate 6. CompositionLayer.setProgress -->Calculate current progress,Then set the progress of each layer in reverse order BaseLayer.setProgress 6.1(transform.setProgress(progress))TransformKeyframeAnimation.setProgress Set the progress of the matrix transformation (scaling, transparency, displacement, etc.)-->Need to focus on analysis 6.2 animations.get(i).setProgress(progress); Traverse settings for each animation Progress of 7. BaseKeyframeAnimation.notifyListeners Callback to listener 8. BaseLayer.onValueChanged (invalidateSelf())Trigger page redrawing,-->Namely LottieDrawable.draw(android.graphics.Canvas, android.graphics.Matrix) 9. compositionLayer.draw(canvas, matrix, alpha) Namely BaseLayer.draw -->This is also a key method 10. drawLayer(canvas, matrix, alpha); Namely BaseLayer.drawLayer This method is an abstract method layer Concrete implementation 10.1 We take ImageLayer As an example (key analysis) ImageLayer.drawLayer First pass BaseKeyframeAnimation.getValue() This uses the previous animation changes progress The current value is obtained according to the differentiator Bitmap 10.2 Then use canvas To draw and complete the transformation of the picture
LottieValueAnimator is a subclass of ValueAnimator and implements choreographer Framecallback interface. Through the progress transformation callback of attribute animation and the doframe callback of VSYNC signal, Layer is notified to calculate the progress and value, and LottieDrawble is notified to redraw, so as to realize the animation and drawing of layers in json, that is, various layers.
The specific drawing is still realized by Canvas, which can be realized through the drawLayer of ImageLayer
public void drawLayer(@NonNull Canvas canvas, Matrix parentMatrix, int parentAlpha) { Bitmap bitmap = getBitmap(); if (bitmap == null || bitmap.isRecycled()) { return; } float density = Utils.dpScale(); paint.setAlpha(parentAlpha); if (colorFilterAnimation != null) { paint.setColorFilter(colorFilterAnimation.getValue()); } //Save the current state of the canvas canvas.save(); //The transformation of the matrix is applied to all objects on the canvas canvas.concat(parentMatrix); //src is used to set the area to draw the bitmap, that is, whether to crop or not src.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); //dst is used to set the display area on the canvas. Here you can see that the width and height of the display will be scaled according to the pixel density dst.set(0, 0, (int) (bitmap.getWidth() * density), (int) (bitmap.getHeight() * density)); //The first Rect(src) represents the bitmap area to be drawn. You can cut the picture. If it is empty, the whole picture will be displayed. The second Rect(dst) is the area where the picture is displayed on the Canvas, that is, where to draw the bitmap on the screen // By dynamically changing the dst, you can achieve the effects of moving and scaling, and scaling according to the pixel density of the screen. By changing the src to deal with the needs of the drawn pictures, you can also achieve many interesting effects, such as displaying a part, or gradually expanding, etc canvas.drawBitmap(bitmap, src, dst, paint); //Restore the previously saved canvas state, corresponding to sava one-to-one canvas.restore(); }
ShapeLayer and CompositionLayer are somewhat complex. We will analyze them separately below.
Thinking: if there are multiple layers, how to ensure the correlation between multiple layers (like ViewTree, how to manage the relationship between them and the drawing order).
2, LayerView tree
There are various layers in Lottie:

So what is the relationship between them? How to carry out management and hierarchical control?
Construction of CompositionLayer
public CompositionLayer(LottieDrawable lottieDrawable, Layer layerModel, List<Layer> layerModels, LottieComposition composition) { //It is mainly the initialization of TransformKeyframeAnimation super(lottieDrawable, layerModel); LongSparseArray<BaseLayer> layerMap = new LongSparseArray<>(composition.getLayers().size()); BaseLayer mattedLayer = null; //Each Layer is produced in reverse order according to the size of layers for (int i = layerModels.size() - 1; i >= 0; i--) { Layer lm = layerModels.get(i); //This is an engineering method, which constructs the corresponding Layer according to the layerType BaseLayer layer = BaseLayer.forModel(this, lm, lottieDrawable, composition); if (layer == null) { continue; } layerMap.put(layer.getLayerModel().getId(), layer); ...... } for (int i = 0; i < layerMap.size(); i++) { long key = layerMap.keyAt(i); BaseLayer layerView = layerMap.get(key); if (layerView == null) { continue; } // Determine the parent-child relationship between layer s BaseLayer parentLayer = layerMap.get(layerView.getLayerModel().getParentId()); if (parentLayer != null) { layerView.setParentLayer(parentLayer); } } }
Factory method: BaseLayer#forModel
static BaseLayer forModel( CompositionLayer compositionLayer, Layer layerModel, LottieDrawable drawable, LottieComposition composition) { //Corresponding to object - > layers - > ty in json switch (layerModel.getLayerType()) { //Contour / shape layer is basically the most used type in lottie animation case SHAPE: return new ShapeLayer(drawable, layerModel, compositionLayer); //Composite layer, equivalent to the role of ViewGroup of ViewTree case PRE_COMP: return new CompositionLayer(drawable, layerModel, composition.getPrecomps(layerModel.getRefId()), composition); //Fill Layer case SOLID: return new SolidLayer(drawable, layerModel); //Picture layers are also commonly used, especially when making some template effects case IMAGE: return new ImageLayer(drawable, layerModel); //An empty layer can be used as a parent of other layers case NULL: return new NullLayer(drawable, layerModel); //Text layer case TEXT: return new TextLayer(drawable, layerModel); case UNKNOWN: default: // Do nothing Logger.warning("Unknown layer type " + layerModel.getLayerType()); return null; } }
We see layerview setParentLayer(ParentLayer); So what's the use of this ParentLayer? It is mainly used when determining the boundary and drawing of each layer
// BaseLayer#buildParentLayerListIfNeeded //This method will call draw when determining the current layer boundary getboundaries and drawing the layer private void buildParentLayerListIfNeeded() { if (parentLayers != null) { return; } //If the layer has a parent layer, it will be updated if (parentLayer == null) { parentLayers = Collections.emptyList(); return; } //The LayerViewTree of the layer parentLayers = new ArrayList<>(); BaseLayer layer = parentLayer; //Recursively find the parent layer, grandfather layer, great grandfather layer, and so on while (layer != null) { parentLayers.add(layer); layer = layer.parentLayer; } }
BaseLayer#getBounds
public void getBounds( RectF outBounds, Matrix parentMatrix, boolean applyParents) { rect.set(0, 0, 0, 0); //Determine the LayerViewTree: parentLayers of the layer buildParentLayerListIfNeeded(); //The matrix transformation of the sub layer is based on the matrix transformation of the parent layer boundsMatrix.set(parentMatrix); if (applyParents) { //Recursively call the parent layer amount matrix transformation to multiply the matrix if (parentLayers != null) { for (int i = parentLayers.size() - 1; i >= 0; i--) { boundsMatrix.preConcat(parentLayers.get(i).transform.getMatrix()); } } else if (parentLayer != null) { boundsMatrix.preConcat(parentLayer.transform.getMatrix()); } } //Finally, it is multiplied by the matrix transformation of the current layer to determine the final boundary matrix boundsMatrix.preConcat(transform.getMatrix()); }
BaseLayer#draw The matrix processing method is the same as baselayer #getboundaries.
Establish the LayerViewTree of the layer through parentid, and then determine its own bound and draw according to the LayerView during measurement and drawing.
3, ShapeLayer analysis
The reason why ShapeLayer is brought out alone is that it is very important in lottie animation ShapeLayer is a subclass of layers drawn by vector graphics rather than bitmap. Specify properties such as color and lineweight, and use Path to define the drawing
public class ShapeLayer extends BaseLayer { ...... //What is this contentgroup? You can see that both drawLayer and getBound of ShapeLayer are represented by contentgroup. private final ContentGroup contentGroup; ShapeLayer(LottieDrawable lottieDrawable, Layer layerModel, CompositionLayer compositionLayer) { ...... //ContentGroup construct contentGroup = new ContentGroup(lottieDrawable, this, shapeGroup); contentGroup.setContents(Collections.<Content>emptyList(), Collections.<Content>emptyList()); } @Override void drawLayer(@NonNull Canvas canvas, Matrix parentMatrix, int parentAlpha) { //The draw of contentGroup was called contentGroup.draw(canvas, parentMatrix, parentAlpha); } @Override public void getBounds(RectF outBounds, Matrix parentMatrix, boolean applyParents) { ...... contentGroup.getBounds(outBounds, boundsMatrix, applyParents); } ...... }
What is ContentGroup? You can see that both drawLayer and getBound of ShapeLayer are represented by contentGroup. Let's take a look at the implementation of draw of ContentGroup
public void draw(Canvas canvas, Matrix parentMatrix, int parentAlpha){ //Traverse and call content. If it is DrawingContent, draw. What is DrawingContent over there for (int i = contents.size() - 1; i >= 0; i--) { Object content = contents.get(i); if (content instanceof DrawingContent) { ((DrawingContent) content).draw(canvas, matrix, childAlpha); } } }
Traverse and call the content. If it is DrawingContent, draw. Which content is DrawingContent?

Let's take FillContent as an example to see the implementation of its draw
public void draw(Canvas canvas, Matrix parentMatrix, int parentAlpha) { ...... //Get color, transparency, etc. set the color of the brush paint int color = ((ColorKeyframeAnimation) this.colorAnimation).getIntValue(); int alpha = (int) ((parentAlpha / 255f * opacityAnimation.getValue() / 100f) * 255); paint.setColor((clamp(alpha, 0, 255) << 24) | (color & 0xFFFFFF)); //Set colorFilter if (colorFilterAnimation != null) { paint.setColorFilter(colorFilterAnimation.getValue()); } ...... //Set path path path.reset(); for (int i = 0; i < paths.size(); i++) { path.addPath(paths.get(i).getPath(), parentMatrix); } //Using cavas drawpath canvas.drawPath(path, paint); }
You can draw ShapeContent's DrawingContent through Canvas.
That's all for the animation and rendering analysis part of Lottie. BaseKeyframeAnimation mainly implements the interpolation calculation of animation in Layer and DrawingContent. There is no detailed analysis. You need to see it again.
Thinking: can you render through OpenGL ES?
5, Advantages and disadvantages of Lottie and simple comparison with PAG
Advantages and disadvantages of Lottie
advantage: Support cross platform (although each end implements one set) Good performance It can be distributed through configuration“ json And material. Insufficient points: Lottie Interaction and editing are not supported Lottie Compressed bitmaps are not supported if used png Wait for bitmap, you need to tiny And other compression platforms to compress pictures and reduce package volume. Lottie existence mask,matters When, you need to first saveLayer,Re call drawLayer return. saveLayer It is a time-consuming operation. You need to allocate and draw one first offscreen This increases the rendering time
A brief introduction to the advantages and disadvantages of PAG
PAG It is an animation component just opened by Tencent yesterday, except lottie In addition to the advantages of, Support more AE Special effects, Support text and sequence frames, Supports template editing, Use secondary value files instead of json,File size and parsing performance will be better Render level: Lottie The implementation of rendering layer depends on the platform side interface, which may vary from platform to platform. PAG Render layer usage C++Implementation, all platforms share the same set of implementation, and the platform side only encapsulates interface calls, provides a rendering environment, and the rendering effect is consistent. PAG Rendering based on google GPl skia 2d To achieve. Increased package size. four.0 The version of will be improved and removed skia 2d. Realize simple rendering encapsulation by yourself (it is estimated that opengl perhaps metal ,vulkan).
Brief introduction to rlottie
[Samsung-rlottie](https://github.com/Samsung/rlottie) rLottie And lottie Workflow consistency, in SDK The implementation is different, rLottie No platform specific implementation is used, which is unified C++realization,Material support lottie of json File, vector rendering performance is good, but it lacks encapsulation of various platforms and supports AE Features are not complete, and text, sequence frames, etc. are not supported This has not analyzed its source code implementation. Take time to analyze and study.
6, Information
- Lottie implementation ideas and source code analysis
- Analysis of Lottie animation principle
- Uncover the advantages, disadvantages and principles of Lottie animation
- Lottie Android framework usage and source code analysis
- Analysis of Android source code of Lottie animation library
- Tencent open source PAG
- Samsung-rlottie
- Compare PAG and lottie from the decoding and rendering level
7, Harvest
Through the learning analysis of this article
- Combed the lottie animation and rendering process
- The concept and understanding of LayerView tree, and find out how lottie manages the relationship between different layers
- The analysis focuses on CompositionLayer, BaseLayer, ImageLayer and ShapeLayer, where ShapeLayer contains content group
- lottie, PAG and rlottie were simply compared
Thank you for reading Welcome to the official account of the "audio and video development tour" and learn and grow together. Welcome to communicate