Scale sprite in C SDK

Hi everyone,

I was playing around with sprites and wanted to make a simple animation where I scale few sprites. Unfortunately, to my surprise, the sprite scaling is only available in Lua but is not available in C SDK. This seems a bit like an odd choice so I wanted to ask whether there is a reason for that? Or, perhaps, I am missing something and sprite scaling is available but I can't quite find it?

If it's indeed not available then I think it would make a great new feature for C SDK.

Cheers,
Robert

Hello, just want to bump the thread as I still wonder why is this not available in the C SDK.

I never used Lua on the Playdate so I cannot comment on what is missing or not.

In C there is drawScaledBitmap if you need to scale images at rendering time:

In general you will get better performance by generating the differently scaled images outside of runtime.

If you need more specific operations, look into the bitmap format, it's not complex at all! You can manipulate it easily.

For example, here's a very simple bitmap resize: bmp-resize/resize.c at master · tgstern/bmp-resize · GitHub

Hi @whatever, first of all, I appreciate your answer! This doesn't exactly solve the problem I am facing. Sprite and bitmap is something different, and I am reusing same bitmap for multiple sprites. To give an example use case which might make it slightly easier to understand imagine having a player's health as health containers. Let's assume there are 3 of these, all look exactly same. So, I load single bitmap of the heart image and then create 3 sprites using this one bitmap. Now, let's say I want to animate one of the heart containers by scaling it's size to 0. I can't really do that using the mentioned function because it would scale the source bitmap thus "animating" all 3 of the containers not just one.

This is why I specifically ask about sprite scaling rather than bitmap scaling.

What's your thought on that?

Hopefully I'm understanding your example correctly. Here's how I would do it with the Sprite API.

Assuming you are using updateAndDrawSprites to render Sprites and you want to render 3 heart images, and one of them a different size (because it's bliking).
All the images are loaded in memory and available as LCDBitmap.

Set a custom draw function with setDrawFunction for each Sprite, instead of using updateAndDrawSprites.
For each Sprite you have, keep a value of its desired scale.
In the custom draw function, call drawScaledBitmap to render with the appropriate scale. You can get the current Sprite image with getImage.

Or you could use sprite->setImage to set a scaled image instead of the regular sprite image and render using the regular updateAndDrawSprites call.

I haven't thought about using custom draw function for a sprite actually, this might work! I'll give it a try just a bit and will get back to confirm. Thanks!

Alright, so I gave it a run and unfortunately something seems to be not quite right. First let me show a minimal example I made for this:

PlaydateAPI* localPD = NULL;
void customSpriteDraw(LCDSprite* sprite, PDRect bounds, PDRect drawRect)
{
	// `getImage` returns `NULL`.
    LCDBitmap* bitmap = localPD->sprite->getImage(sprite);
    if (bitmap != NULL)
    {
        localPD->graphics->drawScaledBitmap(bitmap, drawRect.x, drawRect.y, 0.5f, 0.5f);
    }
}

LCDSprite* createSprite(PlaydateAPI* pd, LCDBitmap* bitmap)
{
	localPD = pd;
	int width, height;
	pd->graphics->getBitmapData(bitmap, &width, &height, NULL, NULL, NULL);
	LCDSprite* newSprite = pd->sprite->newSprite();
	pd->sprite->setImage(newSprite, bitmap, kBitmapUnflipped);
	// according to docs these two need to be explicitly set, not sure if 
	// this is true as I got the customSpriteDraw function called even without setting them. 
	// I assume that `setImage` function do it internally.
	pd->sprite->setSize(newSprite, width, height);
	pd->sprite->setBounds(newSprite, (PDRect){0, 0, width, height});
	pd->sprite->setDrawFunction(newSprite,  customSpriteDraw);
	
	// if I call `getImage` here it returns valid bitmap.
	// LCDBitmap* spriteBitmap = pd->sprite->getImage(newSprite);
	return newSprite;
}

In the example above, whenever I try to read the image from a sprite inside the custom draw function, the getImage function returns NULL. If I call this function right after the sprite is created it returns valid pointer to a bitmap. Not sure why would this be happening. As soon as I comment custom draw function everything works just fine so I don't think there is any issue on freeing bitmap or something on my side.

I really wish the SDK code was open source so we could properly debug stuff like this instead of guessing :confused:

Let me know if you can think of anything that might be happening and causing this behavior.

Okay I just realized that rotating sprites is also not available in the sprite namespace API. There is drawRotatedBitmap which could be used same way @whatever suggested in a custom draw function but I don't really understand why this isn't simply part of the sprite API.

Can anyone from Panic please clarify what is the intention here and whether this has been omitted on purpose or not?

Ok, I played around with this a bit.

It looks like when you set setDrawFunction for a sprite, you must not use sprite->setImage anymore but draw what you want in the custom draw function. (getImage is set to NULL on each frame, as you observed)

Using drawScaledBitmap in the custom function limits the draw area to the sprite's bound. You need to change the sprite's bounds with setBounds right before drawing it otherwise anything outside of this will be clipped.

I've attached a simple demo.
Left and right arrows change the scale of the 2nd sprite.
resize.zip (127.7 KB)

I think if you don't have a simple use case, it's good to consider writing your own sprite renderer and to not use updateAndDrawSprites. Loop over all your entities/sprites each frame and render them with drawScaledBitmap or drawBitmap however you want.

1 Like

Alright, well, this certainly isn't kind of behavior I would expect. I wonder if this is also happening when custom draw function is set in Lua.

Thanks a lot for an example code, makes things much easier to process.

Yeah I guess given this isn't going to be too complicated to roll my own implementation of "sprite rendering" I might do it. I was just wondering if there is anything else than simply drawBitmap going on under the hood I might be missing when I make my own implementation.

Anyway, I really appreciate your input on this thread and for your help! Cheers!

1 Like

Actually I re-read the thread again and realized that even using drawBitmap functions isn't ideal given that drawScaledBitmap and drawRotatedBitmap are separate functions. This means if you want to draw a bitmap both scaled and rotated you would need to make an intermediate bitmap, set it as context, draw rotated bitmap to it, pop the context and then draw it scaled. This stinks :grimacing: :frowning:

You could probably use the playdate->graphics->rotatedBitmap function. The documentation states:

Returns a new, rotated and scaled LCDBitmap based on the given bitmap.

Then you can just draw this bitmap directly, since this function will return both rotated and scaled bitmap. It is similar in Lua, if you don't use the Sprite system.

Also good idea is to cache the returned bitmap and only return new one if the scale or angle changes.

1 Like

Oh I missed this specific one. I also had another look at the documentation page and noticed that playdate->graphics->drawRotatedBitmap actually takes xscale and yscale as params too, so I guess I could use this one. What surprises me though is the fact it also returns LCDBitmap* but doesn't mention anything about it in the comment to this function. It make me wonder whether it also simply create new bitmap and then user is responsible for storing it and freeing later on.