-
-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: Adding bleed option to SpriteBatch #3871
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -41,6 +41,7 @@ class BatchItem { | |||||||||||||
| required this.transform, | ||||||||||||||
| Color? color, | ||||||||||||||
| this.flip = false, | ||||||||||||||
| this.bleed = 0, | ||||||||||||||
| }) : color = color ?? const Color(0x00000000), | ||||||||||||||
| paint = Paint()..color = color ?? const Color(0x00000000), | ||||||||||||||
| destination = Offset.zero & source.size; | ||||||||||||||
|
|
@@ -59,6 +60,12 @@ class BatchItem { | |||||||||||||
| /// The flip value for this batch item. | ||||||||||||||
| bool flip; | ||||||||||||||
|
|
||||||||||||||
| /// The bleed value for this batch item in pixels. | ||||||||||||||
| /// When greater than 0, the destination is expanded outward by this amount | ||||||||||||||
| /// while keeping the source sampling region the same, which helps prevent | ||||||||||||||
| /// edge artifacts (seams between tiles in a tilemap). | ||||||||||||||
| double bleed; | ||||||||||||||
|
|
||||||||||||||
| /// The color of the batch item (used for building the drawAtlas color list). | ||||||||||||||
| Color color; | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -305,6 +312,37 @@ class SpriteBatch { | |||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// Computes a transform with bleed applied. | ||||||||||||||
| /// | ||||||||||||||
| /// The bleed expands the destination rectangle outward by the bleed amount | ||||||||||||||
| /// in all directions while keeping the source sampling region unchanged. | ||||||||||||||
| /// This helps prevent edge artifacts (seams between tiles in a tilemap). | ||||||||||||||
| static RSTransform _computeBleedTransform( | ||||||||||||||
| RSTransform transform, | ||||||||||||||
| Rect source, | ||||||||||||||
| double bleed, | ||||||||||||||
| ) { | ||||||||||||||
| if (bleed == 0) { | ||||||||||||||
| return transform; | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
+325
to
+327
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
|
|
||||||||||||||
| // Scale factors for width and height with bleed | ||||||||||||||
| final scaleX = (source.width + bleed * 2) / source.width; | ||||||||||||||
| final scaleY = (source.height + bleed * 2) / source.height; | ||||||||||||||
|
|
||||||||||||||
| // Apply scale to the rotation/scale components | ||||||||||||||
| final scos = transform.scos * scaleX; | ||||||||||||||
| final ssin = transform.ssin * scaleY; | ||||||||||||||
|
Comment on lines
+334
to
+335
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not scale by x or y but by And then the |
||||||||||||||
|
|
||||||||||||||
| // Adjust translation to keep centered after scaling | ||||||||||||||
| // When we scale up, we need to shift back by the bleed amount | ||||||||||||||
| // adjusted for the current scale and rotation | ||||||||||||||
| final tx = transform.tx - bleed * scaleX; | ||||||||||||||
| final ty = transform.ty - bleed * scaleY; | ||||||||||||||
|
|
||||||||||||||
| return RSTransform(scos, ssin, tx, ty); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// Ensures that the given [handle] exists and returns its slot. | ||||||||||||||
| int _requireSlot(int handle) { | ||||||||||||||
| final slot = _handleToSlot[handle]; | ||||||||||||||
|
|
@@ -338,7 +376,13 @@ class SpriteBatch { | |||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| _sources[slot] = _resolveSourceForAtlas(currentBatchItem); | ||||||||||||||
| _transforms[slot] = currentBatchItem.transform; | ||||||||||||||
|
|
||||||||||||||
| // Apply bleed to the updated transform | ||||||||||||||
| _transforms[slot] = _computeBleedTransform( | ||||||||||||||
| currentBatchItem.transform, | ||||||||||||||
| currentBatchItem.source, | ||||||||||||||
| currentBatchItem.bleed, | ||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| // If color is not explicitly provided, store transparent. | ||||||||||||||
| _colors[slot] = color ?? _defaultColor; | ||||||||||||||
|
|
@@ -360,6 +404,11 @@ class SpriteBatch { | |||||||||||||
| /// The [color] parameter allows you to render a color behind the batch item, | ||||||||||||||
| /// as a background color. | ||||||||||||||
| /// | ||||||||||||||
| /// The [bleed] parameter expands the destination rectangle outward by this | ||||||||||||||
| /// amount in all directions while keeping the source sampling region the | ||||||||||||||
| /// same. This helps prevent edge artifacts (seams between tiles in a | ||||||||||||||
| /// tilemap). For best results, the atlas should have padding between sprites. | ||||||||||||||
| /// | ||||||||||||||
| /// 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 | ||||||||||||||
|
|
@@ -370,6 +419,7 @@ class SpriteBatch { | |||||||||||||
| RSTransform? transform, | ||||||||||||||
| bool flip = false, | ||||||||||||||
| Color? color, | ||||||||||||||
| double bleed = 0, | ||||||||||||||
| }) { | ||||||||||||||
| final handle = _allocateHandle(); | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -378,6 +428,7 @@ class SpriteBatch { | |||||||||||||
| transform: transform ??= defaultTransform ?? RSTransform(1, 0, 0, 0), | ||||||||||||||
| flip: flip, | ||||||||||||||
| color: color ?? defaultColor, | ||||||||||||||
| bleed: bleed, | ||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| if (flip && useAtlas && _flippedAtlasStatus.isNone) { | ||||||||||||||
|
|
@@ -391,7 +442,14 @@ class SpriteBatch { | |||||||||||||
|
|
||||||||||||||
| _batchItems.add(batchItem); | ||||||||||||||
| _sources.add(_resolveSourceForAtlas(batchItem)); | ||||||||||||||
| _transforms.add(batchItem.transform); | ||||||||||||||
|
|
||||||||||||||
| // Apply bleed to the transform | ||||||||||||||
| final bleedTransform = _computeBleedTransform( | ||||||||||||||
| batchItem.transform, | ||||||||||||||
| batchItem.source, | ||||||||||||||
| batchItem.bleed, | ||||||||||||||
| ); | ||||||||||||||
| _transforms.add(bleedTransform); | ||||||||||||||
|
|
||||||||||||||
| // If color is not explicitly provided, store transparent. | ||||||||||||||
| _colors.add(color ?? _defaultColor); | ||||||||||||||
|
|
@@ -410,6 +468,11 @@ class SpriteBatch { | |||||||||||||
| /// The [color] parameter allows you to render a color behind the batch item, | ||||||||||||||
| /// as a background color. | ||||||||||||||
| /// | ||||||||||||||
| /// The [bleed] parameter expands the destination rectangle outward by this | ||||||||||||||
| /// amount in all directions while keeping the source sampling region the | ||||||||||||||
| /// same. This helps prevent edge artifacts (seams between tiles in a | ||||||||||||||
| /// tilemap). For best results, the atlas should have padding between sprites. | ||||||||||||||
| /// | ||||||||||||||
| /// 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 | ||||||||||||||
|
|
@@ -425,6 +488,7 @@ class SpriteBatch { | |||||||||||||
| Vector2? offset, | ||||||||||||||
| bool flip = false, | ||||||||||||||
| Color? color, | ||||||||||||||
| double bleed = 0, | ||||||||||||||
| }) { | ||||||||||||||
| anchor ??= Vector2.zero(); | ||||||||||||||
| offset ??= Vector2.zero(); | ||||||||||||||
|
|
@@ -452,6 +516,7 @@ class SpriteBatch { | |||||||||||||
| transform: transform, | ||||||||||||||
| flip: flip, | ||||||||||||||
| color: color, | ||||||||||||||
| bleed: bleed, | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should do an assertion to prevent negative values