diff --git a/platform/Sprite.roc b/platform/Sprite.roc index e7dd837..7e873b4 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. @@ -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. ## ## ``` @@ -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. ## @@ -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 @@ -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"