Terminal text rendering and font fallback
TerminalTextPainter consumes logical cells from the copied render cache.
TerminalBidiLayout places those cells in the same visual columns used by
backgrounds, overlays, cursor geometry, and hit testing. Bidi affects placement
and shaping direction without changing a cell's rendering policy. These types
are internal; hosts configure fonts through SwingSettings and SwingHostServices.
Cell and run dispatch
| Content | Rendering path |
|---|---|
| Compatible LTR ASCII | drawChars when font advances match the grid; otherwise a cached vector with explicit cell positions |
| Contextual scripts / ordinary text in RTL runs | Contextually shaped vectors positioned by terminal-cell ownership |
| Supported box, block, and geometric characters | Programmatic cell primitives |
| Emoji presentation sequences | Available platform rasterizer, then Java2D fallback |
| Emoji bases with VS15 | Text font selection and bounded TextLayout with proportional cell fitting, including in RTL runs |
| Other scalars and clusters | Bounded cached TextLayout fallback |
columnSpacing changes the cell advance rather than the font size. Negative
spacing retains the uncondensed glyph-fitting budget while primitives,
backgrounds, cursor, and hit testing use the adjusted grid. Text can overlap
neighboring cells within a run; existing paint-span clipping still applies.
ASCII batching compares foreground, font style, visibility, decorations, and hyperlink state. Backgrounds are separate runs. ASCII does not invoke contextual shaping or fallback-font searches. Its ink is clipped to the run's cell span; matching advances alone do not prevent italic or antialiased overhang. When the caller clip is already contained, painting leaves it untouched. Otherwise it intersects and restores the exact clip, including nonrectangular shapes.
Ordinary cells and block-cursor foreground share primitive, emoji, and fallback dispatch. A directional character elsewhere in a row does not disable primitives or emoji. Classification precedes access to the lazy emoji rasterizer.
Native emoji image caching
TerminalEmojiImageCache retains up to 1,024 images or unsupported results for one
rasterizer. Identity is code point content plus raster pixel size; equivalent
scalar and single-code-point cluster requests share an entry. Position, color,
and cell span do not affect identity when raster size is unchanged. Lookups use
reusable keys and borrowed cluster slices. Misses snapshot the content and create
text for rasterization. Borrowed slices are released after lookup, and negative
results share the same bounded LRU as images.
The current platform rasterizer reads installed Segoe UI Emoji COLR/CPAL data on Windows. Other platforms use Java2D fallback. Native painting does not depend on JetBrains Runtime detection. Initialization and misses are synchronous: the first qualifying emoji may load and parse the font on the painting thread, normally the EDT. Classification avoids this work for ordinary text but does not move it off-thread. Cache-hit measurements exclude initialization and rasterization.
Contextual shaping and terminal geometry
Shaping spans are constrained by direction, font style, Unicode script, and cell category. Foreground colors, decorations, hover, and concealment determine paint spans without breaking neighboring shaping context.
The run buffer records a terminal-cell owner for each UTF-16 unit, including
surrogate pairs and combining sequences. Font.layoutGlyphVector provides
contextual shaping. The cache maps glyph indices back to those owners and positions
complete glyph groups at their assigned visual cell spans. Combining marks retain
their base-relative offsets; ligatures own the union of consumed cells. Oversized
groups are fitted using each group's glyph-size budget, which retains the original
font width when spacing is condensed. The entire line is not proportionally fitted.
Large vectors retain bounded glyph batches. Uniform paint spans submit the whole vector; partial styles and cursor clips submit only intersecting batches. Batches preserve glyph codes, positions, and transforms and are built on cache insertion. The block cursor paints the same positioned context under its cell clip instead of shaping an isolated character. Concealed or hidden blinking cells still suppress foreground painting.
A shaping window admits at most 2,048 code points / 4,096 UTF-16 units and stops before splitting a terminal cluster. Long spans continue through windows with shaped-cluster lookahead and lookbehind. A cluster occupying the whole budget is consumed; lookbehind is dropped when it would prevent progress. These bounds preserve coverage but cannot promise unbounded contextual equivalence for every font. Oversized individual clusters have a separate fallback bound.
Cached vectors are read-only after positioning. Identity includes text, cell ownership/span, style, direction, cell advance, and glyph-fitting width. Font-source, font-generation, and font-render-context changes invalidate relevant entries. Lookup scratch buffers are reused and belong to the calling painter.
Font resolution
Ordinary text tries the primary font, optional host resolver, configured fallback fonts, then enabled system fallbacks. Emoji presentation prefers the host resolver and configured/system emoji fonts before the ordinary pipeline. Resolved fonts use the configured size. Fallback and unsupported lookups use bounded caches. System scanning is outside the ASCII path.
Native dispatch and font preference share TerminalEmojiPresentation for
scalars, cluster slices, and UTF-16 text. An emoji base is required; a joiner or
variation selector alone does not classify ordinary script text as emoji. VS15
retains text presentation. Its font search skips recognized emoji families,
including primary and host-resolved candidates. Text-font coverage checks ignore
VS15 itself while retaining the selector in the shaping input; the base and other
cluster code points still require coverage. Oversized fallback layouts scale
equally in both dimensions around the text baseline and keep the owning cell clip.
Contextual script groups retain their existing positioning and fitting policy.
Platform font substitution must not give an emoji font precedence over a capable
primary font for ordinary text. Actual glyph coverage and substitution within
logical fonts remain dependent on the installed fonts and JVM text stack.
Allocation measurement boundaries
Component painters and scratch buffers belong to the EDT. Unit tests assert cache reuse/invalidation, glyph geometry, pixels, and publication ordering. Allocation measurements use JMH warmup, forks, and a GC profiler, rather than exact-byte unit-test assertions. Internal helper benchmarks have private frozen frames and graphics; component/controller benchmarks dispatch batches to the EDT and include amortized dispatch cost.
| Benchmark | Boundary |
|---|---|
TerminalTextRenderingBenchmark |
Warm style scanning, shaped lookup, contained-clip ASCII, and clipped glyph batches; direct Java2D controls |
TerminalFontConfigurationBenchmark |
Unchanged configuration and retained unsupported-glyph lookup |
TerminalEmojiBenchmark |
Image/negative cache hits using a recording rasterizer; excludes OS font loading |
TerminalBidiBenchmark |
Cached mapping/projection and overscan frame acceptance |
TerminalRepaintBenchmark |
Search projection and damage planning |
TerminalScrollbarBenchmark |
Overlay painting with direct drawing controls |
TerminalViewportPublicationBenchmark |
EDT primitive publication and callbacks; excludes public snapshot construction |
TerminalSearchRefreshBenchmark |
Unchanged active search refresh with retained history |
SwingPaintBenchmark |
Complete static component paint in EDT batches |
See the benchmark guide for commands and measurement requirements. Use matching glyphs, positions, transforms, clips, and font-render contexts when comparing painter operations.
Complete component painting creates a Graphics2D copy and may project a
selection. Java2D clipping, transforms, and drawing can allocate. Cold font
loading, layout construction, rasterization, cache growth, session publication,
and host callbacks have separate costs. Helper cache-hit measurements do not
establish zero allocation for a complete frame or live-window input latency.
Raster comparisons must preserve antialiased coverage and exact shaping input.
Erase the old cursor region before comparing a repaint; repeated SrcOver
drawing is not idempotent. Keep joiners in Arabic reference text: removing a ZWJ
can change ligature formation. Compare caches with direct shaping of the same
text and painter output with the complete contextual input.