Write glyph quads into a caller-provided RGBA8 pixel buffer.
Applies coverage-multiplied alpha blend per glyph cell. The destination buffer must be row-major RGBA8 with stride == buf_w * 4 bytes. Quads that fall outside [0, buf_w) x [0, buf_h) are clipped per pixel.
auto layout =
lay_out(text, atlas, 0.F, 0.F, wrap_w);
for (
auto&
q : layout.quads) { ... }
const std::vector< float > * pixels
std::optional< glm::vec3 > color
void rasterize_quads(std::span< const GlyphQuad > quads, GlyphAtlas &atlas, glm::vec4 color, uint8_t *dst, uint32_t buf_w, uint32_t buf_h)
Write glyph quads into a caller-provided RGBA8 pixel buffer.
LayoutResult lay_out(std::string_view text, GlyphAtlas &atlas, float pen_x, float pen_y, uint32_t wrap_w)
Lay out a UTF-8 string into a sequence of screen-space quads.
154{
155 const uint8_t cr =
static_cast<uint8_t
>(std::clamp(
color.r, 0.F, 1.F) * 255.F);
156 const uint8_t cg =
static_cast<uint8_t
>(std::clamp(
color.g, 0.F, 1.F) * 255.F);
157 const uint8_t cb =
static_cast<uint8_t
>(std::clamp(
color.b, 0.F, 1.F) * 255.F);
158 const uint8_t ca =
static_cast<uint8_t
>(std::clamp(
color.a, 0.F, 1.F) * 255.F);
159
161 const std::span<const uint8_t> atlas_pixels = atlas_tex.
pixel_bytes(0);
162 const uint32_t atlas_size = atlas.
atlas_size();
163
164 for (
const auto&
q : quads) {
165 const auto gx =
static_cast<int32_t
>(std::floor(
q.x0));
166 const auto gy =
static_cast<int32_t
>(std::floor(
q.y0));
167 const auto gw =
static_cast<uint32_t
>(std::ceil(
q.x1 -
q.x0));
168 const auto gh =
static_cast<uint32_t
>(std::ceil(
q.y1 -
q.y0));
169
170 const auto src_x =
static_cast<uint32_t
>(
q.uv_x0 *
static_cast<float>(atlas_size));
171 const auto src_y =
static_cast<uint32_t
>(
q.uv_y0 *
static_cast<float>(atlas_size));
172
173 for (uint32_t row = 0; row < gh; ++row) {
174 const int32_t dst_row = gy + static_cast<int32_t>(row);
175 if (dst_row < 0 || static_cast<uint32_t>(dst_row) >= buf_h) {
176 continue;
177 }
178
179 const uint8_t* src_row = atlas_pixels.data()
180 + static_cast<size_t>(src_y + row) * atlas_size + src_x;
181 uint8_t* dst_row_ptr = dst + static_cast<size_t>(dst_row) * buf_w * 4;
182
183 for (uint32_t col = 0; col < gw; ++col) {
184 const int32_t dst_col = gx + static_cast<int32_t>(col);
185 if (dst_col < 0 || static_cast<uint32_t>(dst_col) >= buf_w) {
186 continue;
187 }
188
189 const uint8_t coverage = src_row[col];
190 const auto alpha = static_cast<uint8_t>(
191 (static_cast<uint32_t>(coverage) * static_cast<uint32_t>(ca)) / 255U);
192
193 uint8_t* px = dst_row_ptr + static_cast<size_t>(dst_col) * 4;
194 px[0] = cr;
195 px[1] = cg;
196 px[2] = cb;
197 px[3] = alpha;
198 }
199 }
200 }
201}
std::span< const uint8_t > pixel_bytes(uint32_t layer=0) const
Read-only byte-level view over the pixel buffer.
SignalSourceContainer wrapping GPU texture data as addressable pixel bytes.
uint32_t atlas_size() const
Atlas texture dimension (width == height == atlas_size).
const Kakshya::TextureContainer & texture() const
The atlas texture as a TextureContainer (R8, atlas_size x atlas_size).