Skip to content

Commit d17d97e

Browse files
committed
[vector_graphics] Fix incorrect scaling under Transform in raster mode and reduce extra rendering cost from ColorFilter/Opacity in raster mode.
1 parent 57a9a9b commit d17d97e

File tree

5 files changed

+183
-70
lines changed

5 files changed

+183
-70
lines changed

packages/vector_graphics/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.1.19
2+
3+
* Fix incorrect scaling under Transform in raster mode and reduce extra rendering cost from ColorFilter/Opacity in raster mode.
4+
15
## 1.1.18
26

37
* Allow transition between placeholder and loaded image to have an animation.

packages/vector_graphics/lib/src/render_vector_graphic.dart

+43-30
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:math' as math;
56
import 'dart:ui' as ui;
67

78
import 'package:flutter/animation.dart';
@@ -15,7 +16,7 @@ import 'listener.dart';
1516
@immutable
1617
class RasterKey {
1718
/// Create a new [RasterKey].
18-
const RasterKey(this.assetKey, this.width, this.height);
19+
const RasterKey(this.assetKey, this.width, this.height, this.paint);
1920

2021
/// An object that is used to identify the raster data this key will store.
2122
///
@@ -28,16 +29,22 @@ class RasterKey {
2829
/// The width of this vector graphic raster, in physical pixels.
2930
final int height;
3031

32+
/// The paint of this vector graphic raster.
33+
final Paint paint;
34+
3135
@override
3236
bool operator ==(Object other) {
3337
return other is RasterKey &&
3438
other.assetKey == assetKey &&
3539
other.width == width &&
36-
other.height == height;
40+
other.height == height &&
41+
other.paint.color == paint.color &&
42+
other.paint.colorFilter == paint.colorFilter;
3743
}
3844

3945
@override
40-
int get hashCode => Object.hash(assetKey, width, height);
46+
int get hashCode =>
47+
Object.hash(assetKey, width, height, paint.color, paint.colorFilter);
4148
}
4249

4350
/// The cache entry for a rasterized vector graphic.
@@ -81,7 +88,6 @@ class RenderVectorGraphic extends RenderBox {
8188
this._colorFilter,
8289
this._devicePixelRatio,
8390
this._opacity,
84-
this._scale,
8591
) {
8692
_opacity?.addListener(_updateOpacity);
8793
_updateOpacity();
@@ -116,6 +122,8 @@ class RenderVectorGraphic extends RenderBox {
116122
/// An optional [ColorFilter] to apply to the rasterized vector graphic.
117123
ColorFilter? get colorFilter => _colorFilter;
118124
ColorFilter? _colorFilter;
125+
double _rasterScaleFactor = 1.0;
126+
final Paint _colorPaint = Paint();
119127
set colorFilter(ColorFilter? value) {
120128
if (colorFilter == value) {
121129
return;
@@ -173,16 +181,6 @@ class RenderVectorGraphic extends RenderBox {
173181
/// the vector graphic itself has a size of 50x50, and [BoxFit.fill]
174182
/// is used. This will compute a scale of 2.0, which will result in a
175183
/// raster that is 100x100.
176-
double get scale => _scale;
177-
double _scale;
178-
set scale(double value) {
179-
assert(value != 0);
180-
if (value == scale) {
181-
return;
182-
}
183-
_scale = value;
184-
markNeedsPaint();
185-
}
186184
187185
@override
188186
bool hitTestSelf(Offset position) => true;
@@ -196,19 +194,27 @@ class RenderVectorGraphic extends RenderBox {
196194
}
197195

198196
static RasterData _createRaster(
199-
RasterKey key, double scaleFactor, PictureInfo info) {
197+
RasterKey key, double scaleFactor, PictureInfo info, Paint colorPaint) {
200198
final int scaledWidth = key.width;
201199
final int scaledHeight = key.height;
202200
// In order to scale a picture, it must be placed in a new picture
203201
// with a transform applied. Surprisingly, the height and width
204202
// arguments of Picture.toImage do not control the resolution that the
205203
// picture is rendered at, instead it controls how much of the picture to
206204
// capture in a raster.
205+
// Note: Transform applies a transformation to the canvas matrix.
207206
final ui.PictureRecorder recorder = ui.PictureRecorder();
208207
final ui.Canvas canvas = ui.Canvas(recorder);
209-
208+
final Rect drawSize =
209+
ui.Rect.fromLTWH(0, 0, scaledWidth.toDouble(), scaledHeight.toDouble());
210+
canvas.clipRect(drawSize);
211+
final int saveCount = canvas.getSaveCount();
212+
if (colorPaint.color.opacity != 1.0 || colorPaint.colorFilter != null) {
213+
canvas.saveLayer(drawSize, colorPaint);
214+
}
210215
canvas.scale(scaleFactor);
211216
canvas.drawPicture(info.picture);
217+
canvas.restoreToCount(saveCount);
212218
final ui.Picture rasterPicture = recorder.endRecording();
213219

214220
final ui.Image pending =
@@ -230,12 +236,14 @@ class RenderVectorGraphic extends RenderBox {
230236
// Re-create the raster for a given vector graphic if the target size
231237
// is sufficiently different. Returns `null` if rasterData has been
232238
// updated immediately.
233-
void _maybeUpdateRaster() {
239+
void _maybeUpdateRaster(double drawScaleFactor) {
240+
_rasterScaleFactor = devicePixelRatio * drawScaleFactor;
234241
final int scaledWidth =
235-
(pictureInfo.size.width * devicePixelRatio / scale).round();
242+
(pictureInfo.size.width * _rasterScaleFactor).round();
236243
final int scaledHeight =
237-
(pictureInfo.size.height * devicePixelRatio / scale).round();
238-
final RasterKey key = RasterKey(assetKey, scaledWidth, scaledHeight);
244+
(pictureInfo.size.height * _rasterScaleFactor).round();
245+
final RasterKey key =
246+
RasterKey(assetKey, scaledWidth, scaledHeight, _colorPaint);
239247

240248
// First check if the raster is available synchronously. This also handles
241249
// a no-op change that would resolve to an identical picture.
@@ -249,7 +257,7 @@ class RenderVectorGraphic extends RenderBox {
249257
return;
250258
}
251259
final RasterData data =
252-
_createRaster(key, devicePixelRatio / scale, pictureInfo);
260+
_createRaster(key, _rasterScaleFactor, pictureInfo, _colorPaint);
253261
data.count += 1;
254262

255263
assert(!_liveRasterCache.containsKey(key));
@@ -296,18 +304,23 @@ class RenderVectorGraphic extends RenderBox {
296304
return;
297305
}
298306

299-
_maybeUpdateRaster();
307+
if (colorFilter != null) {
308+
_colorPaint.colorFilter = colorFilter;
309+
}
310+
_colorPaint.color = Color.fromRGBO(0, 0, 0, _opacityValue);
311+
312+
// Use this transform to get real x/y scale facotr.
313+
final Float64List transformList = context.canvas.getTransform();
314+
final double drawScaleFactor = math.max(
315+
transformList[0 + 0 * 4],
316+
transformList[1 + 1 * 4],
317+
);
318+
_maybeUpdateRaster(drawScaleFactor);
319+
300320
final ui.Image image = _rasterData!.image;
301321
final int width = _rasterData!.key.width;
302322
final int height = _rasterData!.key.height;
303323

304-
// Use `FilterQuality.low` to scale the image, which corresponds to
305-
// bilinear interpolation.
306-
final Paint colorPaint = Paint()..filterQuality = ui.FilterQuality.low;
307-
if (colorFilter != null) {
308-
colorPaint.colorFilter = colorFilter;
309-
}
310-
colorPaint.color = Color.fromRGBO(0, 0, 0, _opacityValue);
311324
final Rect src = ui.Rect.fromLTWH(
312325
0,
313326
0,
@@ -325,7 +338,7 @@ class RenderVectorGraphic extends RenderBox {
325338
image,
326339
src,
327340
dst,
328-
colorPaint,
341+
Paint(),
329342
);
330343
}
331344
}

packages/vector_graphics/lib/src/vector_graphics.dart

+1-13
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6-
import 'dart:math' as math;
76
import 'dart:ui' as ui;
87

98
import 'package:flutter/foundation.dart';
@@ -455,12 +454,6 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
455454

456455
assert(width != null && height != null);
457456

458-
double scale = 1.0;
459-
scale = math.min(
460-
pictureInfo.size.width / width!,
461-
pictureInfo.size.height / height!,
462-
);
463-
464457
if (_webRenderObject) {
465458
child = _RawWebVectorGraphicWidget(
466459
pictureInfo: pictureInfo,
@@ -474,7 +467,6 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
474467
assetKey: _pictureInfo!.key,
475468
colorFilter: widget.colorFilter,
476469
opacity: widget.opacity,
477-
scale: scale,
478470
);
479471
} else {
480472
child = _RawPictureVectorGraphicWidget(
@@ -554,13 +546,11 @@ class _RawVectorGraphicWidget extends SingleChildRenderObjectWidget {
554546
required this.pictureInfo,
555547
required this.colorFilter,
556548
required this.opacity,
557-
required this.scale,
558549
required this.assetKey,
559550
});
560551

561552
final PictureInfo pictureInfo;
562553
final ColorFilter? colorFilter;
563-
final double scale;
564554
final Animation<double>? opacity;
565555
final Object assetKey;
566556

@@ -572,7 +562,6 @@ class _RawVectorGraphicWidget extends SingleChildRenderObjectWidget {
572562
colorFilter,
573563
MediaQuery.maybeOf(context)?.devicePixelRatio ?? 1.0,
574564
opacity,
575-
scale,
576565
);
577566
}
578567

@@ -586,8 +575,7 @@ class _RawVectorGraphicWidget extends SingleChildRenderObjectWidget {
586575
..assetKey = assetKey
587576
..colorFilter = colorFilter
588577
..devicePixelRatio = MediaQuery.maybeOf(context)?.devicePixelRatio ?? 1.0
589-
..opacity = opacity
590-
..scale = scale;
578+
..opacity = opacity;
591579
}
592580
}
593581

packages/vector_graphics/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: vector_graphics
22
description: A vector graphics rendering package for Flutter using a binary encoding.
33
repository: https://github.com/flutter/packages/tree/main/packages/vector_graphics
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+vector_graphics%22
5-
version: 1.1.18
5+
version: 1.1.19
66

77
environment:
88
sdk: ^3.4.0

0 commit comments

Comments
 (0)