diff --git a/.ci/coveralls.sh b/.ci/coveralls.sh
new file mode 100755
index 00000000..9dd7e84f
--- /dev/null
+++ b/.ci/coveralls.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -x
+
+if [[ -z $COVERALLS_REPO_TOKEN ]]; then
+ echo "Skipping coveralls, user not authorized"
+ exit 0
+fi
+
+dart pub global activate coveralls
+dart pub global run coveralls coverage/lcov.info
diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index 34c0d628..00000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-container:
- image: cirrusci/flutter:latest
-
-env:
- CI_NAME: "CirrusCI"
- CI_BUILD_NUMBER: $CIRRUS_TASK_ID
- CI_BUILD_URL: "https://cirrus-ci.com/task/$CIRRUS_TASK_ID"
- CI_BRANCH: $CIRRUS_BRANCH
- CI_PULL_REQUEST: $CIRRUS_PR
- COVERALLS_REPO_TOKEN: ENCRYPTED[61ba3fee193a9ed6116e0f61bbe26fe8c0452287e5dfd86c728e2f1f24327818d6c74c956d7b9cbf3bd6236489af0fd1]
-
-test_task:
- matrix:
- - name: stable
- channel_script: |
- flutter channel stable
- flutter upgrade
- - name: master
- channel_script: |
- flutter channel master
- flutter upgrade
- pub_cache:
- folder: ~/.pub-cache
- analyze_script: flutter analyze .
- test_script: flutter test --coverage
- coveralls_script: |
- dart pub global activate coveralls
- dart pub global run coveralls coverage/lcov.info
diff --git a/.github/workflows/flutter_svg.yml b/.github/workflows/flutter_svg.yml
new file mode 100644
index 00000000..4ab36135
--- /dev/null
+++ b/.github/workflows/flutter_svg.yml
@@ -0,0 +1,30 @@
+name: analyze and test
+
+on:
+ push:
+ branches: [master]
+ paths-ignore:
+ - '**/*.md'
+ pull_request:
+ branches: [master]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: packages/flutter_svg
+ steps:
+ - uses: actions/checkout@v3
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ - run: flutter --version
+ - run: flutter pub get
+ - run: dart format --set-exit-if-changed .
+ - run: flutter analyze .
+ - run: flutter test --coverage
+ # - uses: romeovs/lcov-reporter-action@v0.2.16
+ # with:
+ # lcov-file: ./packages/vector_graphics_codec/coverage/lcov.info
+ # github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/flutter_svg_test.yml b/.github/workflows/flutter_svg_test.yml
new file mode 100644
index 00000000..daeea37a
--- /dev/null
+++ b/.github/workflows/flutter_svg_test.yml
@@ -0,0 +1,30 @@
+name: analyze and test flutter_svg_test
+
+on:
+ push:
+ branches: [master]
+ paths-ignore:
+ - '**/*.md'
+ pull_request:
+ branches: [master]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: packages/flutter_svg_test
+ steps:
+ - uses: actions/checkout@v3
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ - run: flutter --version
+ - run: flutter pub get
+ - run: dart format --set-exit-if-changed .
+ - run: flutter analyze .
+ - run: flutter test --coverage
+ # - uses: romeovs/lcov-reporter-action@v0.2.16
+ # with:
+ # lcov-file: ./packages/vector_graphics_codec/coverage/lcov.info
+ # github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 8dd4cc08..a48af087 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,13 +8,12 @@ pubspec.lock
.pub/
build/
ios/.generated/
-packages
.flutter-plugins
doc/api/
coverage/
.project
# golden failure diffs
-test/failures
+**/test/failures
# Flutter crash logs
/flutter_*.log
diff --git a/README.md b/README.md
index 7976d434..44f2cbd4 100644
--- a/README.md
+++ b/README.md
@@ -1,196 +1,33 @@
# flutter_svg
-[](https://pub.dartlang.org/packages/flutter_svg) [](https://travis-ci.org/dnfield/flutter_svg) [](https://coveralls.io/github/dnfield/flutter_svg?branch=master)
+[](https://pub.dartlang.org/packages/flutter_svg) [](https://coveralls.io/github/dnfield/flutter_svg?branch=master)
-Draw SVG (and _some_ Android VectorDrawable (XML)) files on a Flutter Widget.
+Draw SVG files using Flutter.
-## Getting Started
+## Overview
-This is a Dart-native rendering library. Issues/PRs will be raised in Flutter
-and flutter/engine as necessary for features that are not good candidates for
-Dart implementations (especially if they're impossible to implement without
-engine support). However, not everything that Skia can easily do needs to be
-done by Skia; for example, the Path parsing logic here isn't much slower than
-doing it in native, and Skia isn't always doing low level GPU accelerated work
-where you might think it is (e.g. Dash Paths).
+This library consists of two packages: [**flutter_svg**](https://github.com/dnfield/flutter_svg/tree/master/packages/flutter_svg) and [**flutter_svg_test**](https://github.com/dnfield/flutter_svg/tree/master/packages/flutter_svg_test).
-All of the SVGs in the `assets/` folder (except the text related one(s)) now
-have corresponding PNGs in the `golden/` folder that were rendered using
-`flutter test tool/gen_golden.dart` and compared against their rendering output
-in Chrome. Automated tests will continue to compare these to ensure code changes
-do not break known-good renderings.
+[flutter_svg](https://github.com/dnfield/flutter_svg/tree/master/packages/flutter_svg) provides a wrapper around Dart implementations of SVG parsing, including SVG path data. In particular, it provides efficient `BytesLoader` implementations for [`package:vector_graphics`](https://pub.dev/packages/vector_graphics). The package is easier to use but not as performant as using the `vector_graphics` and `vector_graphics_compiler` packages directly. Those packages allow you to do ahead-of-time compilation and optimization of SVGs, and offer some more performant rasterization strategies at runtime.
-Basic usage (to create an SVG rendering widget from an asset):
-
-```dart
-final String assetName = 'assets/image.svg';
-final Widget svg = SvgPicture.asset(
- assetName,
- semanticsLabel: 'Acme Logo'
-);
-```
-
-You can color/tint the image like so:
-
-```dart
-final String assetName = 'assets/up_arrow.svg';
-final Widget svgIcon = SvgPicture.asset(
- assetName,
- color: Colors.red,
- semanticsLabel: 'A red up arrow'
-);
-```
-
-The default placeholder is an empty box (`LimitedBox`) - although if a `height`
-or `width` is specified on the `SvgPicture`, a `SizedBox` will be used instead
-(which ensures better layout experience). There is currently no way to show an
-Error visually, however errors will get properly logged to the console in debug
-mode.
-
-You can also specify a placeholder widget. The placeholder will display during
-parsing/loading (normally only relevant for network access).
-
-```dart
-// Will print error messages to the console.
-final String assetName = 'assets/image_that_does_not_exist.svg';
-final Widget svg = SvgPicture.asset(
- assetName,
-);
-
-final Widget networkSvg = SvgPicture.network(
- 'https://site-that-takes-a-while.com/image.svg',
- semanticsLabel: 'A shark?!',
- placeholderBuilder: (BuildContext context) => Container(
- padding: const EdgeInsets.all(30.0),
- child: const CircularProgressIndicator()),
-);
-```
-
-If you'd like to render the SVG to some other canvas, you can do something like:
-
-```dart
-import 'package:flutter_svg/flutter_svg.dart';
-final String rawSvg = '''''';
-final DrawableRoot svgRoot = await svg.fromSvgString(rawSvg, rawSvg);
-
-// If you only want the final Picture output, just use
-final Picture picture = svgRoot.toPicture();
-
-// Otherwise, if you want to draw it to a canvas:
-// Optional, but probably normally desirable: scale the canvas dimensions to
-// the SVG's viewbox
-svgRoot.scaleCanvasToViewBox(canvas);
-
-// Optional, but probably normally desireable: ensure the SVG isn't rendered
-// outside of the viewbox bounds
-svgRoot.clipCanvasToViewBox(canvas);
-svgRoot.draw(canvas, size);
-```
-
-The `SvgPicture` helps to automate this logic, and it provides some convenience
-wrappers for getting assets from multiple sources and caching the resultant
-`Picture`. _It does not render the data to an `Image` at any point_; you
-certainly can do that in Flutter, but you then lose some of the benefit of
-having a vector format to begin with.
-
-While I'm making every effort to avoid needlessly changing the API, it's not
-guarnateed to be stable yet (hence the pre-1.0.0 version). To date, the biggest
-change is deprecating the `SvgImage` widgets in favor of `SvgPicture` - it
-became very confusing to maintain that name, as `Picture`s are the underlying
-mechanism for rendering rather than `Image`s.
-
-See [main.dart](/../master/example/lib/main.dart) for a complete sample.
-
-## Check SVG compatibility
-
-As not all SVG features are supported by this library (see below), sometimes we have to dynamically
-check if an SVG contains any unsupported features resulting in broken images.
-You might also want to throw errors in tests, but only warn about them at runtime.
-This can be done by using the snippet below:
-
-```dart
-final SvgParser parser = SvgParser();
-try {
- parser.parse(svgString, warningsAsErrors: true);
- print('SVG is supported');
-} catch (e) {
- print('SVG contains unsupported features');
-}
-```
-
-> Note:
-> The library currently only detects unsupported elements (like the `
-
-
-
-
-
-
-
-
-
-
-
-
-''';
- final SvgParser parser = SvgParser();
- expect(
- parser.parse(svgStr, warningsAsErrors: true),
- throwsA(anything),
- );
- });
-
- test('Warns about unsupported elements by default', () async {
- const String svgStr = '''
-''';
-
- final SvgParser parser = SvgParser();
- expect(await parser.parse(svgStr), isA());
- });
-}
diff --git a/test/vector_drawable_test.dart b/test/vector_drawable_test.dart
deleted file mode 100644
index b2c2dcfd..00000000
--- a/test/vector_drawable_test.dart
+++ /dev/null
@@ -1,50 +0,0 @@
-import 'dart:typed_data';
-import 'dart:ui';
-
-import 'package:flutter_svg/flutter_svg.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
- test('DrawableRoot can mergeStyle', () {
- const DrawableStyle styleA = DrawableStyle(
- groupOpacity: 0.5,
- pathFillType: PathFillType.evenOdd,
- );
- const DrawableStyle styleB = DrawableStyle(
- pathFillType: PathFillType.nonZero,
- );
- DrawableRoot root = DrawableRoot(
- '', // No id
- const DrawableViewport(Size(100, 100), Size(100, 100)),
- [],
- DrawableDefinitionServer(),
- styleA,
- );
- expect(root.style!.pathFillType, styleA.pathFillType);
- root = root.mergeStyle(styleB);
- expect(root.style!.pathFillType, styleB.pathFillType);
- });
-
- test('SvgPictureDecoder uses color filter properly', () async {
- final PictureInfo info = await svg.svgPictureStringDecoder(
- '''
-
-''',
- false,
- const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color),
- 'test',
- );
- final Image image = await info.picture.toImage(2, 2);
- final ByteData data = (await image.toByteData())!;
-
- const List expected = [
- 0, 48, 0, 255, //
- 0, 48, 0, 255,
- 0, 48, 0, 255,
- 0, 48, 0, 255,
- ];
- expect(data.buffer.asUint8List(), expected);
- });
-}
diff --git a/test/xml_svg_test.dart b/test/xml_svg_test.dart
deleted file mode 100644
index f8cccfc9..00000000
--- a/test/xml_svg_test.dart
+++ /dev/null
@@ -1,147 +0,0 @@
-import 'dart:ui';
-
-import 'package:test/test.dart';
-import 'package:xml/xml_events.dart';
-
-import 'package:flutter_svg/src/svg/xml_parsers.dart';
-import 'package:flutter_svg/src/utilities/xml.dart';
-
-void main() {
- test('Xlink href tests', () {
- final XmlStartElementEvent el =
- parseEvents('').first
- as XmlStartElementEvent;
-
- final XmlStartElementEvent elXlink =
- parseEvents('')
- .first as XmlStartElementEvent;
-
- expect(getHrefAttribute(el.attributes), 'http://localhost');
- expect(getHrefAttribute(elXlink.attributes), 'http://localhost');
- });
-
- test('Attribute and style tests', () {
- final XmlStartElementEvent el =
- parseEvents('')
- .first as XmlStartElementEvent;
-
- expect(getAttribute(el.attributes, 'stroke'), '#fff');
- expect(getAttribute(el.attributes, 'fill'), '#eee');
- expect(getAttribute(el.attributes, 'stroke-dashpattern'), '1 2');
- expect(getAttribute(el.attributes, 'stroke-opacity'), '1');
- expect(getAttribute(el.attributes, 'stroke-another'), '');
- expect(getAttribute(el.attributes, 'fill-opacity'), '.23');
-
- expect(getAttribute(el.attributes, 'fill-opacity', checkStyle: false), '');
- expect(getAttribute(el.attributes, 'fill', checkStyle: false), '#eee');
- });
-
- // if the parsing logic changes, we can simplify some methods. for now assert that whitespace in attributes is preserved
- test('Attribute WhiteSpace test', () {
- final XmlStartElementEvent xd =
- parseEvents('').first
- as XmlStartElementEvent;
-
- expect(
- xd.attributes[0].value,
- ' asdf',
- reason:
- 'XML Parsing implementation no longer preserves leading whitespace in attributes!',
- );
- expect(
- xd.attributes[1].value,
- 'asdf ',
- reason:
- 'XML Parsing implementation no longer preserves trailing whitespace in attributes!',
- );
- });
-
- test('viewBox tests', () {
- const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
-
- final XmlStartElementEvent svgWithViewBox =
- parseEvents('').first
- as XmlStartElementEvent;
- final XmlStartElementEvent svgWithViewBoxAndWidthHeight =
- parseEvents('')
- .first as XmlStartElementEvent;
- final XmlStartElementEvent svgWithWidthHeight =
- parseEvents('').first
- as XmlStartElementEvent;
- final XmlStartElementEvent svgWithViewBoxMinXMinY =
- parseEvents('').first
- as XmlStartElementEvent;
- final XmlStartElementEvent svgWithNoSizeInfo =
- parseEvents('').first as XmlStartElementEvent;
-
- expect(parseViewBox(svgWithViewBoxAndWidthHeight.attributes)!.size,
- const Size(50, 50));
- expect(parseViewBox(svgWithViewBox.attributes)!.viewBoxRect, rect);
- expect(parseViewBox(svgWithViewBox.attributes)!.viewBoxOffset, Offset.zero);
- expect(parseViewBox(svgWithViewBoxAndWidthHeight.attributes)!.viewBoxRect,
- rect);
- expect(parseViewBox(svgWithWidthHeight.attributes)!.viewBoxRect, rect);
- expect(parseViewBox(svgWithNoSizeInfo.attributes, nullOk: true), null);
- expect(() => parseViewBox(svgWithNoSizeInfo.attributes), throwsStateError);
- expect(parseViewBox(svgWithViewBoxMinXMinY.attributes)!.viewBoxRect, rect);
- expect(parseViewBox(svgWithViewBoxMinXMinY.attributes)!.viewBoxOffset,
- const Offset(-42.0, -56.0));
- });
-
- test('TileMode tests', () {
- final XmlStartElementEvent pad =
- parseEvents('').first
- as XmlStartElementEvent;
- final XmlStartElementEvent reflect =
- parseEvents('').first
- as XmlStartElementEvent;
- final XmlStartElementEvent repeat =
- parseEvents('').first
- as XmlStartElementEvent;
- final XmlStartElementEvent invalid =
- parseEvents('').first
- as XmlStartElementEvent;
-
- final XmlStartElementEvent none =
- parseEvents('').first as XmlStartElementEvent;
-
- expect(parseTileMode(pad.attributes), TileMode.clamp);
- expect(parseTileMode(invalid.attributes), TileMode.clamp);
- expect(parseTileMode(none.attributes), TileMode.clamp);
-
- expect(parseTileMode(reflect.attributes), TileMode.mirror);
- expect(parseTileMode(repeat.attributes), TileMode.repeated);
- });
-
- test('@stroke-dashoffset tests', () {
- final XmlStartElementEvent abs =
- parseEvents('').first
- as XmlStartElementEvent;
- final XmlStartElementEvent pct =
- parseEvents('').first
- as XmlStartElementEvent;
-
- // TODO(dnfield): DashOffset is completely opaque right now, maybe expose the raw value?
- expect(parseDashOffset(abs.attributes), isNotNull);
- expect(parseDashOffset(pct.attributes), isNotNull);
- });
-
- test('font-weight tests', () {
- expect(parseFontWeight('100'), FontWeight.w100);
- expect(parseFontWeight('200'), FontWeight.w200);
- expect(parseFontWeight('300'), FontWeight.w300);
- expect(parseFontWeight('400'), FontWeight.w400);
- expect(parseFontWeight('500'), FontWeight.w500);
- expect(parseFontWeight('600'), FontWeight.w600);
- expect(parseFontWeight('700'), FontWeight.w700);
- expect(parseFontWeight('800'), FontWeight.w800);
- expect(parseFontWeight('900'), FontWeight.w900);
-
- expect(parseFontWeight('normal'), FontWeight.normal);
- expect(parseFontWeight('bold'), FontWeight.bold);
-
- expect(() => parseFontWeight('invalid'), throwsUnsupportedError);
- });
-}
diff --git a/tool/gen_golden.dart b/tool/gen_golden.dart
deleted file mode 100644
index d5731d30..00000000
--- a/tool/gen_golden.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-// There's probably some better way to do this, but for now run `flutter test tool/gen_golden.dart
-// should exclude files that
-// - aren't rendering properly
-// - have text (this doesn't render properly in the host setup?)
-// The golden files should then be visually compared against Chrome's rendering output for correctness.
-// The comparison may have to be made more tolerant if we want to use other sources of rendering for comparison...
-
-import 'dart:io';
-import 'dart:typed_data';
-import 'dart:ui';
-
-import 'package:path/path.dart' as path;
-
-import 'package:flutter_svg/svg.dart';
-import 'package:flutter_svg/src/vector_drawable.dart';
-
-Future getSvgPngBytes(String svgData) async {
- final PictureRecorder rec = PictureRecorder();
- final Canvas canvas = Canvas(rec);
-
- const Size size = Size(200.0, 200.0);
-
- final DrawableRoot svgRoot =
- await svg.fromSvgString(svgData, 'GenGoldenTest');
- svgRoot.scaleCanvasToViewBox(canvas, size);
- svgRoot.clipCanvasToViewBox(canvas);
-
- canvas.drawPaint(Paint()..color = const Color(0xFFFFFFFF));
- svgRoot.draw(canvas, svgRoot.viewport.viewBoxRect);
-
- final Picture pict = rec.endRecording();
-
- final Image image =
- await pict.toImage(size.width.toInt(), size.height.toInt());
- final ByteData bytes = (await image.toByteData(format: ImageByteFormat.png))!;
-
- return bytes.buffer.asUint8List();
-}
-
-Iterable getSvgFileNames() sync* {
- final Directory dir = Directory('./example/assets');
- for (FileSystemEntity fe in dir.listSync(recursive: true)) {
- if (fe is File && fe.path.toLowerCase().endsWith('.svg')) {
- // Skip text based tests unless we're on Linux - these have
- // subtle platform specific differences.
- if (fe.path.toLowerCase().contains('text') && !Platform.isLinux) {
- continue;
- }
- yield fe;
- }
- }
-}
-
-String getGoldenFileName(String svgAssetPath) {
- return svgAssetPath
- .replaceAll('/example\/assets/', '/golden/')
- .replaceAll('\\example\\assets\\', '\\golden\\')
- .replaceAll('.svg', '.png');
-}
-
-Future main() async {
- for (File fe in getSvgFileNames()) {
- final String pathName = getGoldenFileName(fe.path);
-
- final Directory goldenDir = Directory(path.dirname(pathName));
- if (!goldenDir.existsSync()) {
- goldenDir.createSync(recursive: true);
- }
- final File output = File(pathName);
- print(pathName);
- await output.writeAsBytes(await getSvgPngBytes(await fe.readAsString()));
- }
-}
diff --git a/vector_graphics.md b/vector_graphics.md
new file mode 100644
index 00000000..46a12682
--- /dev/null
+++ b/vector_graphics.md
@@ -0,0 +1,169 @@
+# `vector_graphics`, or `flutter_svg` 2.0
+
+Starting in version 2.0, `flutter_svg` migrated to using the
+`vector_graphics_compiler` as an SVG parsing backend and the `vector_graphics`
+runtime package for widget/render object responsibilities.
+
+The `vector_graphics` packages provide a compiler runtime that has no
+dependencies on `dart:ui`, and so can run completely as a CLI application or in
+a background isolate. It produces a binary file that has no versioning or
+compatibility guarantees - meaning that the output of the compiler is only
+suitable for use with the runtime and codec with the same exact git hash. This
+allows for a compact, optimized, and further optimizable format.
+
+The ideal way to use that package is to convert your assets at compile time and
+bundle them with your application, or store them on a server in a location that
+is matched precisely to git hash of the package that produced them. This can
+be challenging with the current state of the flutter_tools and Dart build
+systems, as well as for developers who lack the resources to version and control
+all of their network vector graphic assets. This package is meant to help bridge
+the gap, as well as to help existing `flutter_svg` users benefit from more
+optimizations and an easier migration path.
+
+## Why `vector_graphics`?
+
+SVG, as specified, is a nightmare. Parsing XML is slow, especially in comparison
+to binary formats like Flat/Protocol Buffers and the like. SVG itself pulls in
+many other web standards like CSS, JavaScript, and HTML. Even if you restrict
+yourself to some static subset of SVG, the specification allows for a wide
+latitude of insane but valid things. For example, the specification does nothing
+to prevent or discourage an editor from inserting dozens of `` nodes, each
+having a `transform`, that eventually transform their sole child back to the
+identity matrix. And because the specification states that each group node can
+have other inheritable attributes attached, each node must be examined and some
+suitable data structure(s) must be allocated to account for how to pass on
+those heritable attributes if any arise. On a slow, lost-cost mobile device,
+this cuts into valuable frame-time if done on the UI isolate. And this doesn't
+even mention that design tool may have snuck in any number of masks, which as
+specified require at least two render target switches but frequently are used in
+place of what could be achieved with a much cheaper clip.
+
+The basic problem is that SVG gives design tools a very large number of ways to
+specify a drawing, and design tools are generally not written to produce SVG
+that will be fast to render or fast to parse. Existing SVG optimization tools
+tend to focus on network download size, and may lack internal capabilities to
+examine the relationships between nodes to decide when and how they can be
+optimized.
+
+The `vector_graphics` suite addresses this by focusing less on being fast at
+parsing and more on being able to produce something that will be fast when it
+finally gets to the UI isolate - and faster when Flutter's rasterizer goes to
+render it. Some of the optimizations available that suite are currently only
+available when directly using the `vector_graphics` package, both because of
+challenges around packaging FFI libraries for various platforms and because
+some of the optimization algorithms may be more expensive than just taking the
+penalty of not using them.
+
+Additionally, the design that `flutter_svg` was using took inspiration from
+`package:flutter`'s image related classes. This turned out to be a mistake.
+There is no intention for `flutter_svg` to ever support animated/multi-frame
+SVGs, and the way that Flutter ties together byte acquisition and image decoding
+makes things a bit complicated. Instead, `vector_graphics` introduces the
+concept of a `BytesLoader` class, which knows how to obtain encoded bytes to
+feed to the runtime via an asynchronous method that optionally receives a
+`BuildContext`. See the `loaders.dart` file in this package for examples.
+
+As of this writing, the optimizations that are available include:
+
+- Useless element/attribute pruning.
+- Inheritance removal.
+- Paint/path deduplication.
+- Transform pre-calculation/collapsing.
+- Elimination of XML parsing on the UI isolate.
+- Elimination of UTF-8 and Base 64 decoding (i.e. for embedded images) on the UI
+ isolate.
+- Path dashing is done completely on a background isolate.
+- More use of 32-bit floats instead of 64-bit doubles, which saves on memory
+ with good visual fidelity.
+
+## What changes?
+
+Compared to prior releases of flutter_svg, the following changes can be
+expected:
+
+- Loss of support for `text` elements that use `dx` or `dy` attributes, which
+ already was implemented only partially and incorrectly.
+- Support for out of order `defs` and element references.
+- Support for patterns.
+- Gradients render slightly differently. This is likely due to a combination of
+ some precision differences and pre-calculating transforms.
+- Some shapes, such as circles and rounded rects, render slightly differently.
+ This is due to precision differences and pre-calculation of curves.
+- Golden tests may now show SVGs they did not used to show. this is because more
+ parsing and rendering is completely synchronous in tests, whereas previously
+ it was always at least partially async.
+- Outside of tests (where the extra async makes life more confusing) and web
+ (which does not have isolates), parsing happens in a separate isolates.
+
+## What do I need to change in my code?
+
+### `precachePicture`
+
+This API doesn't exist anymore. If you were using it to pre-fetch bytes from
+some `PictureProvider`, instead write a custom `BytesLoader` implementation
+(or use an existing one) and use `vg.loadPicture`. However, there is currently
+no provided widget to make drawing that picture easy, but generally speaking
+you can use a `CustomPainter`.
+
+### `PictureProvider`s
+
+If you had a custom `PictureProvider`, you will now want a custom `BytesLoader`.
+Consider overriding `SvgLoader`, which takes care of doing the right thing
+with isolates for you and only requires you to implement a function to obtain
+the SVG string that will be run in a non-UI isolate. See more examples in
+`loaders.dart` in this package.
+
+### `PictureCache`
+
+Pictures are no longer cached, however the byte data for vector_graphics output
+is cached in the `Cache` object. This is similar to but not quite the same as
+the old picture cache. It is available via `svg.cache`.
+
+### Widget changes
+
+The `SvgPicture.clipBehavior` property is deprecated (no-op).
+
+The `SvgPicture.cacheColorFilter` property is deprecated (no-op).
+
+The `SvgPicture.*` constructors still have `color` and `colorBlendMode`
+properties, which are deprecated. Instead use the `colorFilter` property. To
+achieve the old behavior of the `color` property, use
+`ColorFilter.mode(color, BlendMode.srcIn)`.
+
+The `SvgPicture.pictureProvider` property has been removed. Use the `loader`
+property instead, if you must.
+
+### Golden testing
+
+Please limit your golden tests. In particular, try to make sure that you avoid
+testing the same SVG over and over again in multiple different golden tests,
+and try to make sure that you _only have as many golden files that as can be
+reviewed by you and your team if they all changed in a single update_. In
+general, this is somewhere around 10-20 golden files _per reviwer_, and that is
+generously assuming that each reviewer will carefully examine the differences
+in those 10-20 files. Assume that the reviewer will have no idea what the author
+of the test intended, _even if the reviewer authored the test_. If your team
+is 5 people, that means somewhere between 50-100 goldens.
+
+Anecdotally, I have missed important regressions in golden tests I wrote because
+I wrote them several years ago and just forgot, and I was only looking for
+change X which I verified but missed changes Y and Z that I hadn't thought about
+in over a year. I have also worked with partner projects that use `flutter_svg`
+that have _tens of thousands of golden images directly or indirectly involveing
+SVGs_ and it is a _nightmare_ for everyone when some minor change comes along
+that touches most of them. Very frequently in such projects, it becomes obvious
+that _no one_ has ever looked at a large number of these golden tests, and they
+actually harm test coverage because they falsely make broken code appear to have
+coverage via a test that requires a human to double check its result.
+
+With that said, most golden testing should "just work," except if your SVGs have
+images in them. In that case, "real" async work needs to get done, and at some
+point the test in question will have to call
+
+```dart
+await tester.runAsync(() => vg.waitForPendingDecodes());
+await tester.pump();
+```
+
+to make sure that the image decode(s) finish and the placeholder widget is
+replaced with the actual picture.