Skip to content

Commit

Permalink
feat: Option to use toImageSync in ImageComposition class (#2593)
Browse files Browse the repository at this point in the history
Added ImageComposition.composeSync() function
  • Loading branch information
ASGAlex committed Jun 29, 2023
1 parent d40fefc commit 66d5f97
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 10 deletions.
7 changes: 6 additions & 1 deletion doc/flame/rendering/images.md
Expand Up @@ -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.
Expand Down
45 changes: 40 additions & 5 deletions packages/flame/lib/src/image_composition.dart
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:ui';

import 'package:flame/extensions.dart';
import 'package:flutter/foundation.dart';

export '../extensions.dart';

Expand Down Expand Up @@ -83,8 +84,26 @@ class ImageComposition {

/// Compose all the images into a single composition.
Future<Image> 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);

Expand Down Expand Up @@ -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,
Expand Down
7 changes: 4 additions & 3 deletions packages/flame/lib/src/parallax.dart
Expand Up @@ -107,6 +107,7 @@ abstract class ParallaxRenderer {
filterQuality = filterQuality ?? FilterQuality.low;

void update(double dt);

Image get image;
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
Expand Down
13 changes: 12 additions & 1 deletion packages/flame/lib/src/sprite.dart
Expand Up @@ -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].
///
Expand Down Expand Up @@ -127,7 +128,17 @@ class Sprite {
/// aren't going to use it anymore.
Future<Image> 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();
}
}

0 comments on commit 66d5f97

Please sign in to comment.