Files
plotter/include/plot/render/program.hpp
T
Martino Ferrari ffc060d6ca refactor(render): the atlas is a declared fixed-cell grid, and staging checks it
The text pipeline stages a glyph's rect *origin* per instance and carries the
rect's *size* once per draw, so a backend whose glyphs have differing rect sizes
-- a TrueType packing trimmed to ink, which is Task 8's shape -- would have drawn
every glyph at whichever size it happened to be told, with nothing downstream
able to tell. Make that constraint structural rather than documented:

- TextBackend::cell_uv() declares the one cell size, and the contract (an atlas
  is a fixed-cell grid; a proportional face draws its glyphs *into* a uniform
  cell instead of trimming each rect to its ink, and why the pipeline needs it)
  is stated on the atlas accessors.
- glyph_is_cell() is the check; add_text()/set_cell() declare the cell from the
  backend, and the staging path asserts it for every glyph in a debug build.
  A release build draws on with the cell it was told, as before.
- Renderer::flush_text() takes u_glyph_uv from the backend, not from the batch,
  so the draw's one rect size can never come from whichever glyph was staged
  first.
2026-09-28 10:09:50 +02:00

69 lines
2.6 KiB
C++

#pragma once
#include <plot/core/color.hpp>
#include <plot/core/status.hpp>
#include <plot/gl/handle.hpp>
#include <plot/math/vec2.hpp>
namespace plot::render {
/// Linked line program and its uniform surface.
///
/// It owns the `plot::gl::Program` and knows the GLSL pair (shaders.hpp), so no
/// caller spells out a uniform name that could drift from the shader. Creating
/// it propagates what the GL layer reports: `ErrorCode::Unsupported` while
/// `plot::gl::available()` is false, `GLFailure` plus the driver's log when the
/// GLSL does not compile or link.
class LineProgram {
public:
bool create(Status* st = nullptr) noexcept;
bool valid() const noexcept { return program_.id() != 0; }
void bind() const noexcept { program_.use(); }
/// `u_scale`: data space to pixels.
void set_scale(Vec2 data_to_px) const noexcept;
/// `u_offset`: pixel offset added after `set_scale`.
void set_offset(Vec2 px) const noexcept;
/// `u_viewport`: render target size in pixels.
void set_viewport(Vec2 px) const noexcept;
/// `u_width`: stroke width in pixels.
void set_width(f32 px) const noexcept;
/// `u_color`: stroke colour, alpha included.
void set_color(Color color) const noexcept;
private:
gl::Program program_;
};
/// Linked text program and its uniform surface. It fails exactly the way
/// `LineProgram::create()` does, propagating `Unsupported` and `GLFailure`.
///
/// `create()` also points the `u_atlas` sampler at texture unit 0, which is the
/// unit `plot::gl::Texture::bind()` defaults to, and leaves this program bound.
/// The atlas itself stays with the caller, who owns its lifetime.
class TextProgram {
public:
bool create(Status* st = nullptr) noexcept;
bool valid() const noexcept { return program_.id() != 0; }
void bind() const noexcept { program_.use(); }
/// `u_viewport`: render target size in pixels.
void set_viewport(Vec2 px) const noexcept;
/// `u_px_range`: the glyph cell's size on screen, in pixels (width and
/// height -- a cell need not be square).
void set_px_range(Vec2 px) const noexcept;
/// `u_glyph_uv`: the glyph cell's size in the atlas, normalised. It is the
/// rect every staged glyph is read through, so it is one size per draw -- and
/// it is the *backend's* (`TextBackend::cell_uv`), never a glyph's, because a
/// draw may stage glyphs whose origins differ even when their size cannot.
void set_glyph_uv(Vec2 uv) const noexcept;
/// `u_tex_size`: atlas size in texels.
void set_texture_size(Vec2 texels) const noexcept;
/// `u_color`: tint colour, alpha included.
void set_color(Color color) const noexcept;
private:
gl::Program program_;
};
} // namespace plot::render