LCOV - code coverage report
Current view: top level - lib/src - sprite_batch.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 69 0.0 %
Date: 2021-08-10 15:50:53 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:collection';
       2             : import 'dart:ui';
       3             : 
       4             : import 'package:flutter/foundation.dart';
       5             : 
       6             : import 'assets/images.dart';
       7             : import 'extensions/image.dart';
       8             : import 'extensions/vector2.dart';
       9             : import 'flame.dart';
      10             : import 'game/game.dart';
      11             : 
      12             : extension SpriteBatchExtension on Game {
      13             :   /// Utility method to load and cache the image for a [SpriteBatch] based on its options
      14           0 :   Future<SpriteBatch> loadSpriteBatch(
      15             :     String path, {
      16             :     Color defaultColor = const Color(0x00000000),
      17             :     BlendMode defaultBlendMode = BlendMode.srcOver,
      18             :     RSTransform? defaultTransform,
      19             :   }) {
      20           0 :     return SpriteBatch.load(
      21             :       path,
      22             :       defaultColor: defaultColor,
      23             :       defaultBlendMode: defaultBlendMode,
      24             :       defaultTransform: defaultTransform,
      25           0 :       images: images,
      26             :     );
      27             :   }
      28             : }
      29             : 
      30             : /// This is the scale value used in [BatchItem.matrix], we can't determine this from the [BatchItem.transform],
      31             : /// but we also don't need to do so because it is already calculated inside the transform values.
      32             : const _defaultScale = 0.0;
      33             : 
      34             : /// A single item in a SpriteBatch.
      35             : ///
      36             : /// Holds all the important information of a batch item,
      37             : ///
      38             : /// Web currently does not support `Canvas.drawAtlas`, so a BatchItem will
      39             : /// automatically calculate a transform matrix based on the [transform] value, to be
      40             : /// used when rendering on the web. It will initialize a [destination] object
      41             : /// and a [paint] object.
      42             : class BatchItem {
      43             :   /// The source rectangle on the [SpriteBatch.atlas].
      44             :   final Rect source;
      45             : 
      46             :   /// The destination rectangle for the Canvas.
      47             :   ///
      48             :   /// It will be transformed by [matrix].
      49             :   final Rect destination;
      50             : 
      51             :   /// The transform values for this batch item.
      52             :   final RSTransform transform;
      53             : 
      54             :   /// The background color for this batch item.
      55             :   final Color color;
      56             : 
      57             :   /// Fallback matrix for the web.
      58             :   ///
      59             :   /// Because `Canvas.drawAtlas` is not supported on the web we also
      60             :   /// build a `Matrix4` based on the [transform] values.
      61             :   final Matrix4 matrix;
      62             : 
      63             :   /// Paint object used for the web.
      64             :   final Paint paint;
      65             : 
      66           0 :   BatchItem({
      67             :     required this.source,
      68             :     required this.transform,
      69             :     required this.color,
      70           0 :   })  : matrix = Matrix4(
      71           0 :           transform.scos, transform.ssin, 0, 0, //
      72           0 :           -transform.ssin, transform.scos, 0, 0, //
      73             :           0, 0, _defaultScale, 0, //
      74           0 :           transform.tx, transform.ty, 0, 1, //
      75             :         ),
      76           0 :         paint = Paint()..color = color,
      77           0 :         destination = Offset.zero & source.size;
      78             : }
      79             : 
      80             : /// The SpriteBatch API allows for rendering multiple items at once.
      81             : ///
      82             : /// This class allows for optimization when you want to draw many parts of an
      83             : /// image onto the canvas. It is more efficient than using multiple calls to [Canvas.drawImageRect]
      84             : /// and provides more functionality by allowing each [BatchItem] to have their own transform
      85             : /// rotation and color.
      86             : ///
      87             : /// By collecting all the necessary transforms on a single image and sending those transforms
      88             : /// in a single batch to the GPU, we can render multiple parts of a single image at once.
      89             : ///
      90             : /// **Note**: Currently web does not support `Canvas.drawAtlas`, which SpriteBatch uses under
      91             : /// the hood, instead it will render each [BatchItem] using `Canvas.drawImageRect`, so there
      92             : /// might be a performance hit on web when working with many batch items.
      93             : class SpriteBatch {
      94             :   /// List of all the existing batch items.
      95             :   final _batchItems = <BatchItem>[];
      96             : 
      97             :   /// The sources to use on the [atlas].
      98             :   final _sources = <Rect>[];
      99             : 
     100             :   /// The sources list shouldn't be modified directly, that is why an
     101             :   /// [UnmodifiableListView] is used. If you want to add sources use the
     102             :   /// [add] or [addTransform] method.
     103           0 :   UnmodifiableListView<Rect> get sources {
     104           0 :     return UnmodifiableListView<Rect>(_sources);
     105             :   }
     106             : 
     107             :   /// The transforms that should be applied on the [_sources].
     108             :   final _transforms = <RSTransform>[];
     109             : 
     110             :   /// The transforms list shouldn't be modified directly, that is why an
     111             :   /// [UnmodifiableListView] is used. If you want to add transforms use the
     112             :   /// [add] or [addTransform] method.
     113           0 :   UnmodifiableListView<RSTransform> get transforms {
     114           0 :     return UnmodifiableListView<RSTransform>(_transforms);
     115             :   }
     116             : 
     117             :   /// The background color for the [_sources].
     118             :   final _colors = <Color>[];
     119             : 
     120             :   /// The colors list shouldn't be modified directly, that is why an
     121             :   /// [UnmodifiableListView] is used. If you want to add colors use the
     122             :   /// [add] or [addTransform] method.
     123           0 :   UnmodifiableListView<Color> get colors {
     124           0 :     return UnmodifiableListView<Color>(_colors);
     125             :   }
     126             : 
     127             :   /// The atlas used by the [SpriteBatch].
     128             :   final Image atlas;
     129             : 
     130             :   /// The default color, used as a background color for a [BatchItem].
     131             :   final Color defaultColor;
     132             : 
     133             :   /// The default transform, used when a transform was not supplied for a [BatchItem].
     134             :   final RSTransform? defaultTransform;
     135             : 
     136             :   /// The default blend mode, used for blending a batch item.
     137             :   final BlendMode defaultBlendMode;
     138             : 
     139             :   /// The width of the [atlas].
     140           0 :   int get width => atlas.width;
     141             : 
     142             :   /// The height of the [atlas].
     143           0 :   int get height => atlas.height;
     144             : 
     145             :   /// The size of the [atlas].
     146           0 :   Vector2 get size => atlas.size;
     147             : 
     148           0 :   SpriteBatch(
     149             :     this.atlas, {
     150             :     this.defaultColor = const Color(0x00000000),
     151             :     this.defaultBlendMode = BlendMode.srcOver,
     152             :     this.defaultTransform,
     153             :   });
     154             : 
     155             :   /// Takes a path of an image, and optional arguments for the SpriteBatch.
     156             :   ///
     157             :   /// When the [images] is omitted, the global [Flame.images] is used.
     158           0 :   static Future<SpriteBatch> load(
     159             :     String path, {
     160             :     Color defaultColor = const Color(0x00000000),
     161             :     BlendMode defaultBlendMode = BlendMode.srcOver,
     162             :     RSTransform? defaultTransform,
     163             :     Images? images,
     164             :   }) async {
     165           0 :     final _images = images ?? Flame.images;
     166           0 :     return SpriteBatch(
     167           0 :       await _images.load(path),
     168             :       defaultColor: defaultColor,
     169           0 :       defaultTransform: defaultTransform ?? RSTransform(1, 0, 0, 0),
     170             :       defaultBlendMode: defaultBlendMode,
     171             :     );
     172             :   }
     173             : 
     174             :   /// Add a new batch item using a RSTransform.
     175             :   ///
     176             :   /// The [source] parameter is the source location on the [atlas].
     177             :   ///
     178             :   /// You can position, rotate and scale it on the canvas using the [transform] parameter.
     179             :   ///
     180             :   /// The [color] parameter allows you to render a color behind the batch item, as a background color.
     181             :   ///
     182             :   /// The [add] method may be a simpler way to add a batch item to the batch. However,
     183             :   /// if there is a way to factor out the computations of the sine and cosine of the
     184             :   /// rotation so that they can be reused over multiple calls to this constructor,
     185             :   /// it may be more efficient to directly use this method instead.
     186           0 :   void addTransform({
     187             :     required Rect source,
     188             :     RSTransform? transform,
     189             :     Color? color,
     190             :   }) {
     191           0 :     final batchItem = BatchItem(
     192             :       source: source,
     193           0 :       transform: transform ??= defaultTransform ?? RSTransform(1, 0, 0, 0),
     194           0 :       color: color ?? defaultColor,
     195             :     );
     196             : 
     197           0 :     _batchItems.add(batchItem);
     198             : 
     199           0 :     _sources.add(batchItem.source);
     200           0 :     _transforms.add(batchItem.transform);
     201           0 :     _colors.add(batchItem.color);
     202             :   }
     203             : 
     204             :   /// Add a new batch item.
     205             :   ///
     206             :   /// The [source] parameter is the source location on the [atlas]. You can position it
     207             :   /// on the canvas using the [offset] parameter.
     208             :   ///
     209             :   /// You can transform the sprite from its [offset] using [scale], [rotation] and [anchor].
     210             :   ///
     211             :   /// The [color] paramater allows you to render a color behind the batch item, as a background color.
     212             :   ///
     213             :   /// This method creates a new [RSTransform] based on the given transform arguments. If many [RSTransform] objects are being
     214             :   /// created and there is a way to factor out the computations of the sine and cosine of the rotation
     215             :   /// (which are computed each time this method is called) and reuse them over multiple [RSTransform] objects,
     216             :   /// it may be more efficient to directly use the more direct [addTransform] method instead.
     217           0 :   void add({
     218             :     required Rect source,
     219             :     double scale = 1.0,
     220             :     Vector2? anchor,
     221             :     double rotation = 0,
     222             :     Vector2? offset,
     223             :     Color? color,
     224             :   }) {
     225           0 :     anchor ??= Vector2.zero();
     226           0 :     offset ??= Vector2.zero();
     227             :     RSTransform? transform;
     228             : 
     229             :     // If any of the transform arguments is different from the defaults,
     230             :     // then we create one. This is to prevent unnecessary computations
     231             :     // of the sine and cosine of the rotation.
     232           0 :     if (scale != 1.0 ||
     233           0 :         anchor != Vector2.zero() ||
     234           0 :         rotation != 0 ||
     235           0 :         offset != Vector2.zero()) {
     236           0 :       transform = RSTransform.fromComponents(
     237             :         scale: scale,
     238           0 :         anchorX: anchor.x,
     239           0 :         anchorY: anchor.y,
     240             :         rotation: rotation,
     241           0 :         translateX: offset.x,
     242           0 :         translateY: offset.y,
     243             :       );
     244             :     }
     245             : 
     246           0 :     addTransform(source: source, transform: transform, color: color);
     247             :   }
     248             : 
     249             :   /// Clear the SpriteBatch so it can be reused.
     250           0 :   void clear() {
     251           0 :     _sources.clear();
     252           0 :     _transforms.clear();
     253           0 :     _colors.clear();
     254           0 :     _batchItems.clear();
     255             :   }
     256             : 
     257           0 :   void render(
     258             :     Canvas canvas, {
     259             :     BlendMode? blendMode,
     260             :     Rect? cullRect,
     261             :     Paint? paint,
     262             :   }) {
     263           0 :     paint ??= Paint();
     264             : 
     265             :     if (kIsWeb) {
     266           0 :       for (final batchItem in _batchItems) {
     267           0 :         paint..blendMode = blendMode ?? paint.blendMode;
     268             : 
     269             :         canvas
     270           0 :           ..save()
     271           0 :           ..transform(batchItem.matrix.storage)
     272           0 :           ..drawRect(batchItem.destination, batchItem.paint)
     273           0 :           ..drawImageRect(
     274           0 :             atlas,
     275           0 :             batchItem.source,
     276           0 :             batchItem.destination,
     277             :             paint,
     278             :           )
     279           0 :           ..restore();
     280             :       }
     281             :     } else {
     282           0 :       canvas.drawAtlas(
     283           0 :         atlas,
     284           0 :         _transforms,
     285           0 :         _sources,
     286           0 :         _colors,
     287           0 :         blendMode ?? defaultBlendMode,
     288             :         cullRect,
     289             :         paint,
     290             :       );
     291             :     }
     292             :   }
     293             : }

Generated by: LCOV version 1.15