fix(render): filter the glyph atlas, snap chrome hairlines to pixels
Text was point-sampled (`GL_NEAREST` plus `floor(v_uv * u_tex_size)`), which at a non-integer scale *deletes* texels instead of blending them. The library's own default -- a 12 px style over the 13 px embedded bake -- dropped one of every 8 columns and one of every 17 rows of every glyph, so whole strokes went missing and which ones depended on where the unsnapped pen fell. `kTextFS` now does a 2x2 bilinear fetch clamped to the instance's own cell: the clamp keeps the no-bleed guarantee the sampler mode protects (the atlas is a tight strip of cells with no padding), and at scale 1 with a whole pen the weights are exactly 0, so it stays as crisp as the point sample. Grid and axis lines were staged in data space and mapped in the vertex shader, so a 1 px line's centre landed on an arbitrary fraction -- x = 100.0 lights two columns at half coverage, x = 100.5 lights one crisply, and a frame mixed both. `u_snap` rounds an axis-aligned segment's ink *leading edge* onto a pixel boundary, which reduces to integer+0.5 for odd widths and to an integer for even ones with no case analysis. It is on for the chrome that frames a plot and off for anything drawn at a data position, since the rounding moves a line by up to half a pixel. Diagonals are untouched. `text_pixel`'s threshold drops to 160 because filtered glyphs legitimately peak at 0.844 coverage at 12/13; the 180 threshold found text only while the sampler was also deleting a column and a row of it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
98cfc5805b
commit
525759f9a9
8 files changed
+295
-25
No files matched your search
@@ -40,6 +40,14 @@ class LineProgram {
|
||||
void set_aa(f32 px) const noexcept;
|
||||
/// `u_color`: stroke colour, alpha included.
|
||||
void set_color(Color color) const noexcept;
|
||||
/// `u_snap`: with it on, an **axis-aligned** segment's ink is rounded onto whole
|
||||
/// pixels along the axis it is constant on, so a hairline lights one column (or
|
||||
/// row) fully instead of two at half coverage. It is for the chrome that frames a
|
||||
/// plot -- grid lines, axis lines, panel and legend borders. Anything that reports
|
||||
/// a data position -- a trace, a cursor, a region edge, the crosshair -- leaves it
|
||||
/// off, because the rounding moves a line by up to half a pixel. A diagonal segment
|
||||
/// is untouched whatever this says.
|
||||
void set_snap(bool on) const noexcept;
|
||||
|
||||
private:
|
||||
gl::Program program_;
|
||||
|
||||
Reference in new issue
Block a user