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

Nine slice API #47

Merged
merged 1 commit into from
Jan 23, 2024
Merged

Nine slice API #47

merged 1 commit into from
Jan 23, 2024

Conversation

Nycto
Copy link
Collaborator

@Nycto Nycto commented Jan 20, 2024

This adds an API for rendering a nine slice. The API, in general, looks like this:

# Create a nine slice instance. This does a bit of pre-calculation to make rendering faster
let nineSlice = playdate.graphics.newBitmap("nineslice.png").newNineSlice()

# Render the nine slice to a bitmap
var img = playdate.graphics.newBitmap(width, height, kColorWhite)
discard img.setBitmapMask()
img.draw(nineSlice)

A few things worth noting:

  • Currently, there is no way to control the size of the segments for the nine slice input. It just divides the image into 3 equal rows and columns.
  • There is no "stretch" rendering, there is only a "repeat" pattern mode
  • There is no way to control the x and y offsets when drawing, nor the height and width of the 9 slice. It just draws to the entire bitmap it's given.

How the precalculation works

In terms of the precalculation that is done, imagine you've got a nine slice with the following pixels set:

A B C D E F
G H I J K L
M N O P Q R
S T U V W X
Y Z 0 1 2 3
4 5 6 7 8 9

The precalculator will split the image into 9 parts:

A B     C D     E F
G H     I J     K L

M N     O P     Q R
S T     U V     W X

Y Z     0 1     2 3
4 5     6 7     8 9

Because the playdate draws images in rows of uint8s, the precalculator will fill in the left and middle sections of the rows so that everything is at an even 8 bits. So it becomes:

A B C D C D C D     C D C D C D C D     E F
G H I J I J I J     I J I J I J I J     K L

M N O P O P O P     O P O P O P O P     Q R
S T U V U V U V     U V U V U V U V     W X

Y Z 0 1 0 1 0 1     0 1 0 1 0 1 0 1     2 3
4 5 6 7 6 7 6 7     6 7 6 7 6 7 6 7     8 9

This means that as we're drawing, we don't have to do any bit twiddling to draw the left and middle columns. We can just copy the full uint8 all at once for the majority of the row.

Once we reach the right column, though, we can't do that. We don't know how wide the image is, so we have to do some render-time calculations to make that work.

@samdze
Copy link
Owner

samdze commented Jan 20, 2024

I feel this is a good starting point for a nine slice bitmap implementation.
Should be good when the size of the nine slice is known not to change during runtime.
in the future we could maybe add a LCDBitmap subclass or utilities to ease dynamic size nine slices.

A note on file naming, I think we should stick with nineslice.nim (or nine_slice.nim, but that would apply when the name is not very readable without underscores, this is not the case)

src/playdate/nineSlice.nim Outdated Show resolved Hide resolved
src/playdate/nineSlice.nim Outdated Show resolved Hide resolved
@Nycto
Copy link
Collaborator Author

Nycto commented Jan 20, 2024

in the future we could maybe add a LCDBitmap subclass or utilities to ease dynamic size nine slices.

My thought on supporting dynamic drawing was that we could add x: int = 0, y: int = 0, width: int = source.width, height: int = source.height parameters to the draw proc. This should make it backwards compatible with the existing API.

The implementation itself would need to do more bit twiddling to get the alignment working. This could be implemented in two ways:

  • Use the bit overlap calculation proc that the right column is currently using.
  • If that’s not fast enough, we could precalculate the bit twiddling for all 8 offsets. More memory, but not much

A note on file naming, I think we should stick with nineslice.nim (or nine_slice.nim, but that would apply when the name is not very readable without underscores, this is not the case)

Can do!

@Nycto
Copy link
Collaborator Author

Nycto commented Jan 20, 2024

Updated with the requested changes. I also converted a few procs to funcs and renamed asNineSlice to newNineSlice.

@samdze
Copy link
Owner

samdze commented Jan 21, 2024

Looks good! Last things:

  1. t_nineSlice.nim -> t_nineslice.nim
  2. Use underscores instead of dashes in images filenames? (not sure, dashes are ok looking at the Inside Playdate document)

@ninovanhooff
Copy link
Collaborator

ninovanhooff commented Jan 21, 2024

Bit late to the party maybe, but here are my 2 cents:

  • Great addition, will be of use to many games for dialogs and textboxes!
  • maybe it's cleaner to have the playdate/api export just the stuff that's in the official Playdate Api as described in the Panic docs, and expose nineslice under playdate/nineslice. This naturally brings me to:
  • Make sure it's documented somewhere for developers to use. Can't rely on the Panic docs for this one.
  • My understanding of the build process is not complete enough to judge whether this already the case, but please make sure the compiler doesn't bundle this into projects that don't use it.
  • In case code does get bundled into projects that don't use it, maybe a separate github project + nimble package may be more appropriate. Like the Corelibs in the lua api.
  • thanks for adding tests :-)

@Nycto
Copy link
Collaborator Author

Nycto commented Jan 21, 2024

Can’t believe I missed renaming that other file. I’ll get that changed. Good catch.

Nim definitely has dead code elimination, so unused procs and structs won’t show up if they aren’t referenced.

The documentation question is a good one. There are a few options:

  1. Add it to the readme. This feels like it would get bulky and out of sync.
  2. Match the existing play date APIs exactly. But this would mean implementing all the same functionality right now, and the functions could never diverge. This probably wouldn’t be terrible
  3. Set up a GitHub site with hosted docs. This would require setup within the repo, though. This seems like the best option, imo.

@samdze, do you have a preference? Are you game to setting up a GitHub hosted doc site? If so, I can cut an issue to coordinate. I’ve got a GitHub workflow that can handle auto publishing using Nims doc generator.

@Nycto
Copy link
Collaborator Author

Nycto commented Jan 21, 2024

For docs, I preemptively opened #48 so you can see what it would look like. Feel free to close that PR if you would rather take another path.

@samdze samdze merged commit ae4d576 into samdze:main Jan 23, 2024
2 checks passed
@samdze
Copy link
Owner

samdze commented Jan 23, 2024

Nice work! Thanks!

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.

3 participants