7.2 KiB
title | author | tags | |
---|---|---|---|
Unifying bitmap resources | katharine |
|
With the upcoming release of firmware 3.8 on Pebble and Pebble Steel, and the associated SDK 3.8, we have decided to redesign how image resources work in Pebble apps.
Why change?
First, some history: when SDK 1 was originally released, and continuing up to
SDK 2.9, there was a single image type, png
. A png
resource would take a PNG
as input and spit out a custom, uncompressed, 1-bit-per-pixel image format we
called "pbi". This was the only bitmap format Pebble supported, and life was
simple.
With the release of SDK 3.0, we added firmware support for a new image format:
PNG (with some restrictions). This enabled Pebble to directly read compressed
images, and those images could be 1-bit, 2-bit, 4-bit or 8-bit palettised. The
existing png
resource type was changed to produce these images instead of the
old PBI format, and everyone had smaller image resources.
Unfortunately, "png" isn't the best option for all cases. The old 1-bit format
supported some compositing operations that other image formats
do not support. We added the pbi
format to achieve this legacy behavior.
Additionally, PNG decompression isn't free: loading a PNG resource requires
enough memory to hold the compressed image, the uncompressed image, and some
scratch space. This was often offset by the benefits of palettised images with
fewer bits per pixel, but sometimes it didn't fit. We added pbi8
, which
produced 8-bit-per-pixel PBI images. This still left it impossible to generate
a palettized PBI, even though the format does exist.
As a further complication, since SDK 1 and continuing through the present day,
pbi
(and pbi8
) images have cropped transparent borders around the outside.
However, png
images (as of SDK 3) do not crop like this. The cropping
behavior was originally a bug, and is generally undesirable, but must be
maintained for backwards compatibility.
There is one additional exception to all of this: until SDK 3.8, the Aplite platform still
interprets png
to mean pbi
. It also interprets pbi8
to mean pbi
. When
we built the 3.8 SDK, we changed png
to really mean png
on Aplite.
Unfortunately, the more limited memory of the Aplite platform meant that these
PNGs sometimes did not have enough space to decompress. The only workaround was
to duplicate resources and use targetPlatforms
to specify a pbi
resource for
Aplite and a png
resource for Basalt and Chalk.
The easiest answer was to keep png
as an alias for pbi
on Aplite—but then
there's no way of generating a real PNG for Aplite. Furthermore, the png
,
pbi
and pbi8
trio was getting confusing, so we decided to do something else.
"bitmap" to the rescue
As of SDK 3.8, png
, pbi
and pbi8
are all deprecated. We are instead
introducing a new resource type, bitmap
. This new resource type unifies all
the existing types, allows the SDK to use its best judgement, increases the
flexibility available to developers, and takes the guesswork out of advanced
image manipulation. It also removes the generally undesirable forced cropping
behavior.
The simplest option is to only specify that you want a bitmap
resource, and
by default the SDK will do the most reasonable thing:
{
"type": "bitmap",
"name": "IMAGE_BERRY_PUNCH",
"file": "images/berry-punch.png"
}
This will create an image with the smallest possible in-memory representation, which will depend on the number of colors. On Aplite, where memory is tight, it will optimize for low memory consumption by creating a pbi. On all other platforms, where there is more memory to spare, it will create a png.
This behavior can be overridden using the following attributes:
memoryFormat
: This determines theGBitmapFormat
that the resulting image will have. The default value isSmallest
, which picks the value that will use the least memory. If you have specific requirements, you can specify:SmallestPalette
,1BitPalette
,2BitPalette
,4BitPalette
,1Bit
or8Bit
. If an image cannot be represented in the requested format, a build error will result. This, for instance, enables palette manipulation with static checking for confidence that you will have an appropriate palette to manipulate.spaceOptimization
: This determines whether we should optimize the image for resource space (storage
) or memory (memory
). The default depends on the memory available on the platform: Aplite defaults tomemory
, while other platforms default tostorage
.storageFormat
: This explicitly states whether an image should be stored on flash as a PNG or pbi image. In most cases you should not specify this, and instead depend onspaceOptimization
. However, if you read resources directly, this option exists to force a value.
So if you want an image that will always have a 2-bit palette and use as little memory as possible, you can do this:
{
"type": "bitmap",
"name": "IMAGE_TIME_TURNER",
"file": "images/time-turner.png",
"memoryFormat": "2BitPalette",
"spaceOptimization": "memory"
}
The resulting image will always have GBitmapFormat2BitPalette
, even if it
could have a 1-bit palette. If it has more than four colors, the build will
fail.
In SDK 3.8-beta8, this would result in a 2-bit palettized pbi. However, this
is not guaranteed: we can change the format in the future, as long as the result
has GBitmapFormat2BitPalette
and we prefer to optimise for memory
consumption where possible.
Finally, note that some combinations are impossible: for instance, a 1Bit
image can never be stored as a PNG, so "storageFormat": "png"
combined with
"memoryFormat": "1Bit"
will be a compile error.
Migration
If you have an existing app, how do you migrate it to use the new bitmap
type?
If you were depending on the pbi
cropping behavior, you will have to manually
crop your image. Beyond that, this table gives the equivalences:
SDK 3.7 type | bitmap specification |
---|---|
png |
{"type": "bitmap"} |
pbi |
{"type": "bitmap", "memoryFormat": "1Bit"} |
pbi8 |
{"type": "bitmap", "storageFormat": "pbi"} |
png-trans
has been left out of this entire exercise. Its behavior is unchanged:
it will produce two pbis with GBitmapFormat1Bit
. However, png-trans
is also
deprecated and discouraged. As of SDK 3.8, all platforms support transparency
in images, and so should use bitmap
instead.
If you have any questions, you can [find us on Discord]({{ site.links.discord_invite }}) or contact us.