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
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.
157{
158 const uint8_t cr = static_cast<uint8_t>(std::clamp(color.r, 0.F, 1.F) * 255.F);
159 const uint8_t cg = static_cast<uint8_t>(std::clamp(color.g, 0.F, 1.F) * 255.F);
160 const uint8_t cb = static_cast<uint8_t>(std::clamp(color.b, 0.F, 1.F) * 255.F);
161 const uint8_t ca = static_cast<uint8_t>(std::clamp(color.a, 0.F, 1.F) * 255.F);
162
164 const std::span<const uint8_t> atlas_pixels = atlas_tex.
pixel_bytes(0);
165 const uint32_t atlas_size = atlas.
atlas_size();
166
167 for (
const auto&
q : quads) {
168 const auto gx =
static_cast<int32_t
>(std::floor(
q.x0));
169 const auto gy =
static_cast<int32_t
>(std::floor(
q.y0));
170 const auto gw =
static_cast<uint32_t
>(std::ceil(
q.x1 -
q.x0));
171 const auto gh =
static_cast<uint32_t
>(std::ceil(
q.y1 -
q.y0));
172
173 const auto src_x =
static_cast<uint32_t
>(
q.uv_x0 *
static_cast<float>(atlas_size));
174 const auto src_y =
static_cast<uint32_t
>(
q.uv_y0 *
static_cast<float>(atlas_size));
175
176 for (uint32_t row = 0; row < gh; ++row) {
177 const int32_t dst_row = gy + static_cast<int32_t>(row);
178 if (dst_row < 0 || static_cast<uint32_t>(dst_row) >= buf_h) {
179 continue;
180 }
181
182 const uint8_t* src_row = atlas_pixels.data()
183 + static_cast<size_t>(src_y + row) * atlas_size + src_x;
184 uint8_t* dst_row_ptr = dst + static_cast<size_t>(dst_row) * buf_w * 4;
185
186 for (uint32_t col = 0; col < gw; ++col) {
187 const int32_t dst_col = gx + static_cast<int32_t>(col);
188 if (dst_col < 0 || static_cast<uint32_t>(dst_col) >= buf_w) {
189 continue;
190 }
191
192 const uint8_t coverage = src_row[col];
193 const auto alpha = static_cast<uint8_t>(
194 (static_cast<uint32_t>(coverage) * static_cast<uint32_t>(ca)) / 255U);
195
196 uint8_t* px = dst_row_ptr + static_cast<size_t>(dst_col) * 4;
197 px[0] = cr;
198 px[1] = cg;
199 px[2] = cb;
200 px[3] = alpha;
201 }
202 }
203 }
204}
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).