Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
sdf_software_renderer.hpp
Go to the documentation of this file.
1#pragma once
2
3// CPU/software renderer for SDF text.
4//
5// Rasterizes an SdfTextQuad run onto an 8-bit grayscale output buffer
6// by replicating the SkSL sampler logic (smoothstep + fwidth-like
7// pixel-scale AA) on the CPU. Lets headless tests and CI validate the
8// SDF pipeline end-to-end without requiring a GPU context, and gives
9// backend adapters (CoreGraphics / Skia) a reference implementation to
10// diff against.
11
14
15#include <cstdint>
16#include <vector>
17
18namespace pulp::canvas {
19
20// Rasterize `quads` into `out` (width * height bytes, row-major, A8).
21// Each output pixel is the maximum alpha contribution of any glyph
22// quad that covers it. `render_size` is the intended render size in
23// pixels and is used to scale source-rect samples back to the atlas.
24template <typename AtlasT>
25inline void render_sdf_text_software(const AtlasT& atlas,
26 const std::vector<SdfTextQuad>& quads,
27 std::uint8_t* out, int out_w, int out_h,
28 const SdfTextOptions& opts = {}) {
29 const int aw = atlas.width();
30 const int ah = atlas.height();
31 if (aw <= 0 || ah <= 0) return;
32
33 const float edge255 = opts.edge * 255.0f;
34
35 for (const auto& q : quads) {
36 const int x0 = static_cast<int>(std::max(0.0f, std::floor(q.dst_x)));
37 const int y0 = static_cast<int>(std::max(0.0f, std::floor(q.dst_y)));
38 const int x1 = static_cast<int>(std::min(
39 static_cast<float>(out_w),
40 std::ceil(q.dst_x + q.dst_w)));
41 const int y1 = static_cast<int>(std::min(
42 static_cast<float>(out_h),
43 std::ceil(q.dst_y + q.dst_h)));
44
45 if (q.dst_w <= 0 || q.dst_h <= 0) continue;
46 const float sx_per_px = q.src_w / q.dst_w;
47 const float sy_per_px = q.src_h / q.dst_h;
48
49 for (int y = y0; y < y1; ++y) {
50 for (int x = x0; x < x1; ++x) {
51 const float sx = q.src_x + (x + 0.5f - q.dst_x) * sx_per_px;
52 const float sy = q.src_y + (y + 0.5f - q.dst_y) * sy_per_px;
53 const int ix = static_cast<int>(sx);
54 const int iy = static_cast<int>(sy);
55 if (ix < 0 || iy < 0 || ix >= aw || iy >= ah) continue;
56
57 const std::uint8_t d = atlas.pixels()[iy * aw + ix];
58 // Pixel-scale AA: half a texel of source per output pixel.
59 const float aa = 0.5f * std::max(sx_per_px, sy_per_px) * 255.0f;
60 const float lo = edge255 - aa;
61 const float hi = edge255 + aa;
62 float a = (d - lo) / std::max(1.0f, (hi - lo));
63 if (a < 0.0f) a = 0.0f;
64 else if (a > 1.0f) a = 1.0f;
65 const auto byte = static_cast<std::uint8_t>(a * 255.0f);
66 std::uint8_t& dst = out[y * out_w + x];
67 if (byte > dst) dst = byte;
68 }
69 }
70 }
71}
72
73} // namespace pulp::canvas
Definition affine_transform.hpp:24
void render_sdf_text_software(const AtlasT &atlas, const std::vector< SdfTextQuad > &quads, std::uint8_t *out, int out_w, int out_h, const SdfTextOptions &opts={})
Definition sdf_software_renderer.hpp:25
Definition sdf_text.hpp:35