From b3a5a23e7afd2e2050981782946660b806696b1d Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 13 Jan 2024 11:00:23 -0800 Subject: [PATCH 1/3] Make sub-sprites first class This makes using a sprite sheet much much nicer. Just extract N sub-sprites from the sheet and use them exactly like normal sprites. No need to deal with with a separate `blitSub` method or doing the math in many places. Makes animations in rocci-bird from a sprite sheet a lot cleaner overall. --- platform/Sprite.roc | 104 +++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/platform/Sprite.roc b/platform/Sprite.roc index e7dd837..b99482c 100644 --- a/platform/Sprite.roc +++ b/platform/Sprite.roc @@ -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. @@ -12,8 +12,19 @@ Sprite := { bpp : [BPP1, BPP2], width : U32, height : U32, + + # This is data for rendering using blitSub. + # It enables creating a subsprite that can be passed around. + # The subsprite can still be rendered with the regular blit api. + subData : [ + Some SubRegion, + None, + ], } +## 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. ## ## ``` @@ -32,7 +43,14 @@ new : height : U32, } -> Sprite -new = @Sprite +new = \{ data, bpp, width, height } -> + @Sprite { + data, + bpp, + width, + height, + subData: None, + } ## Draw a [Sprite] to the framebuffer. ## @@ -42,7 +60,10 @@ 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 sprite, { x, y, flags ? [] } -> + { data, bpp, width: stride } = sprite + + { srcX, srcY, width, height } = subDataWithDefault sprite format = when bpp is @@ -56,40 +77,63 @@ 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] is enables avoiding the result and simpler code. +## +sub : Sprite, SubRegion -> Result Sprite [OutOfBounds] +sub = \@Sprite sprite, subRegion -> + currentRegion = subDataWithDefault sprite - 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 & subData: Some 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. +## +## ``` +## subSpriteResult = Sprite.sub 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" + +subDataWithDefault = \{ width, height, subData } -> + when subData is + Some data -> data + None -> + { + srcX: 0, + srcY: 0, + width, + height, + } From 97d94aa3cd34d81322766681705dfcc5d2884765 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 13 Jan 2024 11:23:50 -0800 Subject: [PATCH 2/3] Avoid tag, always have a SubRegion --- platform/Sprite.roc | 43 +++++++++++++------------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/platform/Sprite.roc b/platform/Sprite.roc index b99482c..a2ca777 100644 --- a/platform/Sprite.roc +++ b/platform/Sprite.roc @@ -10,16 +10,8 @@ interface Sprite Sprite := { data : List U8, bpp : [BPP1, BPP2], - width : U32, - height : U32, - - # This is data for rendering using blitSub. - # It enables creating a subsprite that can be passed around. - # The subsprite can still be rendered with the regular blit api. - subData : [ - Some SubRegion, - None, - ], + stride : U32, + region : SubRegion, } ## A subregion of a [Sprite] @@ -47,9 +39,13 @@ new = \{ data, bpp, width, height } -> @Sprite { data, bpp, - width, - height, - subData: None, + stride: width, + region: { + srcX: 0, + srcY: 0, + width, + height, + }, } ## Draw a [Sprite] to the framebuffer. @@ -60,10 +56,8 @@ new = \{ data, bpp, width, height } -> ## ## [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 sprite, { x, y, flags ? [] } -> - { data, bpp, width: stride } = sprite - - { srcX, srcY, width, height } = subDataWithDefault sprite +blit = \@Sprite { data, bpp, stride, region }, { x, y, flags ? [] } -> + { srcX, srcY, width, height } = region format = when bpp is @@ -93,7 +87,7 @@ blit = \@Sprite sprite, { x, y, flags ? [] } -> ## sub : Sprite, SubRegion -> Result Sprite [OutOfBounds] sub = \@Sprite sprite, subRegion -> - currentRegion = subDataWithDefault sprite + currentRegion = sprite.region outOfBoundX = subRegion.srcX + subRegion.width > currentRegion.width outOfBoundY = subRegion.srcY + subRegion.height > currentRegion.height @@ -107,7 +101,7 @@ sub = \@Sprite sprite, subRegion -> width: subRegion.width, height: subRegion.height, } - Ok (@Sprite { sprite & subData: Some newRegion }) + Ok (@Sprite { sprite & region: newRegion }) ## Equivalent to the [sub] function, but will crash on error. ## This is really useful for static sprite sheet data that needs subSprites extracted. @@ -126,14 +120,3 @@ subOrCrash = \sprite, subRegion -> Err OutOfBounds -> crash "Out of bounds subregion when generating subsprite" - -subDataWithDefault = \{ width, height, subData } -> - when subData is - Some data -> data - None -> - { - srcX: 0, - srcY: 0, - width, - height, - } From bf6f06fdeb504e1781f839bec1f4eb93bae269c5 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 13 Jan 2024 11:27:58 -0800 Subject: [PATCH 3/3] minor docs cleanup --- platform/Sprite.roc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/Sprite.roc b/platform/Sprite.roc index a2ca777..7e873b4 100644 --- a/platform/Sprite.roc +++ b/platform/Sprite.roc @@ -83,7 +83,7 @@ blit = \@Sprite { data, bpp, stride, region }, { x, y, flags ? [] } -> ## ``` ## ## Note: If your program should never generate an invalid subregion, -## [subOrCrash] is enables avoiding the result and simpler code. +## [subOrCrash] enables avoiding the result and simpler code. ## sub : Sprite, SubRegion -> Result Sprite [OutOfBounds] sub = \@Sprite sprite, subRegion -> @@ -107,7 +107,7 @@ sub = \@Sprite sprite, subRegion -> ## This is really useful for static sprite sheet data that needs subSprites extracted. ## ## ``` -## subSpriteResult = Sprite.sub sprite { srcX: 20, srcY: 0, width: 20, height: 20 } +## subSprite = Sprite.subOrCrash sprite { srcX: 20, srcY: 0, width: 20, height: 20 } ## ``` ## ## Warning: Will crash if the subregion is not contained within the sprite.