4.3 KiB
title | description | guide_group | order | platform_choice |
---|---|---|---|---|
Animated Images | How to add animated image resources to a project in the APNG format, and display them in your app. | app-resources | 0 | true |
The Pebble SDK allows animated images to be played inside an app using the
GBitmapSequence
API, which takes APNG
images as input files. APNG files are similar to well-known .gif
files, which
are not supported directly but can be converted to APNG.
A similar effect can be achieved with multiple image resources, a
BitmapLayer
and an AppTimer
, but would require a lot more code. The
GBitmapSequence
API handles the reading, decompression, and frame
duration/count automatically.
Converting GIF to APNG
A .gif
file can be converted to the APNG .png
format with
gif2apng and the -z0
flag:
./gif2apng -z0 animation.gif
Note: The file extension must be
.png
, not.apng
.
Adding an APNG
{% platform local %}
Include the APNG file in the resources
array in package.json
as a raw
resource:
"resources": {
"media": [
{
"type":"raw",
"name":"ANIMATION",
"file":"images/animation.png"
}
]
}
{% endplatform %}
{% platform cloudpebble %} To add the APNG file as a raw resource, click 'Add New' in the Resources section of the sidebar, and set the 'Resource Type' as 'raw binary blob'. {% endplatform %}
Displaying APNG Frames
The GBitmapSequence
will use a GBitmap
as a container and update its
contents each time a new frame is read from the APNG file. This means that the
first step is to create a blank GBitmap
to be this container.
Declare file-scope variables to hold the data:
static GBitmapSequence *s_sequence;
static GBitmap *s_bitmap;
Load the APNG from resources into the GBitmapSequence
variable, and use the
frame size to create the blank GBitmap
frame container:
// Create sequence
s_sequence = gbitmap_sequence_create_with_resource(RESOURCE_ID_ANIMATION);
// Create blank GBitmap using APNG frame size
GSize frame_size = gbitmap_sequence_get_bitmap_size(s_sequence);
s_bitmap = gbitmap_create_blank(frame_size, GBitmapFormat8Bit);
Once the app is ready to begin playing the animated image, advance each frame
using an AppTimer
until the end of the sequence is reached. Loading the next
APNG frame is handled for you and written to the container GBitmap
.
Declare a BitmapLayer
variable to display the current frame, and set it up
as described under
{% guide_link app-resources/images#displaying-an-image "Displaying An Image" %}.
static BitmapLayer *s_bitmap_layer;
Create the callback to be used when the AppTimer
has elapsed, and the next
frame should be displayed. This will occur in a loop until there are no more
frames, and gbitmap_sequence_update_bitmap_next_frame()
returns false
:
static void timer_handler(void *context) {
uint32_t next_delay;
// Advance to the next APNG frame, and get the delay for this frame
if(gbitmap_sequence_update_bitmap_next_frame(s_sequence, s_bitmap, &next_delay)) {
// Set the new frame into the BitmapLayer
bitmap_layer_set_bitmap(s_bitmap_layer, s_bitmap);
layer_mark_dirty(bitmap_layer_get_layer(s_bitmap_layer));
// Timer for that frame's delay
app_timer_register(next_delay, timer_handler, NULL);
}
}
When appropriate, schedule the first frame advance with an AppTimer
:
uint32_t first_delay_ms = 10;
// Schedule a timer to advance the first frame
app_timer_register(first_delay_ms, timer_handler, NULL);
When the app exits or the resource is no longer required, destroy the
GBitmapSequence
and the container GBitmap
:
gbitmap_sequence_destroy(s_sequence);
gbitmap_destroy(s_bitmap);