@@ -4026,13 +4026,128 @@ class Canvas extends NativeFieldWrapperClass2 {
40264026 List <dynamic >? paintObjects,
40274027 ByteData paintData) native 'Canvas_drawVertices' ;
40284028
4029- /// Draws part of an image - the [atlas] - onto the canvas.
4029+ /// Draws many parts of an image - the [atlas] - onto the canvas.
4030+ ///
4031+ /// This method allows for optimization when you want to draw many parts of an
4032+ /// image onto the canvas, such as when using sprites or zooming. It is more efficient
4033+ /// than using multiple calls to [drawImageRect] and provides more functionality
4034+ /// to individually transform each image part by a separate rotation or scale and
4035+ /// blend or modulate those parts with a solid color.
4036+ ///
4037+ /// The method takes a list of [Rect] objects that each define a piece of the
4038+ /// [atlas] image to be drawn independently. Each [Rect] is associated with an
4039+ /// [RSTransform] entry in the [transforms] list which defines the location,
4040+ /// rotation, and (uniform) scale with which to draw that portion of the image.
4041+ /// Each [Rect] can also be associated with an optional [Color] which will be
4042+ /// composed with the associated image part using the [blendMode] before blending
4043+ /// the result onto the canvas. The full operation can be broken down as:
4044+ ///
4045+ /// - Blend each rectangular portion of the image specified by an entry in the
4046+ /// [rects] argument with its associated entry in the [colors] list using the
4047+ /// [blendMode] argument (if a color is specified). In this part of the operation,
4048+ /// the image part will be considered the source of the operation and the associated
4049+ /// color will be considered the destination.
4050+ /// - Blend the result from the first step onto the canvas using the translation,
4051+ /// rotation, and scale properties expressed in the associated entry in the
4052+ /// [transforms] list using the properties of the [Paint] object.
4053+ ///
4054+ /// If the first stage of the operation which blends each part of the image with
4055+ /// a color is needed, then both the [colors] and [blendMode] arguments must
4056+ /// not be null and there must be an entry in the [colors] list for each
4057+ /// image part. If that stage is not needed, then the [colors] argument can
4058+ /// be either null or an empty list and the [blendMode] argument may also be null.
4059+ ///
4060+ /// The optional [cullRect] argument can provide an estimate of the bounds of the
4061+ /// coordinates rendered by all components of the atlas to be compared against
4062+ /// the clip to quickly reject the operation if it does not intersect.
4063+ ///
4064+ /// An example usage to render many sprites from a single sprite atlas with no
4065+ /// rotations or scales:
40304066 ///
4031- /// This method allows for optimization when you only want to draw part of an
4032- /// image on the canvas, such as when using sprites or zooming. It is more
4033- /// efficient than using clips or masks directly.
4067+ /// ```dart
4068+ /// class Sprite {
4069+ /// int index;
4070+ /// double centerX;
4071+ /// double centerY;
4072+ /// }
40344073 ///
4035- /// All parameters must not be null.
4074+ /// class MyPainter extends CustomPainter {
4075+ /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
4076+ /// ui.Image spriteAtlas;
4077+ /// List<Sprite> allSprites;
4078+ ///
4079+ /// @override
4080+ /// void paint(Canvas canvas, Size size) {
4081+ /// Paint paint = Paint();
4082+ /// canvas.drawAtlas(spriteAtlas, <RSTransform>[
4083+ /// for (Sprite sprite in allSprites)
4084+ /// RSTransform.fromComponents(
4085+ /// rotation: 0.0,
4086+ /// scale: 1.0,
4087+ /// // Center of the sprite relative to its rect
4088+ /// anchorX: 5.0,
4089+ /// anchorY: 5.0,
4090+ /// // Location at which to draw the center of the sprite
4091+ /// translateX: sprite.centerX,
4092+ /// translateY: sprite.centerY,
4093+ /// ),
4094+ /// ], <Rect>[
4095+ /// for (Sprite sprite in allSprites)
4096+ /// Rect.fromLTWH(sprite.index * 10.0, 0.0, 10.0, 10.0),
4097+ /// ], null, null, null, paint);
4098+ /// }
4099+ ///
4100+ /// ...
4101+ /// }
4102+ /// ```
4103+ ///
4104+ /// Another example usage which renders sprites with an optional opacity and rotation:
4105+ ///
4106+ /// ```dart
4107+ /// class Sprite {
4108+ /// int index;
4109+ /// double centerX;
4110+ /// double centerY;
4111+ /// int alpha;
4112+ /// double rotation;
4113+ /// }
4114+ ///
4115+ /// class MyPainter extends CustomPainter {
4116+ /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
4117+ /// ui.Image spriteAtlas;
4118+ /// List<Sprite> allSprites;
4119+ ///
4120+ /// @override
4121+ /// void paint(Canvas canvas, Size size) {
4122+ /// Paint paint = Paint();
4123+ /// canvas.drawAtlas(spriteAtlas, <RSTransform>[
4124+ /// for (Sprite sprite in allSprites)
4125+ /// RSTransform.fromComponents(
4126+ /// rotation: sprite.rotation,
4127+ /// scale: 1.0,
4128+ /// // Center of the sprite relative to its rect
4129+ /// anchorX: 5.0,
4130+ /// anchorY: 5.0,
4131+ /// // Location at which to draw the center of the sprite
4132+ /// translateX: sprite.centerX,
4133+ /// translateY: sprite.centerY,
4134+ /// ),
4135+ /// ], <Rect>[
4136+ /// for (Sprite sprite in allSprites)
4137+ /// Rect.fromLTWH(sprite.index * 10.0, 0.0, 10.0, 10.0),
4138+ /// ], <Color>[
4139+ /// for (Sprite sprite in allSprites)
4140+ /// Color.white.withAlpha(sprite.alpha),
4141+ /// ], BlendMode.srcIn, null, paint);
4142+ /// }
4143+ ///
4144+ /// ...
4145+ /// }
4146+ /// ```
4147+ ///
4148+ /// The length of the [transforms] and [rects] lists must be equal and
4149+ /// if the [colors] argument is not null then it must either be empty or
4150+ /// have the same length as the other two lists.
40364151 ///
40374152 /// See also:
40384153 ///
@@ -4041,22 +4156,21 @@ class Canvas extends NativeFieldWrapperClass2 {
40414156 void drawAtlas (Image atlas,
40424157 List <RSTransform > transforms,
40434158 List <Rect > rects,
4044- List <Color > colors,
4045- BlendMode blendMode,
4159+ List <Color >? colors,
4160+ BlendMode ? blendMode,
40464161 Rect ? cullRect,
40474162 Paint paint) {
40484163 // ignore: unnecessary_null_comparison
40494164 assert (atlas != null ); // atlas is checked on the engine side
40504165 assert (transforms != null ); // ignore: unnecessary_null_comparison
40514166 assert (rects != null ); // ignore: unnecessary_null_comparison
4052- assert (colors != null ); // ignore: unnecessary_null_comparison
4053- assert (blendMode != null ); // ignore: unnecessary_null_comparison
4167+ assert (colors == null || colors.isEmpty || blendMode != null );
40544168 assert (paint != null ); // ignore: unnecessary_null_comparison
40554169
40564170 final int rectCount = rects.length;
40574171 if (transforms.length != rectCount)
40584172 throw ArgumentError ('"transforms" and "rects" lengths must match.' );
4059- if (colors.isNotEmpty && colors.length != rectCount)
4173+ if (colors != null && colors .isNotEmpty && colors.length != rectCount)
40604174 throw ArgumentError ('If non-null, "colors" length must match that of "transforms" and "rects".' );
40614175
40624176 final Float32List rstTransformBuffer = Float32List (rectCount * 4 );
@@ -4080,20 +4194,27 @@ class Canvas extends NativeFieldWrapperClass2 {
40804194 rectBuffer[index3] = rect.bottom;
40814195 }
40824196
4083- final Int32List ? colorBuffer = colors.isEmpty ? null : _encodeColorList (colors);
4197+ final Int32List ? colorBuffer = ( colors == null || colors .isEmpty) ? null : _encodeColorList (colors);
40844198 final Float32List ? cullRectBuffer = cullRect? ._value32;
40854199
40864200 _drawAtlas (
40874201 paint._objects, paint._data, atlas, rstTransformBuffer, rectBuffer,
4088- colorBuffer, blendMode.index, cullRectBuffer
4202+ colorBuffer, ( blendMode ?? BlendMode .src) .index, cullRectBuffer
40894203 );
40904204 }
40914205
4092- /// Draws part of an image - the [atlas] - onto the canvas.
4206+ /// Draws many parts of an image - the [atlas] - onto the canvas.
4207+ ///
4208+ /// This method allows for optimization when you want to draw many parts of an
4209+ /// image onto the canvas, such as when using sprites or zooming. It is more efficient
4210+ /// than using multiple calls to [drawImageRect] and provides more functionality
4211+ /// to individually transform each image part by a separate rotation or scale and
4212+ /// blend or modulate those parts with a solid color. It is also more efficient
4213+ /// than [drawAtlas] as the data in the arguments is already packed in a format
4214+ /// that can be directly used by the rendering code.
40934215 ///
4094- /// This method allows for optimization when you only want to draw part of an
4095- /// image on the canvas, such as when using sprites or zooming. It is more
4096- /// efficient than using clips or masks directly.
4216+ /// A full description of how this method uses its arguments to draw onto the
4217+ /// canvas can be found in the description of the [drawAtlas] method.
40974218 ///
40984219 /// The [rstTransforms] argument is interpreted as a list of four-tuples, with
40994220 /// each tuple being ([RSTransform.scos] , [RSTransform.ssin] ,
@@ -4103,7 +4224,121 @@ class Canvas extends NativeFieldWrapperClass2 {
41034224 /// tuple being ([Rect.left] , [Rect.top] , [Rect.right] , [Rect.bottom] ).
41044225 ///
41054226 /// The [colors] argument, which can be null, is interpreted as a list of
4106- /// 32-bit colors, with the same packing as [Color.value] .
4227+ /// 32-bit colors, with the same packing as [Color.value] . If the [colors]
4228+ /// argument is not null then the [blendMode] argument must also not be null.
4229+ ///
4230+ /// An example usage to render many sprites from a single sprite atlas with no rotations
4231+ /// or scales:
4232+ ///
4233+ /// ```dart
4234+ /// class Sprite {
4235+ /// int index;
4236+ /// double centerX;
4237+ /// double centerY;
4238+ /// }
4239+ ///
4240+ /// class MyPainter extends CustomPainter {
4241+ /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
4242+ /// ui.Image spriteAtlas;
4243+ /// List<Sprite> allSprites;
4244+ ///
4245+ /// @override
4246+ /// void paint(Canvas canvas, Size size) {
4247+ /// // For best advantage, these lists should be cached and only specific
4248+ /// // entries updated when the sprite information changes. This code is
4249+ /// // illustrative of how to set up the data and not a recommendation for
4250+ /// // optimal usage.
4251+ /// Float32List rectList = Float32List(allSprites.length * 4);
4252+ /// Float32List transformList = Float32List(allSprites.length * 4);
4253+ /// for (int i = 0; i < allSprites.length; i++) {
4254+ /// final double rectX = sprite.spriteIndex * 10.0;
4255+ /// rectList[i * 4 + 0] = rectX;
4256+ /// rectList[i * 4 + 1] = 0.0;
4257+ /// rectList[i * 4 + 2] = rectX + 10.0;
4258+ /// rectList[i * 4 + 3] = 10.0;
4259+ ///
4260+ /// // This example sets the RSTransform values directly for a common case of no
4261+ /// // rotations or scales and just a translation to position the atlas entry. For
4262+ /// // more complicated transforms one could use the RSTransform class to compute
4263+ /// // the necessary values or do the same math directly.
4264+ /// transformList[i * 4 + 0] = 1.0;
4265+ /// transformList[i * 4 + 1] = 0.0;
4266+ /// transformList[i * 4 + 2] = sprite.centerX - 5.0;
4267+ /// transformList[i * 4 + 2] = sprite.centerY - 5.0;
4268+ /// }
4269+ /// Paint paint = Paint();
4270+ /// canvas.drawAtlas(spriteAtlas, transformList, rectList, null, null, null, paint);
4271+ /// }
4272+ ///
4273+ /// ...
4274+ /// }
4275+ /// ```
4276+ ///
4277+ /// Another example usage which renders sprites with an optional opacity and rotation:
4278+ ///
4279+ /// ```dart
4280+ /// class Sprite {
4281+ /// int index;
4282+ /// double centerX;
4283+ /// double centerY;
4284+ /// int alpha;
4285+ /// double rotation;
4286+ /// }
4287+ ///
4288+ /// class MyPainter extends CustomPainter {
4289+ /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
4290+ /// ui.Image spriteAtlas;
4291+ /// List<Sprite> allSprites;
4292+ ///
4293+ /// @override
4294+ /// void paint(Canvas canvas, Size size) {
4295+ /// // For best advantage, these lists should be cached and only specific
4296+ /// // entries updated when the sprite information changes. This code is
4297+ /// // illustrative of how to set up the data and not a recommendation for
4298+ /// // optimal usage.
4299+ /// Float32List rectList = Float32List(allSprites.length * 4);
4300+ /// Float32List transformList = Float32List(allSprites.length * 4);
4301+ /// Int32List colorList = Int32List(allSprites.length);
4302+ /// for (int i = 0; i < allSprites.length; i++) {
4303+ /// final double rectX = sprite.spriteIndex * 10.0;
4304+ /// rectList[i * 4 + 0] = rectX;
4305+ /// rectList[i * 4 + 1] = 0.0;
4306+ /// rectList[i * 4 + 2] = rectX + 10.0;
4307+ /// rectList[i * 4 + 3] = 10.0;
4308+ ///
4309+ /// // This example uses an RSTransform object to compute the necessary values for
4310+ /// // the transform using a factory helper method because the sprites contain
4311+ /// // rotation values which are not trivial to work with. But if the math for the
4312+ /// // values falls out from other calculations on the sprites then the values could
4313+ /// // possibly be generated directly from the sprite update code.
4314+ /// final RSTransform transform = RSTransform.fromComponents(
4315+ /// rotation: sprite.rotation,
4316+ /// scale: 1.0,
4317+ /// // Center of the sprite relative to its rect
4318+ /// anchorX: 5.0,
4319+ /// anchorY: 5.0,
4320+ /// // Location at which to draw the center of the sprite
4321+ /// translateX: sprite.centerX,
4322+ /// translateY: sprite.centerY,
4323+ /// );
4324+ /// transformList[i * 4 + 0] = transform.scos;
4325+ /// transformList[i * 4 + 1] = transform.ssin;
4326+ /// transformList[i * 4 + 2] = transform.tx;
4327+ /// transformList[i * 4 + 2] = transform.ty;
4328+ ///
4329+ /// // This example computes the color value directly, but one could also compute
4330+ /// // an actual Color object and use its Color.value getter for the same result.
4331+ /// // Since we are using BlendMode.srcIn, only the alpha component matters for
4332+ /// // these colors which makes this a simple shift operation.
4333+ /// colorList[i] = sprite.alpha << 24;
4334+ /// }
4335+ /// Paint paint = Paint();
4336+ /// canvas.drawAtlas(spriteAtlas, transformList, rectList, colorList, BlendMode.srcIn, null, paint);
4337+ /// }
4338+ ///
4339+ /// ...
4340+ /// }
4341+ /// ```
41074342 ///
41084343 /// See also:
41094344 ///
@@ -4112,29 +4347,28 @@ class Canvas extends NativeFieldWrapperClass2 {
41124347 void drawRawAtlas (Image atlas,
41134348 Float32List rstTransforms,
41144349 Float32List rects,
4115- Int32List colors,
4116- BlendMode blendMode,
4350+ Int32List ? colors,
4351+ BlendMode ? blendMode,
41174352 Rect ? cullRect,
41184353 Paint paint) {
41194354 // ignore: unnecessary_null_comparison
41204355 assert (atlas != null ); // atlas is checked on the engine side
41214356 assert (rstTransforms != null ); // ignore: unnecessary_null_comparison
41224357 assert (rects != null ); // ignore: unnecessary_null_comparison
4123- assert (colors != null ); // ignore: unnecessary_null_comparison
4124- assert (blendMode != null ); // ignore: unnecessary_null_comparison
4358+ assert (colors == null || blendMode != null );
41254359 assert (paint != null ); // ignore: unnecessary_null_comparison
41264360
41274361 final int rectCount = rects.length;
41284362 if (rstTransforms.length != rectCount)
41294363 throw ArgumentError ('"rstTransforms" and "rects" lengths must match.' );
41304364 if (rectCount % 4 != 0 )
41314365 throw ArgumentError ('"rstTransforms" and "rects" lengths must be a multiple of four.' );
4132- if (colors.length * 4 != rectCount)
4366+ if (colors != null && colors .length * 4 != rectCount)
41334367 throw ArgumentError ('If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".' );
41344368
41354369 _drawAtlas (
41364370 paint._objects, paint._data, atlas, rstTransforms, rects,
4137- colors, blendMode.index, cullRect? ._value32
4371+ colors, ( blendMode ?? BlendMode .src) .index, cullRect? ._value32
41384372 );
41394373 }
41404374
0 commit comments