Skip to content
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

Make sub-sprites first class #11

Merged
merged 3 commits into from
Jan 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 59 additions & 32 deletions platform/Sprite.roc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
interface Sprite
exposes [Sprite, new, blit, blitSub]
exposes [Sprite, SubRegion, new, blit, sub, subOrCrash]
imports [InternalTask, Task.{ Task }, Effect.{ Effect }]

## Represents a [sprite](https://en.wikipedia.org/wiki/Sprite_(computer_graphics)) for drawing to the screen.
Expand All @@ -10,10 +10,13 @@ interface Sprite
Sprite := {
data : List U8,
bpp : [BPP1, BPP2],
width : U32,
height : U32,
stride : U32,
region : SubRegion,
}

## A subregion of a [Sprite]
SubRegion : { srcX : U32, srcY : U32, width : U32, height : U32 }

## Create a [Sprite] to be drawn or [blit](https://en.wikipedia.org/wiki/Bit_blit) to the screen.
##
## ```
Expand All @@ -32,7 +35,18 @@ new :
height : U32,
}
-> Sprite
new = @Sprite
new = \{ data, bpp, width, height } ->
@Sprite {
data,
bpp,
stride: width,
region: {
srcX: 0,
srcY: 0,
width,
height,
},
}

## Draw a [Sprite] to the framebuffer.
##
Expand All @@ -42,7 +56,8 @@ new = @Sprite
##
## [Refer w4 docs for more information](https://wasm4.org/docs/reference/functions#blit-spriteptr-x-y-width-height-flags)
blit : Sprite, { x : I32, y : I32, flags ? List [FlipX, FlipY, Rotate] } -> Task {} []
blit = \@Sprite { data, bpp, width, height }, { x, y, flags ? [] } ->
blit = \@Sprite { data, bpp, stride, region }, { x, y, flags ? [] } ->
{ srcX, srcY, width, height } = region

format =
when bpp is
Expand All @@ -56,40 +71,52 @@ blit = \@Sprite { data, bpp, width, height }, { x, y, flags ? [] } ->
FlipY -> Num.bitwiseOr state 4
Rotate -> Num.bitwiseOr state 8

Effect.blit data x y width height combined
Effect.blitSub data x y width height srcX srcY stride combined
|> Effect.map Ok
|> InternalTask.fromEffect

## Draw a sub-region of a larger [Sprite] to the framebuffer. Similar to [blit] but with additional parameters.
## Creates a [Sprite] referencing a subregion of the current [Sprite].
## This will return an error if the subregion does not fit in the current [Sprite].
##
## ```
## {} <- Sprite.blitSub {
## x: 0,
## y: 0,
## srcX: 0,
## srcY: 0,
## width: 8,
## height: 8,
## flags: [FlipX, Rotate],
## } |> Task.await
## subSpriteResult = Sprite.sub sprite { srcX: 20, srcY: 0, width: 20, height: 20 }
## ```
##
## [Refer w4 docs for more information](https://wasm4.org/docs/reference/functions#blitsub-spriteptr-x-y-width-height-srcx-srcy-stride-flags)
blitSub : Sprite, { x : I32, y : I32, srcX : U32, srcY : U32, width : U32, height : U32, flags ? List [FlipX, FlipY, Rotate] } -> Task {} []
blitSub = \@Sprite { data, bpp, width: stride }, { x, y, srcX, srcY, width, height, flags ? [] } ->
## Note: If your program should never generate an invalid subregion,
## [subOrCrash] enables avoiding the result and simpler code.
##
sub : Sprite, SubRegion -> Result Sprite [OutOfBounds]
sub = \@Sprite sprite, subRegion ->
currentRegion = sprite.region

format =
when bpp is
BPP1 -> 0
BPP2 -> 1
outOfBoundX = subRegion.srcX + subRegion.width > currentRegion.width
outOfBoundY = subRegion.srcY + subRegion.height > currentRegion.height

combined =
List.walk flags format \state, flag ->
when flag is
FlipX -> Num.bitwiseOr state 2
FlipY -> Num.bitwiseOr state 4
Rotate -> Num.bitwiseOr state 8
if outOfBoundX || outOfBoundY then
Err OutOfBounds
else
newRegion = {
srcX: currentRegion.srcX + subRegion.srcX,
srcY: currentRegion.srcY + subRegion.srcY,
width: subRegion.width,
height: subRegion.height,
}
Ok (@Sprite { sprite & region: newRegion })

Effect.blitSub data x y width height srcX srcY stride combined
|> Effect.map Ok
|> InternalTask.fromEffect
## Equivalent to the [sub] function, but will crash on error.
## This is really useful for static sprite sheet data that needs subSprites extracted.
##
## ```
## subSprite = Sprite.subOrCrash sprite { srcX: 20, srcY: 0, width: 20, height: 20 }
## ```
##
## Warning: Will crash if the subregion is not contained within the sprite.
##
subOrCrash : Sprite, SubRegion -> Sprite
subOrCrash = \sprite, subRegion ->
when sub sprite subRegion is
Ok x ->
x

Err OutOfBounds ->
crash "Out of bounds subregion when generating subsprite"