Skip to content

Commit

Permalink
Removed fallback on web for drawAtlas (#1153)
Browse files Browse the repository at this point in the history
* Removed fallback on web for drawAtlas

* Fix analyzer

* Optional argument for not using `drawAtlas`

* Add changelog entry

* Update to flag name to useAtlas

* Update changelog entry

Co-authored-by: Luan Nico <luanpotter27@gmail.com>
Co-authored-by: Lukas Klingsbo <me@lukas.fyi>
Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com>
  • Loading branch information
4 people authored Dec 7, 2021
1 parent 9699112 commit bd68220
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 35 deletions.
1 change: 1 addition & 0 deletions packages/flame/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Fix `HitboxCircle` when component is flipped
- `MoveAlongPathEffect` can now be absolute, and can auto-orient the object along the path
- `ScaleEffect.by` now applies multiplicatively instead of additively
- Remove web fallback for `drawAtlas` in SpriteBatch, but added flag `useAtlas` to activate it

## [1.0.0-releasecandidate.18]
- Forcing portrait and landscape mode is now supported on web
Expand Down
88 changes: 53 additions & 35 deletions packages/flame/lib/src/sprite_batch.dart
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
import 'dart:collection';
import 'dart:ui';

import 'package:flutter/foundation.dart';

import '../game.dart';
import 'assets/images.dart';
import 'extensions/image.dart';
import 'extensions/vector2.dart';
import 'flame.dart';

extension SpriteBatchExtension on Game {
/// Utility method to load and cache the image for a [SpriteBatch] based on its options
/// Utility method to load and cache the image for a [SpriteBatch] based on
/// its options
Future<SpriteBatch> loadSpriteBatch(
String path, {
Color defaultColor = const Color(0x00000000),
BlendMode defaultBlendMode = BlendMode.srcOver,
RSTransform? defaultTransform,
bool useAtlas = true,
}) {
return SpriteBatch.load(
path,
defaultColor: defaultColor,
defaultBlendMode: defaultBlendMode,
defaultTransform: defaultTransform,
images: images,
useAtlas: useAtlas,
);
}
}

/// This is the scale value used in [BatchItem.matrix], we can't determine this from the [BatchItem.transform],
/// but we also don't need to do so because it is already calculated inside the transform values.
/// This is the scale value used in [BatchItem.matrix], we can't determine this
/// from the [BatchItem.transform], but we also don't need to do so because it
/// is already calculated inside the transform values.
const _defaultScale = 0.0;

/// A single item in a SpriteBatch.
///
/// Holds all the important information of a batch item,
///
/// Web currently does not support `Canvas.drawAtlas`, so a BatchItem will
/// automatically calculate a transform matrix based on the [transform] value, to be
/// used when rendering on the web. It will initialize a [destination] object
/// and a [paint] object.
class BatchItem {
/// The source rectangle on the [SpriteBatch.atlas].
final Rect source;
Expand Down Expand Up @@ -80,16 +77,19 @@ class BatchItem {
/// The SpriteBatch API allows for rendering multiple items at once.
///
/// This class allows for optimization when you want to draw many parts of an
/// image onto the canvas. It is more efficient than using multiple calls to [Canvas.drawImageRect]
/// and provides more functionality by allowing each [BatchItem] to have their own transform
/// rotation and color.
/// image onto the canvas. It is more efficient than using multiple calls to
/// [Canvas.drawImageRect] and provides more functionality by allowing each
/// [BatchItem] to have their own transform rotation and color.
///
/// By collecting all the necessary transforms on a single image and sending those transforms
/// in a single batch to the GPU, we can render multiple parts of a single image at once.
/// By collecting all the necessary transforms on a single image and sending
/// those transforms in a single batch to the GPU, we can render multiple parts
/// of a single image at once.
///
/// **Note**: Currently web does not support `Canvas.drawAtlas`, which SpriteBatch uses under
/// the hood, instead it will render each [BatchItem] using `Canvas.drawImageRect`, so there
/// might be a performance hit on web when working with many batch items.
/// **Note**: If you are experiencing problems with ghost lines, the less
/// performant [Canvas.drawImageRect] can be used instead of [Canvas.drawAtlas].
/// To activate this mode, pass in `useAtlas = false` to the constructor or
/// load method that you are using and each [BatchItem] will be rendered using
/// the [Canvas.drawImageRect] method instead.
class SpriteBatch {
/// List of all the existing batch items.
final _batchItems = <BatchItem>[];
Expand Down Expand Up @@ -130,7 +130,8 @@ class SpriteBatch {
/// The default color, used as a background color for a [BatchItem].
final Color defaultColor;

/// The default transform, used when a transform was not supplied for a [BatchItem].
/// The default transform, used when a transform was not supplied for a
/// [BatchItem].
final RSTransform? defaultTransform;

/// The default blend mode, used for blending a batch item.
Expand All @@ -145,11 +146,15 @@ class SpriteBatch {
/// The size of the [atlas].
Vector2 get size => atlas.size;

/// Whether to use [Canvas.drawAtlas] or not.
final bool useAtlas;

SpriteBatch(
this.atlas, {
this.defaultColor = const Color(0x00000000),
this.defaultBlendMode = BlendMode.srcOver,
this.defaultTransform,
this.useAtlas = true,
});

/// Takes a path of an image, and optional arguments for the SpriteBatch.
Expand All @@ -161,28 +166,33 @@ class SpriteBatch {
BlendMode defaultBlendMode = BlendMode.srcOver,
RSTransform? defaultTransform,
Images? images,
bool useAtlas = true,
}) async {
final _images = images ?? Flame.images;
return SpriteBatch(
await _images.load(path),
defaultColor: defaultColor,
defaultTransform: defaultTransform ?? RSTransform(1, 0, 0, 0),
defaultBlendMode: defaultBlendMode,
useAtlas: useAtlas,
);
}

/// Add a new batch item using a RSTransform.
///
/// The [source] parameter is the source location on the [atlas].
///
/// You can position, rotate and scale it on the canvas using the [transform] parameter.
/// You can position, rotate and scale it on the canvas using the [transform]
/// parameter.
///
/// The [color] parameter allows you to render a color behind the batch item, as a background color.
/// The [color] parameter allows you to render a color behind the batch item,
/// as a background color.
///
/// The [add] method may be a simpler way to add a batch item to the batch. However,
/// if there is a way to factor out the computations of the sine and cosine of the
/// rotation so that they can be reused over multiple calls to this constructor,
/// it may be more efficient to directly use this method instead.
/// The [add] method may be a simpler way to add a batch item to the batch.
/// However, if there is a way to factor out the computations of the sine and
/// cosine of the rotation so that they can be reused over multiple calls to
/// this constructor, it may be more efficient to directly use this method
/// instead.
void addTransform({
required Rect source,
RSTransform? transform,
Expand All @@ -203,17 +213,22 @@ class SpriteBatch {

/// Add a new batch item.
///
/// The [source] parameter is the source location on the [atlas]. You can position it
/// on the canvas using the [offset] parameter.
/// The [source] parameter is the source location on the [atlas]. You can
/// position it on the canvas using the [offset] parameter.
///
/// You can transform the sprite from its [offset] using [scale], [rotation] and [anchor].
/// You can transform the sprite from its [offset] using [scale], [rotation]
/// and [anchor].
///
/// The [color] paramater allows you to render a color behind the batch item, as a background color.
/// The [color] paramater allows you to render a color behind the batch item,
/// as a background color.
///
/// This method creates a new [RSTransform] based on the given transform arguments. If many [RSTransform] objects are being
/// created and there is a way to factor out the computations of the sine and cosine of the rotation
/// (which are computed each time this method is called) and reuse them over multiple [RSTransform] objects,
/// it may be more efficient to directly use the more direct [addTransform] method instead.
/// This method creates a new [RSTransform] based on the given transform
/// arguments. If many [RSTransform] objects are being created and there is a
/// way to factor out the computations of the sine and cosine of the rotation
/// (which are computed each time this method is called) and reuse them over
/// multiple [RSTransform] objects,
/// it may be more efficient to directly use the more direct [addTransform]
/// method instead.
void add({
required Rect source,
double scale = 1.0,
Expand Down Expand Up @@ -254,15 +269,18 @@ class SpriteBatch {
_batchItems.clear();
}

// Used to not create new paint objects in [render] on every tick.
final Paint _emptyPaint = Paint();

void render(
Canvas canvas, {
BlendMode? blendMode,
Rect? cullRect,
Paint? paint,
}) {
paint ??= Paint();
paint ??= _emptyPaint;

if (kIsWeb) {
if (!useAtlas) {
for (final batchItem in _batchItems) {
paint.blendMode = blendMode ?? paint.blendMode;

Expand Down

0 comments on commit bd68220

Please sign in to comment.