Skip to content

[vector_graphics] Fix raster cache size calculation and incorporate ColorFilter/Opacity effects into cache #9082

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/vector_graphics/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.1.19

* Fix incorrect scaling under Transform in raster mode and reduce extra rendering cost from ColorFilter/Opacity in raster mode.

## 1.1.18

* Allow transition between placeholder and loaded image to have an animation.
Expand Down
73 changes: 43 additions & 30 deletions packages/vector_graphics/lib/src/render_vector_graphic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math' as math;
import 'dart:ui' as ui;

import 'package:flutter/animation.dart';
Expand All @@ -15,7 +16,7 @@ import 'listener.dart';
@immutable
class RasterKey {
/// Create a new [RasterKey].
const RasterKey(this.assetKey, this.width, this.height);
const RasterKey(this.assetKey, this.width, this.height, this.paint);

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

/// The paint of this vector graphic raster.
final Paint paint;

@override
bool operator ==(Object other) {
return other is RasterKey &&
other.assetKey == assetKey &&
other.width == width &&
other.height == height;
other.height == height &&
other.paint.color == paint.color &&
other.paint.colorFilter == paint.colorFilter;
}

@override
int get hashCode => Object.hash(assetKey, width, height);
int get hashCode =>
Object.hash(assetKey, width, height, paint.color, paint.colorFilter);
}

/// The cache entry for a rasterized vector graphic.
Expand Down Expand Up @@ -81,7 +88,6 @@ class RenderVectorGraphic extends RenderBox {
this._colorFilter,
this._devicePixelRatio,
this._opacity,
this._scale,
) {
_opacity?.addListener(_updateOpacity);
_updateOpacity();
Expand Down Expand Up @@ -116,6 +122,8 @@ class RenderVectorGraphic extends RenderBox {
/// An optional [ColorFilter] to apply to the rasterized vector graphic.
ColorFilter? get colorFilter => _colorFilter;
ColorFilter? _colorFilter;
double _rasterScaleFactor = 1.0;
final Paint _colorPaint = Paint();
set colorFilter(ColorFilter? value) {
if (colorFilter == value) {
return;
Expand Down Expand Up @@ -173,16 +181,6 @@ class RenderVectorGraphic extends RenderBox {
/// the vector graphic itself has a size of 50x50, and [BoxFit.fill]
/// is used. This will compute a scale of 2.0, which will result in a
/// raster that is 100x100.
double get scale => _scale;
double _scale;
set scale(double value) {
assert(value != 0);
if (value == scale) {
return;
}
_scale = value;
markNeedsPaint();
}

@override
bool hitTestSelf(Offset position) => true;
Expand All @@ -196,19 +194,27 @@ class RenderVectorGraphic extends RenderBox {
}

static RasterData _createRaster(
RasterKey key, double scaleFactor, PictureInfo info) {
RasterKey key, double scaleFactor, PictureInfo info, Paint colorPaint) {
final int scaledWidth = key.width;
final int scaledHeight = key.height;
// In order to scale a picture, it must be placed in a new picture
// with a transform applied. Surprisingly, the height and width
// arguments of Picture.toImage do not control the resolution that the
// picture is rendered at, instead it controls how much of the picture to
// capture in a raster.
// Note: Transform applies a transformation to the canvas matrix.
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);

final Rect drawSize =
ui.Rect.fromLTWH(0, 0, scaledWidth.toDouble(), scaledHeight.toDouble());
canvas.clipRect(drawSize);
final int saveCount = canvas.getSaveCount();
if (colorPaint.color.opacity != 1.0 || colorPaint.colorFilter != null) {
canvas.saveLayer(drawSize, colorPaint);
}
canvas.scale(scaleFactor);
canvas.drawPicture(info.picture);
canvas.restoreToCount(saveCount);
final ui.Picture rasterPicture = recorder.endRecording();

final ui.Image pending =
Expand All @@ -230,12 +236,14 @@ class RenderVectorGraphic extends RenderBox {
// Re-create the raster for a given vector graphic if the target size
// is sufficiently different. Returns `null` if rasterData has been
// updated immediately.
void _maybeUpdateRaster() {
void _maybeUpdateRaster(double drawScaleFactor) {
_rasterScaleFactor = devicePixelRatio * drawScaleFactor;
final int scaledWidth =
(pictureInfo.size.width * devicePixelRatio / scale).round();
(pictureInfo.size.width * _rasterScaleFactor).round();
final int scaledHeight =
(pictureInfo.size.height * devicePixelRatio / scale).round();
final RasterKey key = RasterKey(assetKey, scaledWidth, scaledHeight);
(pictureInfo.size.height * _rasterScaleFactor).round();
final RasterKey key =
RasterKey(assetKey, scaledWidth, scaledHeight, _colorPaint);

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

assert(!_liveRasterCache.containsKey(key));
Expand Down Expand Up @@ -296,18 +304,23 @@ class RenderVectorGraphic extends RenderBox {
return;
}

_maybeUpdateRaster();
if (colorFilter != null) {
_colorPaint.colorFilter = colorFilter;
}
_colorPaint.color = Color.fromRGBO(0, 0, 0, _opacityValue);

// Use this transform to get real x/y scale facotr.
final Float64List transformList = context.canvas.getTransform();
final double drawScaleFactor = math.max(
transformList[0 + 0 * 4],
transformList[1 + 1 * 4],
);
_maybeUpdateRaster(drawScaleFactor);

final ui.Image image = _rasterData!.image;
final int width = _rasterData!.key.width;
final int height = _rasterData!.key.height;

// Use `FilterQuality.low` to scale the image, which corresponds to
// bilinear interpolation.
final Paint colorPaint = Paint()..filterQuality = ui.FilterQuality.low;
if (colorFilter != null) {
colorPaint.colorFilter = colorFilter;
}
colorPaint.color = Color.fromRGBO(0, 0, 0, _opacityValue);
final Rect src = ui.Rect.fromLTWH(
0,
0,
Expand All @@ -325,7 +338,7 @@ class RenderVectorGraphic extends RenderBox {
image,
src,
dst,
colorPaint,
Paint(),
);
}
}
Expand Down
14 changes: 1 addition & 13 deletions packages/vector_graphics/lib/src/vector_graphics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:math' as math;
import 'dart:ui' as ui;

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

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

double scale = 1.0;
scale = math.min(
pictureInfo.size.width / width!,
pictureInfo.size.height / height!,
);

if (_webRenderObject) {
child = _RawWebVectorGraphicWidget(
pictureInfo: pictureInfo,
Expand All @@ -474,7 +467,6 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
assetKey: _pictureInfo!.key,
colorFilter: widget.colorFilter,
opacity: widget.opacity,
scale: scale,
);
} else {
child = _RawPictureVectorGraphicWidget(
Expand Down Expand Up @@ -554,13 +546,11 @@ class _RawVectorGraphicWidget extends SingleChildRenderObjectWidget {
required this.pictureInfo,
required this.colorFilter,
required this.opacity,
required this.scale,
required this.assetKey,
});

final PictureInfo pictureInfo;
final ColorFilter? colorFilter;
final double scale;
final Animation<double>? opacity;
final Object assetKey;

Expand All @@ -572,7 +562,6 @@ class _RawVectorGraphicWidget extends SingleChildRenderObjectWidget {
colorFilter,
MediaQuery.maybeOf(context)?.devicePixelRatio ?? 1.0,
opacity,
scale,
);
}

Expand All @@ -586,8 +575,7 @@ class _RawVectorGraphicWidget extends SingleChildRenderObjectWidget {
..assetKey = assetKey
..colorFilter = colorFilter
..devicePixelRatio = MediaQuery.maybeOf(context)?.devicePixelRatio ?? 1.0
..opacity = opacity
..scale = scale;
..opacity = opacity;
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/vector_graphics/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: vector_graphics
description: A vector graphics rendering package for Flutter using a binary encoding.
repository: https://github.com/flutter/packages/tree/main/packages/vector_graphics
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+vector_graphics%22
version: 1.1.18
version: 1.1.19

environment:
sdk: ^3.4.0
Expand Down
Loading