• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 30144558388

25 Jul 2026 04:44AM UTC coverage: 15.31% (-0.001%) from 15.311%
30144558388

Pull #2147

github

web-flow
Merge 98c48c25e into 13afd939a
Pull Request #2147: gfx: fall back to RGBA8 where the backend has no BGRA8 (fixes images on wasm)

0 of 19 new or added lines in 3 files covered. (0.0%)

1 existing line in 1 file now uncovered.

30392 of 198510 relevant lines covered (15.31%)

984.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/plugins/score-plugin-gfx/Gfx/Graph/TextNode.cpp
1
#include <Gfx/Graph/NodeRenderer.hpp>
2
#include <Gfx/Graph/RenderList.hpp>
3
#include <Gfx/Graph/RenderState.hpp>
4
#include <Gfx/Graph/TextNode.hpp>
5

6
#include <ossia/detail/math.hpp>
7
#include <ossia/gfx/port_index.hpp>
8
#include <ossia/network/value/value.hpp>
9
#include <ossia/network/value/value_conversion.hpp>
10

11
#include <QPainter>
12

13
namespace score::gfx
14
{
15

16
static const constexpr auto text_vertex_shader = R"_(#version 450
17
layout(location = 0) in vec2 position;
18
layout(location = 1) in vec2 texcoord;
19

20
layout(binding = 3) uniform sampler2D y_tex;
21
layout(location = 0) out vec2 v_texcoord;
22

23
layout(std140, binding = 0) uniform renderer_t {
24
  mat4 clipSpaceCorrMatrix;
25
  vec2 renderSize;
26
} renderer;
27

28
layout(std140, binding = 2) uniform material_t {
29
  float opacity;
30
  vec2 position;
31
  vec2 scale;
32
} mat;
33
out gl_PerVertex { vec4 gl_Position; };
34

35
void main()
36
{
37
  v_texcoord = texcoord;
38
  gl_Position = renderer.clipSpaceCorrMatrix * vec4(mat.position + mat.scale * position, 0.0, 1.);
39
#if defined(QSHADER_HLSL) || defined(QSHADER_MSL)
40
  gl_Position.y = - gl_Position.y;
41
#endif
42
}
43
)_";
44

45
static const constexpr auto text_fragment_shader = R"_(#version 450
46
layout(std140, binding = 0) uniform renderer_t {
47
  mat4 clipSpaceCorrMatrix;
48
  vec2 renderSize;
49
} renderer;
50

51
layout(std140, binding = 2) uniform material_t {
52
  float opacity;
53
  vec2 position;
54
  vec2 scale;
55
} mat;
56

57
layout(binding=3) uniform sampler2D y_tex;
58

59
layout(location = 0) in vec2 v_texcoord;
60
layout(location = 0) out vec4 fragColor;
61

62
void main ()
63
{
64
  fragColor = texture(y_tex, v_texcoord) * mat.opacity;
65
}
66
)_";
67
TextNode::TextNode()
×
68
{
×
69
  // FIXME why are the others missing ?????
70
  input.push_back(new Port{this, &ubo.opacity, Types::Float, {}});
×
71
  input.push_back(new Port{this, &ubo.position[0], Types::Vec2, {}});
×
72
  input.push_back(new Port{this, &ubo.scale[0], Types::Vec2, {}});
×
73
  output.push_back(new Port{this, {}, Types::Image, {}});
×
74

75
  m_materialData.reset((char*)&ubo);
×
76
}
×
77

78
TextNode::~TextNode()
×
79
{
×
80
  m_materialData.release();
×
81
}
×
82

83
class TextNode::Renderer : public GenericNodeRenderer
84
{
85
public:
86
  using GenericNodeRenderer::GenericNodeRenderer;
×
87

88
private:
89
  ~Renderer() { }
×
90

91
  TextureRenderTarget renderTargetForInput(const Port& p) override { return {}; }
×
92

93
  // TODO
94
  QSize sz{1920, 1080};
×
95
  void rerender()
×
96
  {
97
    auto& n = static_cast<const TextNode&>(this->node);
×
98

99
    if(m_img.size().isNull())
×
100
      m_img = QImage(sz, QImage::Format::Format_ARGB32_Premultiplied);
×
101
    m_img.fill(Qt::transparent);
×
102
    {
103
      QPainter p{&m_img};
×
104
      p.setRenderHint(QPainter::Antialiasing, true);
×
105
      p.setRenderHint(QPainter::TextAntialiasing, true);
×
106

107
      p.setFont(n.font);
×
108
      p.setPen(n.pen);
×
109
      p.drawText(10, 10, sz.width() - 20, sz.height() - 20, 0, n.text);
×
110
    }
×
111

112
    m_uploaded = false;
×
113
  }
×
114

115
  void init(RenderList& renderer, QRhiResourceUpdateBatch& res) override
×
116
  {
117
    rerender();
×
118
    const auto& mesh = renderer.defaultQuad();
×
119
    defaultMeshInit(renderer, mesh, res);
×
120
    processUBOInit(renderer);
×
121
    m_material.init(renderer, node.input, m_samplers);
×
122
    std::tie(m_vertexS, m_fragmentS) = score::gfx::makeShaders(
×
123
        renderer.state, text_vertex_shader, text_fragment_shader);
×
124

125
    QRhi& rhi = *renderer.state.rhi;
×
126

127
    {
NEW
128
      auto tex = rhi.newTexture(imageTextureFormat(rhi), sz, 1, QRhiTexture::Flag{});
×
129

130
      tex->setName("TextNode::tex");
×
131
      tex->create();
×
132
      m_textures.push_back({{}, tex});
×
133
    }
134

135
    // Create the sampler in which we are going to put the texture
136
    {
137
      auto sampler = rhi.newSampler(
×
138
          QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
139
          QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
140

141
      sampler->setName("TextNode::sampler");
×
142
      sampler->create();
×
143
      auto tex
×
144
          = m_textures.empty() ? &renderer.emptyTexture() : m_textures.front().second;
×
145
      m_samplers.push_back({sampler, tex});
×
146
    }
147

148
    defaultPassesInit(renderer, mesh);
×
149
  }
×
150

151
  void update(RenderList& renderer, QRhiResourceUpdateBatch& res, Edge* edge) override
×
152
  {
153
    defaultUBOUpdate(renderer, res);
×
154

155
    if(m_textures.empty())
×
156
      return;
×
157

158
    // If images haven't been uploaded yet, upload them.
159

160
    auto& n = static_cast<const TextNode&>(this->node);
×
161
    if(int val = n.mustRerender.load(std::memory_order_acquire);
×
162
       val != this->m_renderIndex)
×
163
    {
164
      rerender();
×
165
      this->m_renderIndex = val;
×
166
    }
×
167

168
    if(!m_uploaded)
×
169
    {
NEW
170
      res.uploadTexture(
×
NEW
171
          m_textures[0].second,
×
NEW
172
          adaptImageFormat(m_img, imageTextureFormat(*renderer.state.rhi)));
×
173

174
      m_uploaded = true;
×
175
    }
×
176
  }
×
177

178
  void runRenderPass(RenderList& renderer, QRhiCommandBuffer& cb, Edge& edge) override
×
179
  {
180
    const auto& mesh = renderer.defaultQuad();
×
181
    defaultRenderPass(renderer, mesh, cb, edge);
×
182
  }
×
183

184
  void release(RenderList& r) override
×
185
  {
186
    for(auto tex : m_textures)
×
187
    {
188
      tex.second->deleteLater();
×
189
    }
190
    m_textures.clear();
×
191

192
    defaultRelease(r);
×
193
  }
×
194

195
  QImage m_img;
196
  std::vector<std::pair<score::gfx::Edge*, QRhiTexture*>> m_textures;
197
  int m_renderIndex{-1};
×
198
  bool m_uploaded = false;
×
199
};
200

201
NodeRenderer* TextNode::createRenderer(RenderList& r) const noexcept
×
202
{
203
  return new Renderer{*this};
×
204
}
205

206
void TextNode::process(Message&& msg)
×
207
{
208
  ProcessNode::process(msg.token);
×
209

210
  int32_t p = 0;
×
211
  for(const gfx_input& m : msg.input)
×
212
  {
213
    if(auto val = ossia::get_if<ossia::value>(&m))
×
214
    {
215
      switch(p)
×
216
      {
217
        case 0: {
218
          // Text
219
          {
220
            text = QString::fromStdString(ossia::convert<std::string>(*val));
×
221
            mustRerender++;
×
222
          }
223
          break;
×
224
        }
225
        case 1: {
226
          // Font
227
          {
228
            font.setFamily(QString::fromStdString(ossia::convert<std::string>(*val)));
×
229
            mustRerender++;
×
230
          }
231
          break;
×
232
        }
233
        case 2: {
234
          // Point size
235
          {
236
            font.setPointSizeF(ossia::convert<float>(*val));
×
237
            mustRerender++;
×
238
          }
239
          break;
×
240
        }
241

242
        case 3: // Opacity
243
        {
244
          auto opacity = ossia::convert<float>(*val);
×
245
          this->ubo.opacity = ossia::clamp(opacity, 0.f, 1.f);
×
246
          this->materialChange();
×
247
          break;
×
248
        }
249
        case 4: // Position
250
        {
251
          auto sink = ossia::gfx::port_index{msg.node_id, p - 3};
×
252
          ossia::visit(
×
253
              [this, sink](const auto& v) { ProcessNode::process(sink.port, v); },
×
254
              std::move(m));
×
255
          break;
×
256
        }
257

258
        case 5: // Scale X
259
        {
260
          {
261
            auto scale = ossia::convert<float>(*val);
×
262
            this->ubo.scale[0] = scale;
×
263
            this->materialChange();
×
264
          }
265
          break;
×
266
        }
267
        case 6: // Scale Y
268
        {
269
          {
270
            auto scale = ossia::convert<float>(*val);
×
271
            this->ubo.scale[1] = scale;
×
272
            this->materialChange();
×
273
          }
274
          break;
×
275
        }
276

277
        case 7: {
278
          // Color
279
          {
280
            auto rgba = ossia::convert<ossia::vec4f>(*val);
×
281
            pen.setColor(QColor::fromRgbF(rgba[0], rgba[1], rgba[2], rgba[3]));
×
282
            mustRerender++;
×
283
          }
284
          break;
×
285
        }
286
      }
287
    }
×
288

289
    p++;
×
290
  }
291
}
×
292

293
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc