bombsimon / garmin-seaside

⌚️ Seaside - A minimalistic Garmin Watch Face
https://apps.garmin.com/en-US/apps/fb6f331c-ced6-46bb-afc5-c44abeb973b8
MIT License
2 stars 0 forks source link

Better font support #2

Closed bombsimon closed 2 months ago

bombsimon commented 8 months ago

Tracking issue for a few font improvements

MrJacquers commented 7 months ago

Hiero - libGDX

https://garmin.watchfacebuilder.com/bitmap-font-online-generator/

I generally load the ttf file from my PC. It has some nice options to change the width, x advance, etc. and the preview helps.

You can use css e.g. to make an outline font:

background-color: #000; color: transparent; //-webkit-text-fill-color: transparent; -webkit-text-stroke: 3px white;

Another useful tool is FontForge where you can make your own font by copying glyphs from an existing font or importing svg to make icon fonts. You can also select glyphs, set the width, center in width to try and make a monospace font.

MrJacquers commented 7 months ago

Increasing and decreasing can probably done by placing the font files in the correct resource folder. I think the naming is something like resources-round-260x260 and newer devices support vector fonts which can scale.

The original font will depend on the watch, but I think they like using Roboto and Rajdhani. You can also get clues from the simulator files, e.g. C:\Users\Jacques\AppData\Roaming\Garmin\ConnectIQ\Devices\venu2

"digitalFonts": [ "sourceSansPro", "leagueGothic", "notoSansCJKTC", "qumpellkaNO12" ],

bombsimon commented 7 months ago

Wow, thank you so much for this! I've been using BMFont which isn't great so far so I'll give the other tools a go!

My initial plan was to just create resource folders for each resolution but that in itself is pretty cumbersome in my opinion, it requires you to figure out all the available resolutions and generate fonts for them. And if I want to support multiple sizes on the same device I would have to build multiple versions and hook them to the settings.

Given the current way of creating the watch face, by not using layouts, the computation of offsets from everything was pretty time consuming to do this for all the resolutions and font variations. Especially since there doesn't seem to be a way to get a font's width, only its height.

Since the watch face consists of 4 different font sizes:

The permutation is pretty big, giving me at least 4 size per resolution and 8 if I want one big and one small version.

I was planning to change to Oswald and already started to compile the fonts for all kinds of sizes and I started to try to update the existing code for this but decided to put this on hold. I think I want to revisit this and actually create a proper layout in XML for every resolution and font size but that is more or less a full rewrite of the watch face.

Again, appreciate your input on this, thanks!

MrJacquers commented 7 months ago

No problem, hope the info helps. I think the vector scalable fonts will solve most of these issues, but that's only for newer and more expensive devices. I don't see much of a way around it atm. Maybe a script to autogenerate the fonts will make it a bit easier.

You can get the dimensions of the text. Depending on how the glyphs were saved you'll probably get a bit of extra height and width which is easy to see if you draw the text with a different background color. Maybe a propelry monospaced font can help with this.

var textDims = dc.getTextDimensions(timeString, _outlineFont); var textW = textDims[0]; var textH = textDims[1];

MrJacquers commented 7 months ago

btw, thanks for the formula and explanation of where to draw the seconds dot. I don't quite understand it (geometry isn't my strong point) and the Garmin coordinates can be a bit confusing. From playing with drawArc:

What is the license of this project, and would you mind me sharing the formula on a forum, giving you credit of course. I think yours is more accurate and helped me to spot an issue with the one I had copied, which is basically the one in the sdk Analog example.

https://forums.garmin.com/developer/connect-iq/f/discussion/1740/better-code-needed-for-hands-on-watchface

bombsimon commented 7 months ago

What is the license of this project

Oh, completely missed that - thanks! Added LICENSE now, it's MIT.

would you mind me sharing the formula on a forum

Of course not, feel free to share whatever you want! It's up to you if you want to add credit or reference here but don't feel you have to!

Also just to be clear my geometry skills isn't the best either but I remember I just googled it when implementing it and added all the docs to try to remember it in the future 😅

bombsimon commented 7 months ago

Tried to give this a new attempt but feels like everything layout and font related with Garmin is really bad and frustrating to work with. I tried to use Hiero for fonts but couldn't get a good result with anti-aliasing so went back to BMFont. Spent longer than I dare to admit to try to figure out why font size didn't match until I realized you have to select "Match chars height" in font settings. After that I spent too much time to find the proper settings to get best smoothing.

Ended up with these settings (for hours on an 454x454 AMOLED display).

Click to show ```cfg # AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Oswald fontFile= charSet=0 fontSize=-140 aa=2 scaleH=100 useSmoothing=1 isBold=1 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=0 autoFitNumPages=0 autoFitFontSizeMin=0 autoFitFontSizeMax=0 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=0 forceZero=0 widthPaddingFactor=0.00 # output file outWidth=256 outHeight=256 outBitDepth=8 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=48-57,65 # imported icon images ```

I then tried to write a simple layout to get started with using layouts to easier support multiple devices. Ended up with

<layout id="WatchFace">
    <drawable id="BottomArea" class="BottomArea">
        <param name="top">350</param>
    </drawable>

    <label id="HourLabel" x="100" y="100" background="Graphics.COLOR_RED" font="@Fonts.oswaldBold140" justification="Graphics.TEXT_JUSTIFY_LEFT" color="Graphics.COLOR_WHITE" />
    <label id="MinuteLabel" x="0" y="0" font="Graphics.FONT_MEDIUM" justification="Graphics.TEXT_JUSTIFY_LEFT" color="Graphics.COLOR_WHITE" />
    <label id="WeekdayLabel" x="center" y="0" font="Graphics.FONT_SMALL" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_WHITE" />
    <label id="BatteryLabel" x="center" y="0" font="Graphics.FONT_SMALL" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_WHITE" />
    <label id="DateLabel" x="center" y="0" font="Graphics.FONT_SMALL" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_WHITE" />
</layout>

Feels like the x and y coordinates ended up all over the place based on which ones were visible, what font I used etc. After not understanding how coordinates works for way too long I realized that my custom font (different from system fonts) adds this padding that I don't know how to get rid off.

image

Not sure why this is because the font file seems fine

oswald-bold-140_0

I tried adjusting the yoffset in the .fnt file but it only allows me to update padding within the area.

This is the system font Graphics.FONT_LARGE renderd at 100,100.

image

The result seems to be the same no matter if I use the label or text-area and update the text or if I use dc.drawText.

Right now I support devices down to API 1.4.0 so I can't really use text-area anyway but would be nice if I could use this layout with label and just put specific font's and update x and y coordinates in the layout. That is after going through all resolutions in this list 🙈

This was a bit of a let down because I was planning (and started) to create an image document with all the resolutions where I could just try and adjust different font sizes and get the coordinates to not have to use trial and error and re-compiling the code every time but seems like that's not going to happen.

I guess I could just not care about the padding and all that stuff and look for coordinates by hand but that will be really cumbersome. Would be nice with some examples of how other developers do this.

I think a more reasonable thing would be to keep using relative offsets (from center or other drawables) but adjust how much the offset is in exact pixels based on the font size and configure that in layout.xml.

Some resources for fonts and sizes

MrJacquers commented 7 months ago

Garmin's layouts, coordinates, fonts, etc. can be challenging to work with because they are not quite standard. The best font builder for me i.t.o. smoothness has been the one from WatchfaceBuilder. It also doesn't leave the extra space on top / bottom. In Hiero the native option had the best results and for BMFont TrueType hinting helped. I've read that on Mac BMGlyph is nice, although not free.

You've probably seen the relative layouts, which may make some things easier. https://forums.garmin.com/developer/connect-iq/b/news-announcements/posts/using-relative-layouts-and-textarea and https://forums.garmin.com/developer/connect-iq/f/discussion/349773/send-relative-coordinates-to-a-customer-drawable-from-layout

For my personal watchface I'm using direct drawing and hardcoded some things. I'm still considering whether to publish it, but there are some extras I need to add, e.g. icons, color customization, complications, etc. I'm probably not going to support older devices and would have to make the coordinates more dynamic. At this stage I'm mostly playing around with ideas just for fun and learning. It's relatively basic, but if you want to have a look it's here: https://github.com/MrJacquers/VenuWatchFace

bombsimon commented 6 months ago

I picked this up and decided to finish the layout route and was thinking I'd just create unique resources with unique coordinates for all resolutions. That was dumb, because using layouts require support to toggle visibility to support the settings such as show/hide battery level and show/hide steps indicator. This is apparently not supported on all supported devices so I'll have to revert all the changes and go back to drawing them manually. I can still use the resource tables to only embed fonts that I need but I'll need to find another way to setup coordinates for each device unless percentages like now won't work.