Skip to content

Commit

Permalink
Background loading & RenderCache.drawingCommands (#326)
Browse files Browse the repository at this point in the history
  • Loading branch information
xvrh authored Dec 22, 2023
1 parent 9f4a8d7 commit 08e9678
Show file tree
Hide file tree
Showing 56 changed files with 1,339 additions and 926 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 3.0.0-alpha.4
- Add `backgroundLoading` parameter to `Lottie.asset|network|file|memory`.
If `backgroundLoading` is true, the animation will be loaded in a background isolate.
This is useful for large animations that can take a long time to parse and block the UI work.

- Replace `enableRenderCache` with `renderCache: RenderCache.raster`.
The new enum `RenderCacheMode` allows to specify the cache behaviour:
- `RenderCacheMode.raster`: Cache the frames as rasterized images living in the GPU memory.
- `RenderCacheMode.drawingCommands`: Cache the frames as a list of graphical operations. This will only save CPU
work but will use a lot less memory.

## 3.0.0-alpha.3
- Reduce the max memory used when using `enableRenderCache` (now limited to 50MB)
- Allow to configure the memory with a global settings:
Expand Down Expand Up @@ -66,7 +77,7 @@ Future<LottieComposition?> decoder(List<int> bytes) {
return LottieComposition.decodeZip(bytes, imageProviderFactory: imageProviderFactory);
}
Lottie.asset('anim.json', imageProviderFactory: imageProviderFactory, decoder: decoder)
Lottie.asset('anim.json', decoder: decoder)
```
- Disable gradient cache optimization when `ValueDelegate.gradientColor` is used
- Use `DefaultAssetBundle.of` in `AssetLottie` before fallback to `rootBundle`
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,10 @@ Future<LottieComposition?> customDecoder(List<int> bytes) {

## Performance or excessive CPU/GPU usage

Version `v3.0` introduced the `enableRenderCache` parameter to help reduce an excessive energy consumption.
Version `v3.0` introduced the `renderCache` parameter to help reduce an excessive energy consumption.

In this mode, the frames of the animation are rendered lazily in an offscreen cache. Subsequent runs of the animation
are very cheap to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
are cheaper to render. It helps reduce the power usage of the application at the cost of an increased memory usage.

## Limitations
This port supports the same [feature set as Lottie Android](https://airbnb.io/lottie/#/supported-features).
Expand Down
4 changes: 2 additions & 2 deletions README.template.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ import 'example/lib/examples/dotlottie.dart#example';

## Performance or excessive CPU/GPU usage

Version `v3.0` introduced the `enableRenderCache` parameter to help reduce an excessive energy consumption.
Version `v3.0` introduced the `renderCache` parameter to help reduce an excessive energy consumption.

In this mode, the frames of the animation are rendered lazily in an offscreen cache. Subsequent runs of the animation
are very cheap to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
are cheaper to render. It helps reduce the power usage of the application at the cost of an increased memory usage.

## Limitations
This port supports the same [feature set as Lottie Android](https://airbnb.io/lottie/#/supported-features).
Expand Down
1 change: 0 additions & 1 deletion example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
/build/

# Web related
lib/generated_plugin_registrant.dart

# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
1 change: 1 addition & 0 deletions example/assets/Animation-1700642783167.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"v":"5.12.1","fr":25,"ip":0,"op":50,"w":1080,"h":2330,"nm":"Testcomp","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Top Shape With Multiply","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,1165,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[608,608],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.996078012504,0.996078012504,0.996078012504,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.108819677316,0.687484142827,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4,-261],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":825,"st":0,"ct":1,"bm":1},{"ddd":0,"ind":2,"ty":4,"nm":"Shape With Outline","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-45,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.25,"y":1},"o":{"x":0.75,"y":0},"t":0,"s":[524,1208,0],"to":[0,39.333,0],"ti":[0,0,0]},{"i":{"x":0.25,"y":1},"o":{"x":0.75,"y":0},"t":25,"s":[524,1444,0],"to":[0,0,0],"ti":[0,39.333,0]},{"t":49,"s":[524,1208,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-74,327,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":24,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.109803929048,0.686274509804,0.956862804936,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-74,327],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":825,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"BKG","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,1165,0],"ix":2,"l":2},"a":{"a":0,"k":[540,1165,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":1080,"sh":2330,"sc":"#ffffff","ip":0,"op":825,"st":0,"bm":0}],"markers":[],"props":{}}
226 changes: 81 additions & 145 deletions example/lib/examples/render_cache.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
// ignore: implementation_imports
import 'package:lottie/src/render_cache.dart';

void main() {
globalRenderCache.enableDebugBackground = true;
runApp(const App());
}

Expand Down Expand Up @@ -45,133 +43,77 @@ class App extends StatelessWidget {
}
}

class _Example extends StatefulWidget {
static String _text(a) => '';

@override
State<_Example> createState() => _ExampleState();
}

class _ExampleState extends State<_Example> {
int _animationCount = 1;

class _Example extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: [
ElevatedButton(
onPressed: () {
setState(() {
++_animationCount;
});
_Row(
builder: (cache) {
return Lottie.asset('assets/Mobilo/Z.json',
renderCache: cache, height: 100);
},
child: Text('Add animation $_animationCount'),
),
Row(
children: [
Expanded(
child: Stack(
children: [
for (var i = 0; i < _animationCount; i++)
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/B.json',
height: 200,
frameRate: const FrameRate(60),
enableRenderCache: true,
fit: BoxFit.cover,
delegates: LottieDelegates(
text: _Example._text,
values: [
ValueDelegate.color(['*'], value: Color(i)),
],
),
),
),
],
),
),
Expanded(
child: Lottie.asset(
'assets/Mobilo/B.json',
height: 200,
frameRate: const FrameRate(10),
fit: BoxFit.cover,
),
),
],
),
Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
for (var fit in [BoxFit.cover, BoxFit.fill, BoxFit.contain])
_Row(
builder: (cache) {
return Lottie.asset(
'assets/lottiefiles/bb8.json',
renderCache: cache,
fit: fit,
height: 60,
);
},
),
child: Lottie.asset('assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
fit: BoxFit.fill,
enableRenderCache: true),
),
Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Transform.scale(
scale: 2,
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
enableRenderCache: true,
frameRate: const FrameRate(10),
fit: BoxFit.fill,
),
),
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Transform.scale(
scale: 0.5,
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
enableRenderCache: true,
fit: BoxFit.fill,
),
),
),
),
),
],
_Row(
builder: (cache) {
return Lottie.asset(
'assets/lottiefiles/a_mountain.json',
renderCache: cache,
height: 40,
);
},
),
for (var align in [
Alignment.bottomCenter,
Alignment.center,
Alignment.topRight
])
_Row(
builder: (cache) {
return Lottie.asset('assets/lottiefiles/bomb.json',
renderCache: cache, height: 40, alignment: align);
},
),
],
);
}
}

class _Row extends StatelessWidget {
final Widget Function(RenderCache? cache) builder;

const _Row({required this.builder});

@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(border: Border.all(color: Colors.green)),
child: Row(
children: [
for (var cache in [
null,
RenderCache.raster,
RenderCache.drawingCommands
])
Expanded(child: builder(cache))
],
),
);
}
}

class RenderCacheDebugPanel extends StatefulWidget {
const RenderCacheDebugPanel({super.key});

Expand All @@ -180,40 +122,34 @@ class RenderCacheDebugPanel extends StatefulWidget {
}

class _RenderCacheDebugPanelState extends State<RenderCacheDebugPanel> {
late Timer _refreshTimer;

@override
void initState() {
super.initState();

_refreshTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
setState(() {
// refresh
});
});
}

@override
Widget build(BuildContext context) {
return StreamBuilder<void>(
stream: globalRenderCache.onUpdate,
builder: (context, snapshot) {
return ListView(
children: [
Text('Images: ${globalRenderCache.imageCount}'),
Text(
'Memory: ${(globalRenderCache.totalMemory / 1000000).toStringAsFixed(1)}MB'),
const Divider(),
ElevatedButton(
onPressed: () {
globalRenderCache.clear();
},
child: const Text('Clear'),
),
const Divider(),
SwitchListTile(
title: const Text('Enable debug background'),
value: globalRenderCache.enableDebugBackground,
onChanged: (v) {
setState(() {
globalRenderCache.enableDebugBackground = v;
});
},
)
],
);
});
return ListView(
children: [
Text('Images: ${RenderCache.raster.store.imageCount}'),
Text(
'Memory: ${(RenderCache.raster.store.totalMemory / 1000000).toStringAsFixed(1)}MB'),
const Divider(),
],
);
}

@override
void dispose() {
_refreshTimer.cancel();
super.dispose();
}
}
3 changes: 2 additions & 1 deletion example/lib/main_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class App extends StatelessWidget {
child: Lottie.asset(
assetName,
fit: BoxFit.contain,
enableRenderCache: true,
renderCache: RenderCache.drawingCommands,
backgroundLoading: false,
onWarning: (w) => _logger.info('$assetName - $w'),
frameBuilder: (context, child, composition) {
return AnimatedOpacity(
Expand Down
2 changes: 2 additions & 0 deletions example/lib/sizing_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
showPerformanceOverlay: true,
home: Scaffold(
appBar: AppBar(
title: const Text(''),
Expand Down Expand Up @@ -136,6 +137,7 @@ class __LottieState extends State<_Lottie> with TickerProviderStateMixin {
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Lottie(
composition: widget.composition,
renderCache: RenderCache.raster,
controller: _controller,
width: widget.width,
height: widget.height,
Expand Down
12 changes: 0 additions & 12 deletions example/macos/Runner/DebugProfile.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,7 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.assets.pictures.read-write</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
15 changes: 1 addition & 14 deletions example/macos/Runner/Release.entitlements
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.assets.pictures.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
<dict/>
</plist>
Loading

0 comments on commit 08e9678

Please sign in to comment.