reflex-terminal 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/renderer.cpp CHANGED
@@ -3,8 +3,10 @@
3
3
 
4
4
  #include <assert.h>
5
5
  #include <math.h>
6
+ #include <algorithm>
6
7
  #include <vector>
7
8
  #include <map>
9
+ #include <xot/util.h>
8
10
  #include <rays/image.h>
9
11
  #include <reflex/exception.h>
10
12
  #include "terminal.h"
@@ -17,38 +19,67 @@ namespace ReflexTerminal
17
19
  using namespace Reflex;
18
20
 
19
21
 
20
- enum
22
+ struct Glyph
21
23
  {
24
+ // a glyph's place in the atlas image. width is its own, so that a
25
+ // wide character does not run over the slot after it
22
26
 
23
- ATLAS_WIDTH = 1024,
27
+ coord x, y, width;
24
28
 
25
- ATLAS_INITIAL_ROWS = 8,
29
+ bool is_valid () const {return width > 0;}
26
30
 
27
- ATLAS_MAX_HEIGHT = 4096
31
+ };// Glyph
28
32
 
29
- };
33
+ typedef std::map<String, Glyph> GlyphMap;
30
34
 
31
35
 
32
- struct Glyph
36
+ enum
33
37
  {
34
- // a glyph's place in the atlas image. width is its own, so that a
35
- // wide character does not run over the slot after it
36
38
 
37
- coord x, y, width;
39
+ FONT_BOLD = Xot::bit(0),
38
40
 
39
- bool is_valid () const {return width > 0;}
41
+ FONT_ITALIC = Xot::bit(1),
40
42
 
41
- };// Glyph
43
+ };
44
+
45
+
46
+ static constexpr int NFONTS = 4;
47
+
48
+ static constexpr int ATLAS_WIDTH = 1024;
49
+
50
+ static constexpr int ATLAS_INITIAL_ROWS = 8;
51
+
52
+ static constexpr int ATLAS_MAX_HEIGHT = 4096;
53
+
54
+ static constexpr float FAINT_OPACITY = 0.5f;
55
+
56
+
57
+ static int
58
+ to_font_index (uint attribs)
59
+ {
60
+ int index = 0;
61
+ if (attribs & Terminal::Span::BOLD) index |= FONT_BOLD;
62
+ if (attribs & Terminal::Span::ITALIC) index |= FONT_ITALIC;
63
+ return index;
64
+ }
42
65
 
43
66
 
44
67
  struct Renderer::Data
45
68
  {
46
69
 
47
- Font font;
70
+ Font fonts[NFONTS];
48
71
 
49
72
  coord cell_width = 0, cell_height = 0;
50
73
 
51
- // where the next glyph goes
74
+ // whether blinking spans are in their lit phase. the renderer only
75
+ // draws one frame, so the timer toggling this lives with a view
76
+ bool blink_visible = true;
77
+
78
+ float background_alpha = 1;
79
+
80
+ // where the next glyph goes. the faces share the one atlas, so
81
+ // that a row mixing them still draws from a single texture and
82
+ // the painter's batch holds
52
83
  coord x = 0, y = 0;
53
84
 
54
85
  // Painter::text() flushes the batch and uploads a texture on every
@@ -57,11 +88,11 @@ namespace ReflexTerminal
57
88
 
58
89
  // the empty Glyph of a character too wide for the atlas is kept
59
90
  // as well, so that it is not measured again on every frame
60
- std::map<String, Glyph> glyphs;
91
+ GlyphMap glyphs[NFONTS];
61
92
 
62
93
  // reused between frames so that a screenful of new characters
63
94
  // does not allocate
64
- std::vector<String> missing;
95
+ std::vector<String> missing[NFONTS];
65
96
 
66
97
  };// Renderer::Data
67
98
 
@@ -82,7 +113,7 @@ namespace ReflexTerminal
82
113
  static void
83
114
  reset_atlas (Renderer::Data* self)
84
115
  {
85
- self->glyphs.clear();
116
+ for (auto& glyphs : self->glyphs) glyphs.clear();
86
117
  self->x = self->y = 0;
87
118
  self->atlas = Image(ATLAS_WIDTH, self->cell_height * ATLAS_INITIAL_ROWS);
88
119
  }
@@ -93,8 +124,18 @@ namespace ReflexTerminal
93
124
  if (!font)
94
125
  argument_error(__FILE__, __LINE__);
95
126
 
96
- self->font = font;
97
- self->cell_width = font.get_width("M");
127
+ Font bold = font.dup(); bold .set_weight(Font::WEIGHT_BOLD);
128
+ Font italic = font.dup(); italic.set_italic(true);
129
+ Font bold_italic = bold.dup(); bold_italic.set_italic(true);
130
+
131
+ self->fonts[0] = font;
132
+ self->fonts[FONT_BOLD] = bold;
133
+ self->fonts[ FONT_ITALIC] = italic;
134
+ self->fonts[FONT_BOLD | FONT_ITALIC] = bold_italic;
135
+
136
+ // the cell is sized to the regular face; a family whose variants
137
+ // measure differently has them clipped to it
138
+ self->cell_width = ceil(font.get_width("M"));
98
139
  self->cell_height = ceil(font.get_height());
99
140
  reset_atlas(self.get());
100
141
  }
@@ -102,7 +143,7 @@ namespace ReflexTerminal
102
143
  const Font&
103
144
  Renderer::font () const
104
145
  {
105
- return self->font;
146
+ return self->fonts[0];
106
147
  }
107
148
 
108
149
  coord
@@ -117,6 +158,33 @@ namespace ReflexTerminal
117
158
  return self->cell_height;
118
159
  }
119
160
 
161
+ void
162
+ Renderer::set_blink_visible (bool visible)
163
+ {
164
+ self->blink_visible = visible;
165
+ }
166
+
167
+ bool
168
+ Renderer::blink_visible () const
169
+ {
170
+ return self->blink_visible;
171
+ }
172
+
173
+ void
174
+ Renderer::set_background_alpha (float alpha)
175
+ {
176
+ if (!(0 <= alpha && alpha <= 1))
177
+ argument_error(__FILE__, __LINE__, "invalid background alpha: %g", alpha);
178
+
179
+ self->background_alpha = alpha;
180
+ }
181
+
182
+ float
183
+ Renderer::background_alpha () const
184
+ {
185
+ return self->background_alpha;
186
+ }
187
+
120
188
  static bool
121
189
  grow_atlas (Renderer::Data* self)
122
190
  {
@@ -131,6 +199,7 @@ namespace ReflexTerminal
131
199
  Image grown(ATLAS_WIDTH, height);
132
200
  Painter painter = grown.painter();
133
201
  painter.begin();
202
+ painter.set_blend_mode(Rays::BLEND_REPLACE);
134
203
  painter.image(self->atlas);
135
204
  painter.end();
136
205
 
@@ -139,9 +208,9 @@ namespace ReflexTerminal
139
208
  }
140
209
 
141
210
  static Glyph
142
- allocate_glyph (Renderer::Data* self, const String& text)
211
+ allocate_glyph (Renderer::Data* self, int font_index, const String& text)
143
212
  {
144
- coord width = ceil(self->font.get_width(text.c_str()));
213
+ coord width = ceil(self->fonts[font_index].get_width(text.c_str()));
145
214
  if (width <= 0 || width > ATLAS_WIDTH) return {0, 0, 0};
146
215
 
147
216
  if (self->x + width > ATLAS_WIDTH)
@@ -157,49 +226,63 @@ namespace ReflexTerminal
157
226
  }
158
227
 
159
228
  static void
160
- add_glyphs (Renderer::Data* self, const std::vector<String>& texts)
229
+ add_glyphs (Renderer::Data* self)
161
230
  {
162
231
  // each paint switches the offscreen rendering context, which costs
163
- // far more than the drawing itself, so the unknown glyphs are
164
- // collected first and drawn together
232
+ // far more than the drawing itself, so the unknown glyphs of every
233
+ // face are collected first and drawn in the one session
165
234
 
166
- std::map<String, Glyph> added;
167
- for (const String& text : texts)
235
+ GlyphMap added[NFONTS];
236
+ size_t nadded = 0;
237
+ for (int i = 0; i < NFONTS; ++i)
168
238
  {
169
- if (self->glyphs.find(text) != self->glyphs.end()) continue;
170
- if (added.find(text) != added.end()) continue;
171
-
172
- Glyph glyph = allocate_glyph(self, text);
173
- if (glyph.is_valid())
174
- added[text] = glyph;
175
- else
176
- self->glyphs[text] = glyph;// no room: do not try again
239
+ for (const String& text : self->missing[i])
240
+ {
241
+ if (self->glyphs[i].find(text) != self->glyphs[i].end()) continue;
242
+ if (added[i].find(text) != added[i].end()) continue;
243
+
244
+ Glyph glyph = allocate_glyph(self, i, text);
245
+ if (glyph.is_valid())
246
+ {
247
+ added[i][text] = glyph;
248
+ ++nadded;
249
+ }
250
+ else
251
+ self->glyphs[i][text] = glyph;// no room: do not try again
252
+ }
177
253
  }
178
- if (added.empty()) return;
254
+ if (nadded == 0) return;
179
255
 
180
256
  Painter painter = self->atlas.painter();
181
257
  painter.begin();
182
- painter.set_font(self->font);
258
+ painter.set_blend_mode(Rays::BLEND_REPLACE);
183
259
  painter.set_fill(1, 1, 1);
184
- for (const auto& it : added)
185
- painter.text(it.first.c_str(), it.second.x, it.second.y);
186
- painter.end();
260
+ for (int i = 0; i < NFONTS; ++i)
261
+ {
262
+ if (added[i].empty()) continue;
187
263
 
188
- self->glyphs.insert(added.begin(), added.end());
264
+ painter.set_font(self->fonts[i]);
265
+ for (const auto& it : added[i])
266
+ painter.text(it.first.c_str(), it.second.x, it.second.y);
267
+
268
+ self->glyphs[i].insert(added[i].begin(), added[i].end());
269
+ }
270
+ painter.end();
189
271
  }
190
272
 
191
273
  template <typename FUN>
192
274
  static void
193
275
  add_missing_glyphs (Renderer::Data* self, FUN fun)
194
276
  {
195
- assert(self->missing.empty());
277
+ assert(std::all_of(
278
+ self->missing, self->missing + NFONTS,
279
+ [](const auto& missing) {return missing.empty();}));
196
280
 
197
281
  fun();
198
- if (self->missing.empty())
199
- return;
200
282
 
201
- add_glyphs(self, self->missing);
202
- self->missing.clear();
283
+ add_glyphs(self);
284
+ for (auto& missing : self->missing)
285
+ missing.clear();
203
286
  }
204
287
 
205
288
  void
@@ -209,21 +292,23 @@ namespace ReflexTerminal
209
292
  // rendering context and replaces the image whenever it grows, so a
210
293
  // caller must not run bake_glyphs inside a frame being drawn
211
294
 
212
- if (!self->font)
295
+ if (!self->fonts[0])
213
296
  invalid_state_error(__FILE__, __LINE__);
214
297
 
215
298
  // an empty atlas is seeded with printable ascii, so that a screen
216
299
  // of it is never handed back to be rasterized a character at a
217
300
  // time. it also leaves the atlas holding something no matter what
218
- // the screen showed, which is how a caller tells it has been baked
219
- if (self->glyphs.empty())
301
+ // the screen showed, which is how a caller tells it has been baked.
302
+ // only the regular face is seeded; the other faces are rare enough
303
+ // to earn their place as they appear
304
+ if (self->glyphs[0].empty())
220
305
  {
221
306
  // baked on its own, so that the walk that follows collects
222
307
  // only what the seed does not already cover
223
308
  add_missing_glyphs(self.get(), [&]()
224
309
  {
225
310
  for (char c = 0x20; c <= 0x7e; ++c)
226
- self->missing.emplace_back(&c, 1);
311
+ self->missing[0].emplace_back(&c, 1);
227
312
  });
228
313
  }
229
314
 
@@ -234,6 +319,9 @@ namespace ReflexTerminal
234
319
  {
235
320
  for (const Terminal::Span& span : row)
236
321
  {
322
+ if (span.attribs & Terminal::Span::INVISIBLE) continue;
323
+
324
+ int font_index = to_font_index(span.attribs);
237
325
  const uint* offsets = cell_offsets + span.cell_offset;
238
326
  for (uint i = 0; i < span.cell_size; ++i)
239
327
  {
@@ -241,8 +329,8 @@ namespace ReflexTerminal
241
329
  uint end = (i + 1) < span.cell_size ? offsets[i + 1] : (uint) span.text.size();
242
330
 
243
331
  String text(span.text.data() + begin, end - begin);
244
- if (self->glyphs.find(text) == self->glyphs.end())
245
- self->missing.emplace_back(text);
332
+ if (self->glyphs[font_index].find(text) == self->glyphs[font_index].end())
333
+ self->missing[font_index].emplace_back(text);
246
334
  }
247
335
  }
248
336
  }
@@ -256,7 +344,10 @@ namespace ReflexTerminal
256
344
  // had the room for them: one too wide to fit is kept as an empty Glyph
257
345
  // and still counted
258
346
 
259
- return self->glyphs.size();
347
+ size_t count = 0;
348
+ for (const auto& glyphs : self->glyphs)
349
+ count += glyphs.size();
350
+ return count;
260
351
  }
261
352
 
262
353
  static Color
@@ -279,57 +370,162 @@ namespace ReflexTerminal
279
370
  ((attribs & Terminal::Span::SELECTED) != 0);
280
371
  }
281
372
 
282
- void
283
- Renderer::draw (Painter* painter, const Terminal& terminal, const Bounds& bounds)
373
+ static Color
374
+ to_text_color (uint attribs, Color color)
284
375
  {
285
- if (!painter)
286
- argument_error(__FILE__, __LINE__);
287
- if (!self->font)
288
- invalid_state_error(__FILE__, __LINE__);
376
+ if (attribs & Terminal::Span::FAINT)
377
+ color.alpha *= FAINT_OPACITY;
289
378
 
290
- // what the terminal has not been told is left to the renderer, so the
291
- // theme starts here and get_color only writes over it when the child
292
- // or the application has named a color of its own
293
- Color theme_fg(1, 1, 1), theme_bg(0, 0, 0);
294
- terminal.get_color(Terminal::COLOR_FOREGROUND, &theme_fg);
295
- terminal.get_color(Terminal::COLOR_BACKGROUND, &theme_bg);
379
+ return color;
380
+ }
296
381
 
297
- coord cw = self->cell_width;
298
- coord ch = self->cell_height;
382
+ static int
383
+ underline_style (uint attribs)
384
+ {
385
+ return (int) ((attribs & Terminal::Span::UNDERLINE_MASK) >> Terminal::Span::UNDERLINE_SHIFT);
386
+ }
387
+
388
+ static bool
389
+ has_decorations (uint attribs)
390
+ {
391
+ return
392
+ (attribs & (Terminal::Span::STRIKETHROUGH | Terminal::Span::OVERLINE)) ||
393
+ underline_style(attribs) != Terminal::Span::UNDERLINE_NONE;
394
+ }
395
+
396
+ static bool
397
+ is_blink_hidden (const Renderer::Data* self, uint attribs)
398
+ {
399
+ return (attribs & Terminal::Span::BLINK) && !self->blink_visible;
400
+ }
401
+
402
+ static void
403
+ draw_curly_underline (
404
+ Painter* painter, const Color& color,
405
+ coord left, coord top, coord width, coord cw, coord ch,
406
+ coord thickness, coord amplitude)
407
+ {
408
+ static constexpr int NSEGMENTS = 8;
409
+
410
+ coord center_y = top + ch - amplitude - thickness * 0.5f;
411
+ size_t npoints = (size_t) ceil(width / cw * NSEGMENTS) + 1;
412
+
413
+ std::vector<Point> points;
414
+ points.reserve(npoints);
415
+ for (size_t i = 0; i < npoints; ++i)
416
+ {
417
+ coord x = std::min((coord) (cw / NSEGMENTS * i), width);
418
+ points.emplace_back(left + x, center_y + amplitude * sin(x / cw * 2 * M_PI));
419
+ }
299
420
 
300
421
  painter->push_state();
301
- painter->set_font(self->font);
422
+ painter->no_fill();
423
+ painter->set_stroke(color);
424
+ painter->set_stroke_width(thickness);
425
+ painter->line(points.data(), points.size());
426
+ painter->pop_state();
427
+ }
302
428
 
303
- painter->set_fill(theme_bg);
304
- painter->rect(bounds);
429
+ static void
430
+ draw_decoration (
431
+ Painter* painter, uint attribs, const Color& color,
432
+ coord left, coord top, coord width, coord cw, coord ch)
433
+ {
434
+ coord amplitude = std::max((coord) 1, (coord) round(ch / 16));
435
+ coord t = amplitude;
436
+ if (attribs & Terminal::Span::BOLD) t = ceil(t * 1.5f);
437
+
438
+ painter->set_fill(color);
439
+ if (attribs & Terminal::Span::STRIKETHROUGH)
440
+ painter->rect(left, top + ch * 0.5f - t, width, t);
441
+ if (attribs & Terminal::Span::OVERLINE)
442
+ painter->rect(left, top, width, t);
443
+
444
+ coord bottom = top + ch - t;
445
+ switch (underline_style(attribs))
446
+ {
447
+ case Terminal::Span::UNDERLINE_SINGLE:
448
+ painter->rect(left, bottom, width, t);
449
+ break;
450
+
451
+ case Terminal::Span::UNDERLINE_DOUBLE:
452
+ painter->rect(left, bottom, width, t);
453
+ painter->rect(left, bottom - t * 2, width, t);
454
+ break;
455
+
456
+ case Terminal::Span::UNDERLINE_DOTTED:
457
+ for (coord x = 0; x < width; x += t * 2)
458
+ painter->rect(left + x, bottom, std::min(t, width - x), t);
459
+ break;
460
+
461
+ case Terminal::Span::UNDERLINE_DASHED:
462
+ for (coord x = 0, dash = cw / 3; x < width; x += dash * 2)
463
+ painter->rect(left + x, bottom, std::min(dash, width - x), t);
464
+ break;
465
+
466
+ case Terminal::Span::UNDERLINE_CURLY:
467
+ draw_curly_underline(painter, color, left, top, width, cw, ch, t, amplitude);
468
+ break;
469
+ }
470
+ }
471
+
472
+ static void
473
+ draw_backgrounds (
474
+ Renderer::Data* self, Painter* painter, const Terminal& terminal,
475
+ const Color& theme_fg, const Color& theme_bg)
476
+ {
477
+ coord cw = self->cell_width;
478
+ coord ch = self->cell_height;
305
479
 
306
- // every background first, then every glyph: alternating shapes
307
- // and images breaks the painter's batch and costs several times
308
- // more than the two passes do
309
480
  const Terminal::RowList& rows = terminal.spans();
310
481
  for (size_t y = 0; y < rows.size(); ++y)
311
482
  {
312
483
  for (const Terminal::Span& span : rows[y])
313
484
  {
314
- bool inverted = colors_inverted(span.attribs);
315
- Color fill = inverted ? to_color(span.fg, theme_fg) : to_color(span.bg, theme_bg);
316
- if (fill == theme_bg) continue;
485
+ if (colors_inverted(span.attribs))
486
+ painter->set_fill(to_color(span.fg, theme_fg));
487
+ else if (span.bg != Terminal::Span::COLOR_NONE)
488
+ painter->set_fill(to_color(span.bg, theme_bg));
489
+ else
490
+ continue;
317
491
 
318
- painter->set_fill(fill);
319
492
  painter->rect(span.x * cw, y * ch, span.width * cw, ch);
320
493
  }
321
494
  }
495
+ }
496
+
497
+ static void
498
+ draw_glyphs (
499
+ Renderer::Data* self, Painter* painter, const Terminal& terminal,
500
+ const Color& theme_fg, const Color& theme_bg)
501
+ {
502
+ coord cw = self->cell_width;
503
+ coord ch = self->cell_height;
322
504
 
323
505
  const Image& atlas = self->atlas;
324
506
  const uint* cell_offsets = Terminal_get_cell_offsets(terminal).data();
507
+
508
+ const Terminal::RowList& rows = terminal.spans();
325
509
  for (size_t y = 0; y < rows.size(); ++y)
326
510
  {
327
511
  for (const Terminal::Span& span : rows[y])
328
512
  {
329
- if (span.cell_size == 0) continue;
513
+ if (span.cell_size == 0)
514
+ continue;
515
+
516
+ if (span.attribs & Terminal::Span::INVISIBLE)
517
+ continue;
518
+
519
+ if (is_blink_hidden(self, span.attribs))
520
+ continue;
330
521
 
331
522
  bool inverted = colors_inverted(span.attribs);
332
- painter->set_fill(inverted ? to_color(span.bg, theme_bg) : to_color(span.fg, theme_fg));
523
+ painter->set_fill(to_text_color(
524
+ span.attribs,
525
+ inverted ? to_color(span.bg, theme_bg) : to_color(span.fg, theme_fg)));
526
+
527
+ int font_index = to_font_index(span.attribs);
528
+ const GlyphMap& glyphs = self->glyphs[font_index];
333
529
 
334
530
  // a span breaks where narrow meets wide, so every cell in
335
531
  // one steps the same distance
@@ -344,10 +540,11 @@ namespace ReflexTerminal
344
540
  uint end = i + 1 < span.cell_size ? offsets[i + 1] : (uint) span.text.size();
345
541
  String text(span.text.data() + begin, end - begin);
346
542
 
347
- auto it = self->glyphs.find(text);
348
- if (it == self->glyphs.end() || !it->second.is_valid())
543
+ auto it = glyphs.find(text);
544
+ if (it == glyphs.end() || !it->second.is_valid())
349
545
  {
350
546
  // the atlas is full: draw it the slow way
547
+ painter->set_font(self->fonts[font_index]);
351
548
  painter->text(text.c_str(), left, top);
352
549
  continue;
353
550
  }
@@ -357,6 +554,79 @@ namespace ReflexTerminal
357
554
  }
358
555
  }
359
556
  }
557
+ }
558
+
559
+ static Color
560
+ to_decoration_color (
561
+ const Terminal::Span& span, const Color& theme_fg, const Color& theme_bg)
562
+ {
563
+ if (span.ul != Terminal::Span::COLOR_NONE)
564
+ return to_color(span.ul, theme_fg);
565
+ else if (colors_inverted(span.attribs))
566
+ return to_color(span.bg, theme_bg);
567
+ else
568
+ return to_color(span.fg, theme_fg);
569
+ }
570
+
571
+ static void
572
+ draw_decorations (
573
+ Renderer::Data* self, Painter* painter, const Terminal& terminal,
574
+ const Color& theme_fg, const Color& theme_bg)
575
+ {
576
+ coord cw = self->cell_width;
577
+ coord ch = self->cell_height;
578
+
579
+ const Terminal::RowList& rows = terminal.spans();
580
+ for (size_t y = 0; y < rows.size(); ++y)
581
+ {
582
+ for (const Terminal::Span& span : rows[y])
583
+ {
584
+ if (!has_decorations(span.attribs))
585
+ continue;
586
+
587
+ if (is_blink_hidden(self, span.attribs))
588
+ continue;
589
+
590
+ draw_decoration(
591
+ painter, span.attribs,
592
+ to_text_color(span.attribs, to_decoration_color(span, theme_fg, theme_bg)),
593
+ span.x * cw, y * ch, span.width * cw, cw, ch);
594
+ }
595
+ }
596
+ }
597
+
598
+ void
599
+ Renderer::draw (Painter* painter, const Terminal& terminal, const Bounds& bounds)
600
+ {
601
+ if (!painter)
602
+ argument_error(__FILE__, __LINE__);
603
+ if (!self->fonts[0])
604
+ invalid_state_error(__FILE__, __LINE__);
605
+
606
+ // what the terminal has not been told is left to the renderer, so the
607
+ // theme starts here and get_color only writes over it when the child
608
+ // or the application has named a color of its own
609
+ Color theme_fg(1, 1, 1), theme_bg(0, 0, 0);
610
+ terminal.get_color(Terminal::COLOR_FOREGROUND, &theme_fg);
611
+ terminal.get_color(Terminal::COLOR_BACKGROUND, &theme_bg);
612
+
613
+ painter->push_state();
614
+ painter->set_font(self->fonts[0]);
615
+
616
+ if (self->background_alpha > 0)
617
+ {
618
+ Color background = theme_bg;
619
+ background.alpha = self->background_alpha;
620
+ painter->set_fill(background);
621
+ painter->rect(bounds);
622
+ }
623
+
624
+ // a pass per kind rather than span by span: alternating shapes and
625
+ // images would break the painter's batch and cost several times
626
+ // more. decorations go last, over the glyphs they cross
627
+ draw_backgrounds(self.get(), painter, terminal, theme_fg, theme_bg);
628
+ draw_glyphs( self.get(), painter, terminal, theme_fg, theme_bg);
629
+ draw_decorations(self.get(), painter, terminal, theme_fg, theme_bg);
360
630
 
361
631
  painter->pop_state();
362
632
  }