Loading...
Searching...
No Matches
TextToTexture.hpp
1#pragma once
2#include <halp/controls.hpp>
3#include <halp/meta.hpp>
4#include <halp/texture.hpp>
5
6#include <QColor>
7#include <QFont>
8#include <QImage>
9#include <QPainter>
10
11#include <string>
12
13namespace Threedim
14{
15
16// Rasterize a text string into an RGBA texture via QPainter. Pipes into
17// any node that consumes halp::gpu_texture — most commonly
18// MaterialOverride (to show text on a mesh's base-color slot) or
19// Instancer / a billboard renderer (for text sprites).
20//
21// Re-renders only when a control (text / font / size / color / canvas
22// dimensions) changes — the update() hooks on each port fire recreate().
24{
25public:
26 halp_meta(name, "Text to Texture")
27 halp_meta(category, "Visuals/3D/Text")
28 halp_meta(c_name, "text_to_texture")
29 halp_meta(authors, "ossia team")
30 halp_meta(
31 manual_url,
32 "https://ossia.io/score-docs/processes/text-to-texture.html")
33 halp_meta(uuid, "5d3a9b2f-7e6c-4a8d-b1f4-9c2e3d5a7b8f")
34
35 struct ins
36 {
37 struct : halp::lineedit<"Text", "Hello, world">
38 {
39 void update(TextToTexture& self) { self.recreate(); }
40 } text;
41 struct : halp::lineedit<"Font family", "Sans">
42 {
43 void update(TextToTexture& self) { self.recreate(); }
44 } font_family;
45 struct : halp::spinbox_i32<"Font size", halp::irange{4, 512, 64}>
46 {
47 void update(TextToTexture& self) { self.recreate(); }
48 } font_size;
49 struct : halp::toggle<"Bold">
50 {
51 void update(TextToTexture& self) { self.recreate(); }
52 } bold;
53 struct : halp::toggle<"Italic">
54 {
55 void update(TextToTexture& self) { self.recreate(); }
56 } italic;
57
58 struct : halp::spinbox_i32<"Canvas width", halp::irange{16, 4096, 1024}>
59 {
60 void update(TextToTexture& self) { self.recreate(); }
61 } canvas_w;
62 struct : halp::spinbox_i32<"Canvas height", halp::irange{16, 4096, 256}>
63 {
64 void update(TextToTexture& self) { self.recreate(); }
65 } canvas_h;
66
67 // Colors are vec4 (r, g, b, a) in [0, 1]. The background defaults to
68 // transparent, so only the glyphs show.
69 struct : halp::hslider_f32<"Text R", halp::range{0., 1., 1.}> { void update(TextToTexture& s) { s.recreate(); } } fg_r;
70 struct : halp::hslider_f32<"Text G", halp::range{0., 1., 1.}> { void update(TextToTexture& s) { s.recreate(); } } fg_g;
71 struct : halp::hslider_f32<"Text B", halp::range{0., 1., 1.}> { void update(TextToTexture& s) { s.recreate(); } } fg_b;
72 struct : halp::hslider_f32<"Text A", halp::range{0., 1., 1.}> { void update(TextToTexture& s) { s.recreate(); } } fg_a;
73 struct : halp::hslider_f32<"BG R", halp::range{0., 1., 0.}> { void update(TextToTexture& s) { s.recreate(); } } bg_r;
74 struct : halp::hslider_f32<"BG G", halp::range{0., 1., 0.}> { void update(TextToTexture& s) { s.recreate(); } } bg_g;
75 struct : halp::hslider_f32<"BG B", halp::range{0., 1., 0.}> { void update(TextToTexture& s) { s.recreate(); } } bg_b;
76 struct : halp::hslider_f32<"BG A", halp::range{0., 1., 0.}> { void update(TextToTexture& s) { s.recreate(); } } bg_a;
77
78 // Text alignment inside the canvas: 0=left, 1=center, 2=right for h;
79 // 0=top, 1=center, 2=bottom for v.
80 struct : halp::spinbox_i32<"H align", halp::irange{0, 2, 1}>
81 { void update(TextToTexture& s) { s.recreate(); } } h_align;
82 struct : halp::spinbox_i32<"V align", halp::irange{0, 2, 1}>
83 { void update(TextToTexture& s) { s.recreate(); } } v_align;
84 } inputs;
85
86 struct
87 {
88 halp::texture_output<"Output", halp::rgba_texture> main;
89 } outputs;
90
91 void recreate()
92 {
93 const int w = inputs.canvas_w.value;
94 const int h = inputs.canvas_h.value;
95 if(w <= 0 || h <= 0)
96 return;
97
98 // Qt renders with premultiplied alpha; we output straight RGBA8.
99 // QImage::Format_RGBA8888 is non-premultiplied and matches what
100 // gpu_texture expects when upload-bound as RGBA8.
101 QImage img(w, h, QImage::Format_RGBA8888);
102 img.fill(QColor::fromRgbF(
103 inputs.bg_r.value, inputs.bg_g.value, inputs.bg_b.value,
104 inputs.bg_a.value));
105
106 QPainter p(&img);
107 p.setRenderHint(QPainter::Antialiasing, true);
108 p.setRenderHint(QPainter::TextAntialiasing, true);
109
110 QFont f(QString::fromStdString(inputs.font_family.value));
111 f.setPixelSize(inputs.font_size.value);
112 f.setBold(inputs.bold.value);
113 f.setItalic(inputs.italic.value);
114 p.setFont(f);
115 p.setPen(QColor::fromRgbF(
116 inputs.fg_r.value, inputs.fg_g.value, inputs.fg_b.value,
117 inputs.fg_a.value));
118
119 int flags = 0;
120 switch(inputs.h_align.value)
121 {
122 case 0: flags |= Qt::AlignLeft; break;
123 case 2: flags |= Qt::AlignRight; break;
124 default: flags |= Qt::AlignHCenter;
125 }
126 switch(inputs.v_align.value)
127 {
128 case 0: flags |= Qt::AlignTop; break;
129 case 2: flags |= Qt::AlignBottom; break;
130 default: flags |= Qt::AlignVCenter;
131 }
132 flags |= Qt::TextWordWrap;
133
134 p.drawText(
135 QRect(0, 0, w, h), flags,
136 QString::fromStdString(inputs.text.value));
137 p.end();
138
139 outputs.main.create(w, h);
140 std::memcpy(outputs.main.texture.bytes, img.constBits(), std::size_t(w) * h * 4);
141 outputs.main.upload();
142 }
143};
144
145}
Definition TextToTexture.hpp:24
Definition TextToTexture.hpp:36