#pragma once #include #include #include #include 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_dash`: the dash pattern in pixels -- `x` the ink length, `y` the gap, and a /// non-positive `y` (the default a caller passes) means **solid**. It is a fragment-shader /// discard on the interpolated `v_along`, so it costs no geometry, and it is the one uniform /// that distinguishes the crosshair's stroke from every other line in the library. void set_dash(Vec2 on_off) 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