Skip to content

feat: Adding bleed option to SpriteBatch#3871

Merged
spydon merged 16 commits into
mainfrom
feat/bleed-in-spritebatch
May 10, 2026
Merged

feat: Adding bleed option to SpriteBatch#3871
spydon merged 16 commits into
mainfrom
feat/bleed-in-spritebatch

Conversation

@erickzanardo
Copy link
Copy Markdown
Member

@erickzanardo erickzanardo commented Mar 23, 2026

Description

Adds a bleed parameter to SpriteBatch.add() and SpriteBatch.addTransform() that slightly expands the rendered sprite beyond its source boundaries in all directions. This prevents seam artifacts (visible lines between tiles) that appear when texture filtering samples neighbouring pixels at tile edges in tilemaps.

Atlas path (useAtlas = true, default): applies a uniform scale to the RSTransform via _computeBleedTransform, keeping the sprite centre fixed in world space. A uniform scale using max(bleedScaleX, bleedScaleY) is used to preserve rotation; for non-square source rects the shorter axis is scaled slightly beyond the requested bleed value.

Non-atlas path (useAtlas = false / web fallback): expands BatchItem.destination by bleed pixels in every direction, always exactly bleed pixels on every side regardless of aspect ratio.

For best results, the atlas should include padding between sprites.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

Addresses seam/ghost-line artifacts commonly reported when using SpriteBatch for tilemaps.

@erickzanardo erickzanardo marked this pull request as draft March 23, 2026 00:13
Comment thread packages/flame/lib/src/sprite_batch.dart
Comment on lines +325 to +327
if (bleed == 0) {
return transform;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (bleed == 0) {
return transform;
}
if (bleed <= 0) {
return transform;
}

Comment on lines +334 to +335
final scos = transform.scos * scaleX;
final ssin = transform.ssin * scaleY;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not scale by x or y but by math.max of either so we can preserve rotation (and ensure scale happens uniformly even if width and height are not equal).

And then the tx and ty should be multiplied by the scos and ssin (subtract for tx, addition for ty). That should keep the centerr fixed to bleed across the borders when we are dealing with rotation

@spydon
Copy link
Copy Markdown
Member

spydon commented May 8, 2026

Any update on this @erickzanardo ?

spydon and others added 11 commits May 9, 2026 13:49
…ng path

Bleed was only applied via _computeBleedTransform for the drawAtlas path.
The non-atlas (web fallback) path used batchItem.destination which was
always set to Offset.zero & source.size, ignoring bleed entirely.

Now BatchItem expands its destination rect by the bleed amount in each
direction when bleed > 0, so drawImageRect in the fallback path also
renders the sprite slightly larger to prevent edge seams. The replace()
method is updated to recalculate destination when the source changes.
- Zero-size source guard in _computeBleedTransform to prevent Infinity/NaN
- Convert BatchItem.matrix lazy field to nullable+getter so replace() can
  invalidate it when source or transform changes, fixing stale matrix bug
  in the non-atlas render path
- Use original source rect (not bleed-expanded destination) for the
  background color drawRect in the non-atlas path, matching atlas path
  behaviour
- Fix wording of transform/rotation comment in _computeBleedTransform
- Document the non-square uniform-scale trade-off in bleed docstrings
- Document that bleed cannot be changed via replace()
- Add tests: negative bleed assertion, non-square atlas scale behaviour,
  non-square non-atlas exact expansion, zero-size source guard, matrix
  invalidation after replace(transform) and replace(source)
- Add CHANGELOG entry
- Fill in PR description and checklist
- Fix trailing whitespace in example description string
All 115 golden files were regenerated in commit d54a82b ("Update
goldens") using a different Flutter version than main, producing
pixel-identical renders stored as different PNG binaries. Only
sprite_batch_test_3.png belongs to this PR.
Replace the ??= form with an explicit null check and local variable so
dart format keeps the structure readable rather than wrapping after ??=.
…yout

Splitting the constructor call and the cascade mutations into two
statements lets dart format keep `final result = Matrix4(` on one line
instead of breaking before Matrix4. Also renames the local variable from
the abbreviated `m` to `result`.
The 16 positional doubles are now arranged as 4x4 rows matching the
matrix layout, guarded by dart format off/on to prevent the formatter
from expanding them to one-per-line.
@spydon spydon marked this pull request as ready for review May 10, 2026 17:01
@spydon spydon requested a review from wolfenrain May 10, 2026 17:06
@spydon spydon merged commit 2c4a6d5 into main May 10, 2026
8 checks passed
@spydon spydon deleted the feat/bleed-in-spritebatch branch May 10, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants