Skip to content

Commit b1ef7b9

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 b1ef7b9

File tree

5 files changed

+182
-70
lines changed

5 files changed

+182
-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

+42-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,7 +194,7 @@ 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
@@ -206,9 +204,16 @@ class RenderVectorGraphic extends RenderBox {
206204
// capture in a raster.
207205
final ui.PictureRecorder recorder = ui.PictureRecorder();
208206
final ui.Canvas canvas = ui.Canvas(recorder);
209-
207+
final Rect drawSize =
208+
ui.Rect.fromLTWH(0, 0, scaledWidth.toDouble(), scaledHeight.toDouble());
209+
canvas.clipRect(drawSize);
210+
final int saveCount = canvas.getSaveCount();
211+
if (colorPaint.color.opacity != 1.0 || colorPaint.colorFilter != null) {
212+
canvas.saveLayer(drawSize, colorPaint);
213+
}
210214
canvas.scale(scaleFactor);
211215
canvas.drawPicture(info.picture);
216+
canvas.restoreToCount(saveCount);
212217
final ui.Picture rasterPicture = recorder.endRecording();
213218

214219
final ui.Image pending =
@@ -230,12 +235,14 @@ class RenderVectorGraphic extends RenderBox {
230235
// Re-create the raster for a given vector graphic if the target size
231236
// is sufficiently different. Returns `null` if rasterData has been
232237
// updated immediately.
233-
void _maybeUpdateRaster() {
238+
void _maybeUpdateRaster(double drawScaleFactor) {
239+
_rasterScaleFactor = devicePixelRatio * drawScaleFactor;
234240
final int scaledWidth =
235-
(pictureInfo.size.width * devicePixelRatio / scale).round();
241+
(pictureInfo.size.width * _rasterScaleFactor).round();
236242
final int scaledHeight =
237-
(pictureInfo.size.height * devicePixelRatio / scale).round();
238-
final RasterKey key = RasterKey(assetKey, scaledWidth, scaledHeight);
243+
(pictureInfo.size.height * _rasterScaleFactor).round();
244+
final RasterKey key =
245+
RasterKey(assetKey, scaledWidth, scaledHeight, _colorPaint);
239246

240247
// First check if the raster is available synchronously. This also handles
241248
// a no-op change that would resolve to an identical picture.
@@ -249,7 +256,7 @@ class RenderVectorGraphic extends RenderBox {
249256
return;
250257
}
251258
final RasterData data =
252-
_createRaster(key, devicePixelRatio / scale, pictureInfo);
259+
_createRaster(key, _rasterScaleFactor, pictureInfo, _colorPaint);
253260
data.count += 1;
254261

255262
assert(!_liveRasterCache.containsKey(key));
@@ -296,18 +303,23 @@ class RenderVectorGraphic extends RenderBox {
296303
return;
297304
}
298305

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

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);
311323
final Rect src = ui.Rect.fromLTWH(
312324
0,
313325
0,
@@ -325,7 +337,7 @@ class RenderVectorGraphic extends RenderBox {
325337
image,
326338
src,
327339
dst,
328-
colorPaint,
340+
Paint(),
329341
);
330342
}
331343
}

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)