The layout calculator functions form the pipeline that transforms rawDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/6xingyv/accompanist-lyrics-ui/llms.txt
Use this file to discover all available pages before exploring further.
KaraokeSyllable data into the fully positioned, animation-ready SyllableLayout objects that the drawing layer consumes. Each function in the pipeline is pure and stateless — they operate on immutable lists and return new lists, making them safe to call from within Compose’s remember / LaunchedEffect blocks.
Pipeline Overview
The four functions must be called in order. Each stage feeds its output directly into the next.measureSyllablesAndDetermineAnimation— InvokeTextMeasurerfor every syllable, cache per-character layouts, and decide whether to use the character-level “awesome” animation or the simpler word-scale animation.calculateBalancedLines— Wrap the flat list ofSyllableLayoutobjects into rows using a dynamic-programming badness minimisation algorithm. ReturnsWrappedLineobjects whose syllables still haveposition = Offset.Zero.calculateStaticLineLayout— Assign realx/ycanvas coordinates to every syllable and fill inwordPivot,wordAnimInfo, andcharOffsetInWord.calculateRowRenderData— Compute bounding rectangles and timing windows for each row so that theCanvasdrawing code can callsaveLayerwith tight bounds.
measureSyllablesAndDetermineAnimation
Measures every syllable with the providedTextMeasurer and determines whether each word qualifies for character-level animation.
Parameters
The ordered list of
KaraokeSyllable objects for the line. Must preserve the original lyric order; timing values are read directly from each item.A
TextMeasurer instance obtained from rememberTextMeasurer(). Expensive to create — pass the one from the composable rather than constructing a new one.The
TextStyle used for the main lyric text. Applied to every textMeasurer.measure() call for syllable content.The
TextStyle used for phonetic annotations (pinyin, romaji, etc.). Applied only when a syllable’s phonetic field is non-null and non-blank.Pass
true for background/accompaniment lines. Accompaniment lines always use the simple word-scale animation regardless of timing, because they are not the focus of the display.The pixel width of a single space character at the current
style. Used to re-add trailing-space width that TextMeasurer may elide when measuring text that ends with whitespace.Returns
List<SyllableLayout> with all fields populated except position, wordPivot, wordAnimInfo, and charOffsetInWord — those are set by later pipeline stages.
Animation mode decision
useAwesomeAnimation is set to true for all syllables in a word when all of the following hold:
| Condition | Value |
|---|---|
| Per-character duration | > 200 ms |
| Total word duration | ≥ 1 000 ms |
| Script | Not CJK, Arabic, or Devanagari |
| Line type | Not an accompaniment line |
useAwesomeAnimation = true, the function also pre-measures charLayouts and charOriginalBounds so the draw phase never has to call TextMeasurer again.
calculateBalancedLines
Wraps a flat list ofSyllableLayout objects into display rows using a dynamic-programming line-breaking algorithm that minimises the sum of squared end-of-line slack (typographic badness).
Parameters
The output of
measureSyllablesAndDetermineAnimation(). Syllables must still be in lyric order.Maximum row width in pixels. Syllables are placed until this limit would be exceeded, then a new row starts.
Used only by the internal
trimDisplayLineTrailingSpaces() helper to re-measure trailing syllables after trailing whitespace is stripped.Passed through to
trimDisplayLineTrailingSpaces().Returns
List<WrappedLine> — each item holds the syllables for one display row and the row’s total rendered width. Syllable positions are not set yet.
Algorithm
The algorithm fills acosts array of size n + 1 where costs[i] is the minimum total badness achievable when breaking the first i syllables optimally. Badness for a line ending at position i starting at position j is:
availableWidthPx), it falls back to calculateGreedyWrappedLines(), which breaks at word boundaries and syllable boundaries when no alternative exists.
calculateStaticLineLayout
Assigns real canvas coordinates to every syllable and populates the word-level animation fields.Parameters
The output of
calculateBalancedLines().When
true, each row starts at canvasWidth − row.totalWidth so that the text is flush with the right edge of the canvas.Full pixel width of the drawing canvas. Used as the reference for right-alignment calculations.
Pixel height of one text row, used to compute the
y origin for each successive row.Pixel height reserved for phonetic annotations. When any syllable in the block carries a phonetic layout, each row’s
y origin is shifted down by phoneticHeight × 0.7 and an equivalent gap is added above the first row.When
true, syllables are placed right-to-left within each row’s start offset, so the first syllable appears at the rightmost position.Returns
List<List<SyllableLayout>> — the outer list corresponds to rows; the inner list to syllables within each row. Every SyllableLayout in the returned structure has its position, wordPivot, wordAnimInfo, and charOffsetInWord fields fully populated.
Layout logic
- Y coordinates:
rowTopY = lineIndex × rowHeight + phoneticOffset. Within a row, syllables are additionally offset bymaxBaselineInLine − syllable.firstBaselineso that their text baselines are aligned even when font sizes differ. - X coordinates (LTR):
currentXstarts atstartXand advances bysyllable.widthafter each syllable. - X coordinates (RTL):
currentXstarts atstartX + row.totalWidthand retreats bysyllable.widthbefore placing each syllable. - wordPivot:
Offset(x = (minX + maxX) / 2, y = bottomY)computed across all syllables sharing awordId. - wordAnimInfo: Created once per word (only when
useAwesomeAnimation = true) and attached to every syllable in that word.
calculateRowRenderData
Computes per-row bounding rectangles and timing windows used by the canvas drawing phase.Parameters
The output of
calculateStaticLineLayout() — syllables with fully populated positions.When
true, phonetic annotation heights are included in the layerBounds calculation so that the saveLayer call covers phonetic text.Display density (
Density.density) used to convert the padding values from dp to pixels.Extra padding in dp added to the top and bottom of
layerBounds. Defaults to 8f. Increase this if glow effects at the row edges are being clipped.Returns
List<RowRenderData> — one entry per non-empty row. See RowRenderData for field documentation.
Padding calculation
saveLayer bounds.
groupIntoWords
Groups a flat syllable list into word-sized sub-lists by detecting word boundaries.Ordered list of
KaraokeSyllable objects for a single line.content string contains trailing whitespace — that is, content.trimEnd().length < content.length. This mirrors the convention used in most karaoke lyric formats where a space after a syllable indicates a word boundary.
groupIntoWords is called internally by measureSyllablesAndDetermineAnimation and does not need to be called directly in normal usage.shouldUseSimpleAnimation (String extension)
true if the receiver string (stripped of whitespace and punctuation) consists entirely of CJK characters or contains any Arabic or Devanagari characters. Used by measureSyllablesAndDetermineAnimation to force useAwesomeAnimation = false for scripts where per-character bounce animations are visually inappropriate.