Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
sdf_text.hpp
Go to the documentation of this file.
1#pragma once
2
3// Shared SDF/MSDF text rendering options and pen-position helpers.
4//
5// These live in a dedicated header (not sdf_atlas.hpp) because both the
6// single-channel SdfAtlas path and the multi-channel MsdfAtlas path share
7// the same sampler uniforms and the same pen-snapping policy. Keeping them
8// here avoids a circular include between the atlas headers and the canvas
9// text entry points.
10
11#include <cmath>
12#include <string>
13#include <vector>
14
15namespace pulp::canvas {
16
17// Snapping policy for fractional pen positions in animated UIs.
18//
19// Free — use the floating-point pen position directly. Gives
20// the smoothest motion; may shimmer at sub-pixel scales.
21// Nearest — round pen x/y to the nearest integer device pixel.
22// Crisp at rest, visible quantization when animating.
23// SubpixelThird — round pen x to the nearest 1/3 px (y to integer).
24// Works for LCD subpixel output where the atlas is
25// sampled in an R-G-B stripe order.
31
32// Tunables shared by SDF and MSDF samplers. Mirrors the uniforms in
33// core/canvas/shaders/sdf_text.sksl and core/canvas/shaders/msdf_text.sksl
34// so both CPU-side draw calls and SkSL shaders agree on the contract.
36 // Distance value marking the glyph edge. 0.5 for an SDF atlas whose
37 // texels store distance mapped linearly to [0, 1] with 0.5 on the
38 // edge — the convention produced by SdfAtlas.
39 float edge = 0.5f;
40
41 // Extra edge softening added on top of fwidth-derived AA width.
42 // 0 = crispest. Small positive values (0.02..0.05) suit glow prep.
43 float softness = 0.0f;
44
45 // Pixel-scale bias applied to the AA width. Positive values
46 // (0.25..1.0) reduce shimmer at very small sizes. Negative values
47 // (-0.5..0) sharpen at extreme zoom. 0 is the default.
48 float mip_bias = 0.0f;
49
50 // Output gamma. 1.0 = linear (correct on linear framebuffers),
51 // 2.2 ≈ sRGB perceptual correction for 8-bit targets.
52 float gamma = 2.2f;
53
54 // Pen snapping policy. See SdfPenSnap for semantics.
56};
57
58// Apply the pen-snapping policy to a fractional pen position. The
59// returned coordinate is the "effective" pen position that glyph quads
60// should be built from. Separated from the canvas because demos, tests,
61// and SDF/MSDF call sites all share the same rule.
62inline float snap_pen_x(float x, SdfPenSnap policy) {
63 switch (policy) {
65 return x;
67 return std::round(x);
69 return std::round(x * 3.0f) / 3.0f;
70 }
71 return x;
72}
73
74inline float snap_pen_y(float y, SdfPenSnap policy) {
75 // y is always snapped to integer device pixels if any snapping is
76 // requested — vertical antialiasing does not benefit from subpixel
77 // offsets on standard LCDs (stripe order is horizontal).
78 return policy == SdfPenSnap::Free ? y : std::round(y);
79}
80
81// ---------------------------------------------------------------------
82// Quad-building helpers
83// ---------------------------------------------------------------------
84//
85// `fill_text_sdf()` and `fill_text_msdf()` produce the draw-call geometry
86// (one textured quad per glyph) for a text run against a prebuilt atlas.
87// Keeping these as pure helpers — independent of the concrete `Canvas`
88// backend — means unit tests can exercise layout without a GPU context,
89// and adapters (Skia, CoreGraphics) consume the same output through
90// their own textured-quad path.
91
93 // Destination rectangle in device pixels.
94 float dst_x = 0.0f, dst_y = 0.0f, dst_w = 0.0f, dst_h = 0.0f;
95 // Source rectangle in atlas pixels (top-left origin).
96 float src_x = 0.0f, src_y = 0.0f, src_w = 0.0f, src_h = 0.0f;
97 char32_t codepoint = 0;
98};
99
100// Walk `text` against `atlas`, advancing the pen and emitting one
101// `SdfTextQuad` per glyph. Skips codepoints missing from the atlas
102// (callers should prebuild with the full required set).
103//
104// `AtlasT` must expose: `glyph(c) → const Glyph*`, `base_size()`, and
105// `Glyph` must carry atlas_x/atlas_y/width/height/bearing_x/bearing_y/advance.
106// `SdfAtlas`, `MsdfAtlas`, and `PsdfAtlas` satisfy this structural contract
107// without further adaptation; `PsdfAtlas` inherits the contract from
108// `SdfAtlas`.
109template <typename AtlasT>
110inline std::vector<SdfTextQuad> build_text_quads(const AtlasT& atlas,
111 const std::u32string& text,
112 float x, float y,
113 float render_size,
114 const SdfTextOptions& opts = {}) {
115 std::vector<SdfTextQuad> quads;
116 quads.reserve(text.size());
117 const float base = static_cast<float>(atlas.base_size());
118 if (base <= 0.0f) return quads;
119 const float scale = render_size / base;
120
121 float pen_x = snap_pen_x(x, opts.snap);
122 const float pen_y = snap_pen_y(y, opts.snap);
123
124 for (char32_t c : text) {
125 const auto* g = atlas.glyph(c);
126 if (!g) continue;
127 SdfTextQuad q;
128 q.codepoint = c;
129 q.dst_x = snap_pen_x(pen_x + g->bearing_x * scale, opts.snap);
130 q.dst_y = pen_y - g->bearing_y * scale;
131 q.dst_w = g->width * scale;
132 q.dst_h = g->height * scale;
133 q.src_x = static_cast<float>(g->atlas_x);
134 q.src_y = static_cast<float>(g->atlas_y);
135 q.src_w = static_cast<float>(g->width);
136 q.src_h = static_cast<float>(g->height);
137 quads.push_back(q);
138 pen_x += g->advance * scale;
139 }
140 return quads;
141}
142
143// ---------------------------------------------------------------------
144// fill_text_sdf / fill_text_msdf — named entry points
145// ---------------------------------------------------------------------
146//
147// These are thin wrappers around `build_text_quads` that exist so call
148// sites read naturally ("fill_text_msdf(...)") and so the two atlas
149// families have distinct, type-checked entry points even though the
150// quad-building math is shared. Neither touches a GPU context directly;
151// adapters consume the returned quads through their own textured-quad
152// submission path.
153
154// Single templated entry point; callers include the concrete atlas
155// header (`sdf_atlas.hpp`, `msdf_atlas.hpp`, `psdf_atlas.hpp`) before
156// calling. Named wrappers below keep call sites self-documenting while
157// the template below does the actual work.
158template <typename AtlasT>
159inline std::vector<SdfTextQuad>
160fill_text_sdf(const AtlasT& atlas, const std::u32string& text,
161 float x, float y, float render_size,
162 const SdfTextOptions& opts = {}) {
163 return build_text_quads(atlas, text, x, y, render_size, opts);
164}
165
166template <typename AtlasT>
167inline std::vector<SdfTextQuad>
168fill_text_msdf(const AtlasT& atlas, const std::u32string& text,
169 float x, float y, float render_size,
170 const SdfTextOptions& opts = {}) {
171 return build_text_quads(atlas, text, x, y, render_size, opts);
172}
173
174template <typename AtlasT>
175inline std::vector<SdfTextQuad>
176fill_text_psdf(const AtlasT& atlas, const std::u32string& text,
177 float x, float y, float render_size,
178 const SdfTextOptions& opts = {}) {
179 return build_text_quads(atlas, text, x, y, render_size, opts);
180}
181
182} // namespace pulp::canvas
Definition affine_transform.hpp:24
SdfPenSnap
Definition sdf_text.hpp:26
@ Nearest
Definition sdf_text.hpp:28
@ Free
Definition sdf_text.hpp:27
@ SubpixelThird
Definition sdf_text.hpp:29
std::vector< SdfTextQuad > fill_text_msdf(const AtlasT &atlas, const std::u32string &text, float x, float y, float render_size, const SdfTextOptions &opts={})
Definition sdf_text.hpp:168
std::vector< SdfTextQuad > fill_text_sdf(const AtlasT &atlas, const std::u32string &text, float x, float y, float render_size, const SdfTextOptions &opts={})
Definition sdf_text.hpp:160
float snap_pen_y(float y, SdfPenSnap policy)
Definition sdf_text.hpp:74
std::vector< SdfTextQuad > build_text_quads(const AtlasT &atlas, const std::u32string &text, float x, float y, float render_size, const SdfTextOptions &opts={})
Definition sdf_text.hpp:110
@ text
Definition scene_node.hpp:96
std::vector< SdfTextQuad > fill_text_psdf(const AtlasT &atlas, const std::u32string &text, float x, float y, float render_size, const SdfTextOptions &opts={})
Definition sdf_text.hpp:176
float snap_pen_x(float x, SdfPenSnap policy)
Definition sdf_text.hpp:62
Definition sdf_text.hpp:35
float mip_bias
Definition sdf_text.hpp:48
SdfPenSnap snap
Definition sdf_text.hpp:55
float edge
Definition sdf_text.hpp:39
float softness
Definition sdf_text.hpp:43
float gamma
Definition sdf_text.hpp:52
Definition sdf_text.hpp:92
float src_w
Definition sdf_text.hpp:96
char32_t codepoint
Definition sdf_text.hpp:97
float dst_y
Definition sdf_text.hpp:94
float src_y
Definition sdf_text.hpp:96
float dst_w
Definition sdf_text.hpp:94
float dst_x
Definition sdf_text.hpp:94
float src_x
Definition sdf_text.hpp:96
float dst_h
Definition sdf_text.hpp:94
float src_h
Definition sdf_text.hpp:96