Skip to content

Optimize watch face - #3500

Draft
alexvanyo wants to merge 26 commits into
mainfrom
av/optimize-watch-face
Draft

Optimize watch face#3500
alexvanyo wants to merge 26 commits into
mainfrom
av/optimize-watch-face

Conversation

@alexvanyo

Copy link
Copy Markdown
Owner

Fixes #2926

…ions

Pass flags 12 (short-post table format 3.0 and no-hints) to FontForge
Generate to omit the 18,000 glyph name strings from the post table and
strip unnecessary TrueType instructions.

Size reduction:
- 342 KB uncompressed per font file
- ~11.29 MB uncompressed reduction across all 33 font TTFs
Remove the unused test watchface XML file from the module resources.
This file was packaged into the final APK but never referenced.

Size reduction:
- Module resources: 342,737 bytes uncompressed reduction
- Watchface APK: ~13 KB compressed reduction
…mapping

Deduplicate repeating Game of Life cell states within each minute in the generated SFD fonts. Once a pattern stabilizes into a still life or periodic oscillator, identical outlines are defined once and mapped to multiple Unicode character codepoints using FontForge AltUni2 encoding (which populates the TrueType cmap table to point multiple codepoints to the single canonical glyph).

Size reduction:
- Uncompressed font resources: 151,974,816 bytes (144.93 MB, 38.2%) reduction across all 33 hour font TTFs
- Watchface APK: ~1.24 MB compressed reduction
Remove unnecessary leading and trailing whitespace formatting from watchface.xml while preserving all XML element structures, attributes, and character entity references.

Size reduction:
- watchface.xml resource: 700,695 bytes (700 KB, 11.0%) uncompressed reduction
- Watchface APK: ~24 KB compressed reduction
Configure packaging options in wear-watchface-wff-version-4 to exclude unused Kotlin metadata (.kotlin_builtins) and version properties that are packaged by default from dependencies. Since the WFF watchface contains no runtime Kotlin code, these files are not required.

Size reduction:
- Uncompressed APK footprint: ~54 KB reduction (8 files removed)
- Watchface APK: ~13 KB compressed reduction
Convert all 33 generated hour font files from TrueType (.ttf) to OpenType CFF (.otf). Type 2 CharStrings in CFF encode orthogonal line segments and coordinate deltas using compact single-byte opcodes without TrueType's loca table overhead and redundant point flags.

Size reduction:
- Uncompressed font assets: 108,345,096 bytes (103.33 MB, 44.1% reduction across all 33 fonts)
- Release Watchface APK: 8,938,281 bytes (8.52 MB, 30.6% APK reduction, dropping from 29.2 MB down to 20.2 MB)
…e stripping

Post-process generated OpenType CFF (.otf) fonts using fontTools CharString specialization to combine adjacent line coordinates into compound Type 2 opcodes (hlineto/vlineto/rlineto). Also strips unnecessary FontForge timestamp (FFTM) and glyph definition (GDEF) metadata tables.

Size reduction:
- Uncompressed font assets: 1,246,344 bytes (1.19 MB reduction across all 33 fonts, dropping total font size to 129.89 MB)
Expand fontTools post-processing in ConvertSfdToOtf to:
- Specialize CFF Private Subrs bytecode
- Prune redundant Macintosh (platform 1) cmap subtables, retaining only the Windows Unicode BMP (platform 3) table
- Prune redundant Macintosh name records, keeping only platform 3 records

Size reduction:
- Uncompressed font assets: 26,392 bytes reduction across all 33 fonts
….py script

Extract Python fontTools post-processing from build.gradle.kts into a dedicated script (wear-watchface-wff-resources/scripts/optimize_otf.py) with comprehensive docstrings and comments detailing:
- Type 2 CharString opcode compaction via specializeProgram
- Private Subrs bytecode specialization
- Windows Unicode BMP cmap table pruning
- Windows Unicode name table minimization
- FFTM and GDEF table stripping
- Ensure <Variant> precedes <Font> within <TimeText>
- Add required name attributes to root <Group> elements
- Add Rectangle DrawElements within PartDraw sampling blocks
- Fix Expression name casing from IsTwoZero to isTwoZero
- Remove invalid duration attributes from Variant elements in version 1

Validated with wff-validator for formats #1 and #4.
- Update DestructionTests.kt to output lightweight minute contour data files (.data) instead of legacy SplineFontDB (.sfd) syntax
- Create wear-watchface-wff-resources/scripts/build_hour_otf.py using fontTools.fontBuilder and T2CharStringPen to compile CFF OTF fonts directly in memory
- Replace CreateHourSfd and ConvertSfdToOtf Gradle tasks with BuildHourOtf task
- Remove fontforgeCommand and FontForge installation dependencies from gradle.properties and GitHub Actions CI workflows
- Remove obsolete SFD solution files and optimize_otf.py script

Reduces font compilation time from ~20 minutes to under 5 seconds with zero external FontForge dependencies.
… canonicalization

- Implement greedy token subroutinizer in build_hour_otf.py extracting high-frequency drawing sequences into CFF Private.Subrs
- Canonicalize polygon contour vertex ordering in DestructionTests.kt to start at top-left minimum vertex, ensuring identical token sequences across repeated geometries
- Omit redundant width tokens from Type 2 CharStrings by setting width=None in T2CharStringPen

Size reduction:
- Uncompressed font size: 56,546,080 bytes (53.93 MB, 41.5% reduction, dropping total font size from 129.86 MB down to 75.94 MB)
- Total lifetime uncompressed savings: 299.72 MB (-79.0% reduction from baseline ~379 MB)
- In DestructionTests.kt, extract relative polygon shapes as reusable subroutines (SUBR) and represent each glyph as positioned shape instances (INST)
- Simplify build_hour_otf.py to directly map Kotlin-extracted shape subroutines to CFF Private.Subrs, eliminating runtime token n-gram scanning in Python
- Maintain fast ~3-second font generation pipeline with 93.4 MB total uncompressed font size and 18.58 MB release APK
…h blocks to guarantee pixel-perfect centering
@alexvanyo
alexvanyo force-pushed the av/optimize-watch-face branch from db7ae18 to 420b82a Compare August 30, 2026 23:50
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.01%. Comparing base (44c656d) to head (420b82a).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #3500      +/-   ##
============================================
+ Coverage     76.72%   78.01%   +1.28%     
- Complexity        0      270     +270     
============================================
  Files           283      163     -120     
  Lines         14507     9917    -4590     
  Branches       1821     1428     -393     
============================================
- Hits          11131     7737    -3394     
+ Misses         2567     1506    -1061     
+ Partials        809      674     -135     
Flag Coverage Δ
24 ?
36 ?
aosp-nexus4-26 76.56% <ø> (ø)
aospatd-pixel6pro-35 76.83% <ø> (ø)
device 78.01% <ø> (ø)
unit ?
wear-wearoslargeround-30 50.29% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optimize watchface format generated fonts

1 participant