Skip to content

A Garmin watch face example using antiliased rounded text and hands

License

Notifications You must be signed in to change notification settings

tomfogg/garmin-roundedtext

Repository files navigation

A Garmin watch face example using antiliased rounded text and hands

screenshot

This is an example watch face created using a truetype font and the included generatefonts.js script to produce antialiased hands and text placed around the bezel of the watch.

This watch face is an example and not meant to be usable. The code included is the minimum required to demonstrate the technique and doesn't include optimisations such as clipping.

Building the watch face

There is a Dockerfile included which will install the Garmin SDK, ImageMagick and NodeJS, generate the fonts, build the watch face and run it in the simulator. If you are on a linux machine with docker and X11, running ./run.sh should do all this for you.

Without docker you will need to install the requirements below, generate the fonts by running the node generatefonts.js commands and build the watch face using your dev setup and the Garmin SDK.

Requirements

  1. A machine that can run NodeJS scripts
  2. ImageMagick which is used to produce the font files compatible with Garmin watches. This is available to install in most Linux distributions
  3. A font (any format acceptable by ImageMagick, eg ttf or otf) (an example font.ttf is included here)
  4. A font editor such as FontForge to draw any symbols you want to use in your watch face, such as the hands

Generating the fonts

  1. Change into the directory where you want to create the font eg resources/fonts
  2. Run the script, referencing the font and the characters you want to include

Usage: node generatefonts.js <font file> <font name> <character size> <list of characters to include> <degrees per font> <radius> <lookups> <lookupstep>

  • character size determines the size of font used

    • 60C means size the font to fit 60 characters into the diameter of the watch face
    • 3L means size the font to fit 3 lines of text in the radius of the watch face
  • degrees per font is the number of degrees to go clockwise on each rotation

    • larger values will use less memory but the angles the characters are at won't fit the circle as well, smaller values will look better but use more memory
  • radius is half with width of the watch face in pixels

  • lookups is the lines of text you are going to use eg: "1 3" means i need to draw text at the outermost ring of the watch face, then two lines in from that

  • lookupstep is the number of degrees to move on per lookup value. if you only need to do angles every 6 degrees (eg clock hands) use a 6 here to save memory, otherwise 1

  • example for a font to be used in text around the edge of the watch in resources/fonts

node generatefonts.js font.ttf font 60C "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 -" 10 120 1 1

  • example for a font to be used for drawing hands on the watchface in resources/hands

node generatefonts.js font.ttf hand 5L "( ) *" 6 120 "1 2 3 4" 6

Using the fonts

Take a look at the code in Hand.mc Font.mc and BezelText.mc which includes functions for using the fonts

Font.mc

  • getHand and getFont which load the correct font for the position at which the hand or letter is being drawn. Since the total size of all the fonts used is quite big, the getFont function includes an LRU cache used to allow the fonts to be loaded and unloaded from memory when memory is low.

BezelText.mc

  • draw draws an antialiased text string around the edge of the watch face

Hand.mc

  • draw draws an antialiased hand at a specified position (0-60) on the watch face

How it works

The script creates antialiased images of each letter using the font file, rotates the letter from -90 to 90 degrees and combines the images of all the letters into a single font image and font file reference per angle. You can see this only equates to 180 degrees rather than 360 in a circle. This is because the bottom half of the watch face will re-use the rotated characters from the top half. Rotating further than 90 degreens would mean the letters would be printed upside down and unreadable.

To make it easy to know where to draw the character on the screen and the correct font file for the angle you want, the script also generates a lookup table of x,y and font file values. The files ResLookupsfontname.mc and ResFontfontname.mc are generated by the script, containing the getLookupsfontname and getFontfontname functions to easily load the fonts and lookups. See Font.mc for where these are used. To allow for variable width fonts a JSON resource with the widths of all the characters, Rez.JsonData.letterwidths_font is generated by the script. See Font.mc and BezelText.mc for where this is used.

For watch hands, the same system is used. The arm parts of a watch hand will work with this arrangement since they will be the same upside down. For the end parts of the arm, where there might be an arrow, the system doesn't work since resuing the top rotations on the bottom means the arrow is still pointing up. To solve this, an extra character is used (with the arrow pointing down) for the end parts in the bottom half of the face. See Hand.draw for how this is handled. In the example the * character is used for the main arm part and ( and ) are used for the up and down arrow parts of the arm.

Limitations

These font files can be quite large and memory size is limited on Garmin devices. For most devices a full set of font files for all the angles will not fit into memory so only some angles can be loaded at a time, which makes redrawing slow. To mitigate this, use as few characters as you can (if none of your UI uses an X, dont include it in the font!), try to use only lower or upper case characters and create a font of the smallest pixel size that works for your design. As long as the font for one particular angle can fit in memory the font can be used but loading fonts is expensive so should be done as little as possible.

You can also reduce the number of font files generated by increasing the degrees per font value used. The fonts will look best if you create 1 font per degree on the circle but you can get away with using, for example, 1 font per 10 degrees which means a character drawn at angles 20 to 30 degrees will use the same font file so the angle of the characters will not be perfect but good enough for a small screen. This can reduce the memory consumption significantly. This tradeoff is more visible for clock hands where you should stick to using one font file per 6 degrees (since there are only 60 positions for a hand on the watch face).

About

A Garmin watch face example using antiliased rounded text and hands

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published