6.4 KiB
title | author | tags | |
---|---|---|---|
Displaying remote images in a Pebble app | thomas |
|
A picture is worth a thousand words.
The old adage applies just as well to your Pebble apps! One of the most common requests when we attend hackathons is "How do I transfer an image from the Internet to my Pebble app?".
Today we introduce a new example that demonstrates downloading a PNG file from the Internet and loading it on Pebble. We will also cover how to prepare your images so that they take as little memory as possible and load quickly on Pebble.
The code is available in our pebble-examples github account. Continue reading for more details!
Downloading images to Pebble
The first step to display a remote image is to download it onto the phone. To do
this we built a simple library that can be easily reused: NetDownload. It is
based on PebbleKit JavaScript and
AppMessage
. The implementation is in
pebble-js-app.js
for the JavaScript part and in
netdownload.c
for the C part.
Let's walk through the download process:
- The C application initializes the library with a call to
netdownload_initialize()
. The library in turns initializes the AppMessage framework. - The
show_next_image()
function callsnetdownload_request(char *url)
to initiate a download. This function sends an AppMessage to the JavaScript with two elements. One is the URL to be downloaded and the second one is the maximum transmission size supported by the watch (this is provided byapp_message_inbox_size_maximum()
). - The JavaScript receives this message, tries to download the resource from
the Internet (via
downloadBinaryResource()
) and saves it as byte array. - The image is then split into chunks (based on the maximum transmission size) and sent to the watch. The first message contains the total size of the image so the watchapp can allocate a buffer large enough to receive the entire image. We use the JavaScript success and error callbacks to resend chunks as necessary.
- After the last chunk of data, we send a special message to tell the app that we are done sending. The app can then check that the size matches what was announced and if everything is ok, it calls the download successful callback that was defined when initializing the library.
Loading PNG images on Pebble
Instead of converting images to the native PBI format of Pebble, this example transmits the image in PNG format to the watch and uses a PNG library to decompress the image and to display it on the screen. The PNG library used is uPNG by Sean Middleditch and Lode Vandevenne.
This approach is very convenient for multiple reasons:
- It is often easier to generate PNG images on your server instead of the native PBI format of Pebble.
- PNG images will be smaller and therefore faster to transfer on the network and over Bluetooth.
It does have one drawback though and that is that the PNG library uses approximately 5.5kB of code space.
In the NetDownload callback we receive a pointer to a byte-array and its length.
We simply pass them to gbitmap_create_with_png_data()
(provided by png.h
)
and in return we get a GBitmap
that we can display like any other
GBitmap
structure.
Preparing your images
Because your PNG file will be transferred to Pebble and loaded into the limited memory available on the watch, it is very important to make sure that the PNG is as small as possible and does not contain useless information.
Specifically:
- The image should be 144x168 pixels (fullscreen) or smaller
- It should be in black and white
- It should not contain any metadata (like the author name, gps location of the pictures, etc)
To prepare your image, we recommend using Image Magick (careful! Graphics Magick is a different library and does not support some of the options recommended below, it is important to use Image Magick!)
convert myimage.png \
-adaptive-resize '144x168>' \
-fill '#FFFFFF00' -opaque none \
-type Grayscale -colorspace Gray \
-colors 2 -depth 1 \
-define png:compression-level=9 -define png:compression-strategy=0 \
-define png:exclude-chunk=all \
myimage.pbl.png
Notes:
-fill #FFFFFF00 -opaque none
makes the transparent pixels white-adaptive-resize
with>
at end means resize only if larger, and maintains aspect ratio- we exclude PNG chunks to reduce size (like when image was made, author)
If you want to use dithering to simulate Gray, you can use this command:
convert myimage.png \
-adaptive-resize '144x168>' \
-fill '#FFFFFF00' -opaque none \
-type Grayscale -colorspace Gray \
-black-threshold 30% -white-threshold 70% \
-ordered-dither 2x1 \
-colors 2 -depth 1 \
-define png:compression-level=9 -define png:compression-strategy=0 \
-define png:exclude-chunk=all \
myimage.pbl.png
For more information about PNG on Pebbles, how to optimize memory usage and tips on image processing, please refer to the Advanced techniques videos from the Pebble Developer Retreat 2014.
Connecting the pieces
The main C file (pebble-faces.c
) contains a list of images to load and
everytime you shake your wrist it will load the next one. Take a few minutes to
test this out and maybe this technique will find its way into your next watchface!