From 66d5f97d303aa1712673b8ca7e1a889cf5e7270e Mon Sep 17 00:00:00 2001 From: Alexey Volkov Date: Thu, 29 Jun 2023 23:22:33 +0300 Subject: [PATCH] feat: Option to use toImageSync in ImageComposition class (#2593) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added ImageComposition.composeSync() function --- doc/flame/rendering/images.md | 7 ++- packages/flame/lib/src/image_composition.dart | 45 ++++++++++++++++--- packages/flame/lib/src/parallax.dart | 7 +-- packages/flame/lib/src/sprite.dart | 13 +++++- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/doc/flame/rendering/images.md b/doc/flame/rendering/images.md index 44ce55c02d0..2961b15c09e 100644 --- a/doc/flame/rendering/images.md +++ b/doc/flame/rendering/images.md @@ -230,10 +230,15 @@ final composition = ImageComposition() Vector2(128, 0), source: Rect.fromLTWH(32, 32, 64, 64), ); - + Image image = await composition.compose(); +Image imageSync = composition.composeSync(); ``` +As you can see, two versions of composing image are available. Use `ImageComposition.compose()` for +the async approach. Or use the new `ImageComposition.composeSync()` function to rasterize the +image into GPU context using the benefits of the `Picture.toImageSync` function. + **Note:** Composing images is expensive, we do not recommend you run this every tick as it affect the performance badly. Instead we recommend to have your compositions pre-rendered so you can just reuse the output image. diff --git a/packages/flame/lib/src/image_composition.dart b/packages/flame/lib/src/image_composition.dart index 6abdd7365a6..5c3996967d8 100644 --- a/packages/flame/lib/src/image_composition.dart +++ b/packages/flame/lib/src/image_composition.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:ui'; import 'package:flame/extensions.dart'; +import 'package:flutter/foundation.dart'; export '../extensions.dart'; @@ -83,8 +84,26 @@ class ImageComposition { /// Compose all the images into a single composition. Future compose() async { + final result = _composeCore(); + + return result.picture.toImageSafe( + result.width, + result.height, + ); + } + + /// Compose all the images into a single composition. + /// + /// A sync version of [compose] function. Read [Picture.toImageSync] for + /// detailed description of possible benefits in performance + Image composeSync() { + final result = _composeCore(); + return result.picture.toImageSync(result.width, result.height); + } + + _ComposeResult _composeCore() { // Rect used to determine how big the output image will be. - var output = Rect.zero; + var outputRect = Rect.zero; final recorder = PictureRecorder(); final canvas = Canvas(recorder); @@ -117,15 +136,31 @@ class ImageComposition { // Expand the output so it can be used later on when the output image gets // created. - output = output.expandToInclude(realDest); + outputRect = outputRect.expandToInclude(realDest); } - return recorder - .endRecording() - .toImageSafe(output.width.toInt(), output.height.toInt()); + final picture = recorder.endRecording(); + return _ComposeResult( + picture: picture, + width: outputRect.width.toInt(), + height: outputRect.height.toInt(), + ); } } +@immutable +class _ComposeResult { + const _ComposeResult({ + required this.picture, + required this.width, + required this.height, + }); + + final Picture picture; + final int width; + final int height; +} + class _Fragment { _Fragment( this.image, diff --git a/packages/flame/lib/src/parallax.dart b/packages/flame/lib/src/parallax.dart index 605cdb807d0..55ca774aded 100644 --- a/packages/flame/lib/src/parallax.dart +++ b/packages/flame/lib/src/parallax.dart @@ -107,6 +107,7 @@ abstract class ParallaxRenderer { filterQuality = filterQuality ?? FilterQuality.low; void update(double dt); + Image get image; } @@ -196,9 +197,8 @@ class ParallaxAnimation extends ParallaxRenderer { final animation = await SpriteAnimation.load(path, animationData, images: images); - final prerenderedFrames = await Future.wait( - animation.frames.map((frame) => frame.sprite.toImage()).toList(), - ); + final prerenderedFrames = + animation.frames.map((frame) => frame.sprite.toImageSync()).toList(); return ParallaxAnimation( animation, @@ -421,6 +421,7 @@ class Parallax { /// Do not modify this directly, since the layers won't be resized if you do. Vector2 get size => _size; + set size(Vector2 newSize) { resize(newSize); } diff --git a/packages/flame/lib/src/sprite.dart b/packages/flame/lib/src/sprite.dart index ccdda423bf1..20fa24a4116 100644 --- a/packages/flame/lib/src/sprite.dart +++ b/packages/flame/lib/src/sprite.dart @@ -84,6 +84,7 @@ class Sprite { // Used to avoid the creation of new Vector2 objects in render. static final _tmpRenderPosition = Vector2.zero(); static final _tmpRenderSize = Vector2.zero(); + static final _zeroPosition = Vector2.zero(); /// Renders this sprite onto the [canvas]. /// @@ -127,7 +128,17 @@ class Sprite { /// aren't going to use it anymore. Future toImage() async { final composition = ImageComposition() - ..add(image, Vector2.zero(), source: src); + ..add(image, _zeroPosition, source: src); return composition.compose(); } + + /// Return a new [Image] based on the [src] of the Sprite. + /// + /// A sync version of the [toImage] function. Read [Picture.toImageSync] for a + /// detailed description of possible benefits in performance. + Image toImageSync() { + final composition = ImageComposition() + ..add(image, _zeroPosition, source: src); + return composition.composeSync(); + } }