rays 0.3.11 → 0.3.12

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/painter.cpp CHANGED
@@ -49,7 +49,7 @@ namespace Rays
49
49
  if (!viewport)
50
50
  argument_error(__FILE__, __LINE__);
51
51
 
52
- if (self->painting)
52
+ if (self->is_painting())
53
53
  invalid_state_error(__FILE__, __LINE__, "painting flag should be false.");
54
54
 
55
55
  self->viewport = viewport;
@@ -71,7 +71,7 @@ namespace Rays
71
71
  bool
72
72
  Painter::painting () const
73
73
  {
74
- return self->painting;
74
+ return self->is_painting();
75
75
  }
76
76
 
77
77
  static inline void
@@ -102,7 +102,7 @@ namespace Rays
102
102
  {
103
103
  Painter::Data* self = painter->self.get();
104
104
 
105
- if (!self->painting)
105
+ if (!self->is_painting())
106
106
  invalid_state_error(__FILE__, __LINE__, "painting flag should be true.");
107
107
 
108
108
  if (!self->state.has_color())
@@ -328,19 +328,17 @@ namespace Rays
328
328
  Painter* painter, const Image& image,
329
329
  coord src_x, coord src_y, coord src_w, coord src_h,
330
330
  coord dst_x, coord dst_y, coord dst_w, coord dst_h,
331
- bool nofill, bool nostroke,
332
331
  const Shader* shader)
333
332
  {
334
- static const PrimitiveMode MODES[] = {MODE_TRIANGLE_FAN, MODE_LINE_LOOP};
335
-
336
333
  assert(painter && image);
337
334
 
338
335
  Painter::Data* self = painter->self.get();
339
336
 
340
- if (!self->painting)
337
+ if (!self->is_painting())
341
338
  invalid_state_error(__FILE__, __LINE__, "painting flag should be true.");
342
339
 
343
- if (!self->state.has_color())
340
+ Color color;
341
+ if (!self->state.get_color(&color, FILL))
344
342
  return;
345
343
 
346
344
  const Texture& texture = Image_get_texture(image);
@@ -365,19 +363,9 @@ namespace Rays
365
363
 
366
364
  TextureInfo texinfo(texture, src_x, src_y, src_x + src_w, src_y + src_h);
367
365
 
368
- Color color;
369
- for (int type = 0; type < COLOR_TYPE_MAX; ++type)
370
- {
371
- if ((nofill && type == FILL) || (nostroke && type == STROKE))
372
- continue;
373
-
374
- if (!painter->self->state.get_color(&color, (ColorType) type))
375
- continue;
376
-
377
- Painter_draw(
378
- painter, MODES[type], &color, points, 4, NULL, 0, NULL, texcoords,
379
- &texinfo, shader);
380
- }
366
+ Painter_draw(
367
+ painter, MODE_TRIANGLE_FAN, &color, points, 4, NULL, 0, NULL, texcoords,
368
+ &texinfo, shader);
381
369
  }
382
370
 
383
371
  void
@@ -477,7 +465,7 @@ namespace Rays
477
465
 
478
466
  Painter::Data* self = painter->self.get();
479
467
 
480
- if (!self->painting)
468
+ if (!self->is_painting())
481
469
  invalid_state_error(__FILE__, __LINE__, "painting flag should be true.");
482
470
 
483
471
  if (!self->state.has_color())
@@ -489,7 +477,7 @@ namespace Rays
489
477
  {
490
478
  coord line_height = painter->line_height();
491
479
 
492
- Xot::StringList lines;
480
+ StringList lines;
493
481
  split(&lines, str, '\n');
494
482
  for (const auto& line : lines)
495
483
  {
@@ -553,7 +541,7 @@ namespace Rays
553
541
  {
554
542
  self->state.background = color;
555
543
 
556
- if (self->painting && clear) this->clear();
544
+ if (self->is_painting() && clear) this->clear();
557
545
  }
558
546
 
559
547
  void
@@ -725,6 +713,11 @@ namespace Rays
725
713
  void
726
714
  Painter::set_clip (const Bounds& bounds)
727
715
  {
716
+ if (bounds == self->state.clip)
717
+ return;
718
+
719
+ Painter_flush(this);
720
+
728
721
  self->state.clip = bounds;
729
722
  Painter_update_clip(this);
730
723
  }
@@ -773,12 +766,21 @@ namespace Rays
773
766
  void
774
767
  Painter::set_texture (const Image& image)
775
768
  {
769
+ if (image == self->state.texture)
770
+ return;
771
+
772
+ Painter_flush(this);
773
+
776
774
  self->state.texture = image;
777
775
  }
778
776
 
779
777
  void
780
778
  Painter::no_texture ()
781
779
  {
780
+ if (!self->state.texture) return;
781
+
782
+ Painter_flush(this);
783
+
782
784
  self->state.texture = Image();
783
785
  }
784
786
 
@@ -791,6 +793,11 @@ namespace Rays
791
793
  void
792
794
  Painter::set_texcoord_mode (TexCoordMode mode)
793
795
  {
796
+ if (mode == self->state.texcoord_mode)
797
+ return;
798
+
799
+ Painter_flush(this);
800
+
794
801
  self->state.texcoord_mode = mode;
795
802
  }
796
803
 
@@ -803,6 +810,11 @@ namespace Rays
803
810
  void
804
811
  Painter::set_texcoord_wrap (TexCoordWrap wrap)
805
812
  {
813
+ if (wrap == self->state.texcoord_wrap)
814
+ return;
815
+
816
+ Painter_flush(this);
817
+
806
818
  self->state.texcoord_wrap = wrap;
807
819
  }
808
820
 
@@ -815,12 +827,21 @@ namespace Rays
815
827
  void
816
828
  Painter::set_shader (const Shader& shader)
817
829
  {
830
+ if (shader == self->state.shader)
831
+ return;
832
+
833
+ Painter_flush(this);
834
+
818
835
  self->state.shader = shader;
819
836
  }
820
837
 
821
838
  void
822
839
  Painter::no_shader ()
823
840
  {
841
+ if (!self->state.shader) return;
842
+
843
+ Painter_flush(this);
844
+
824
845
  self->state.shader = Shader();
825
846
  }
826
847
 
@@ -842,6 +863,8 @@ namespace Rays
842
863
  if (self->state_stack.empty())
843
864
  invalid_state_error(__FILE__, __LINE__, "state stack underflow.");
844
865
 
866
+ Painter_flush(this);
867
+
845
868
  self->state = self->state_stack.back();
846
869
  self->state_stack.pop_back();
847
870
  Painter_update_clip(this);
@@ -937,6 +960,24 @@ namespace Rays
937
960
  self->position_matrix_stack.pop_back();
938
961
  }
939
962
 
963
+ void
964
+ Painter::add_flag (uint flags)
965
+ {
966
+ Xot::add_flag(&self->flags, flags);
967
+ }
968
+
969
+ void
970
+ Painter::remove_flag (uint flags)
971
+ {
972
+ Xot::remove_flag(&self->flags, flags);
973
+ }
974
+
975
+ bool
976
+ Painter::has_flag (uint flags) const
977
+ {
978
+ return Xot::has_flag(self->flags, flags);
979
+ }
980
+
940
981
  Painter::operator bool () const
941
982
  {
942
983
  return self->viewport;
@@ -948,5 +989,19 @@ namespace Rays
948
989
  return !operator bool();
949
990
  }
950
991
 
992
+ static bool g_debug = false;
993
+
994
+ void
995
+ Painter::set_debug (bool debug)
996
+ {
997
+ g_debug = debug;
998
+ }
999
+
1000
+ bool
1001
+ Painter::debug ()
1002
+ {
1003
+ return g_debug;
1004
+ }
1005
+
951
1006
 
952
1007
  }// Rays
data/src/painter.h CHANGED
@@ -174,7 +174,14 @@ namespace Rays
174
174
  struct Painter::Data
175
175
  {
176
176
 
177
- bool painting = false;
177
+ enum Flag
178
+ {
179
+
180
+ PAINTING = Xot::bit(1, Painter::FLAG_LAST)
181
+
182
+ };// Flag
183
+
184
+ uint flags = Painter::FLAG_BATCHING;
178
185
 
179
186
  float pixel_density = 1;
180
187
 
@@ -197,6 +204,11 @@ namespace Rays
197
204
 
198
205
  virtual ~Data () = default;
199
206
 
207
+ bool is_painting () const
208
+ {
209
+ return Xot::has_flag(flags, PAINTING);
210
+ }
211
+
200
212
  void set_pixel_density (float density);
201
213
 
202
214
  };// Painter::Data
@@ -204,6 +216,8 @@ namespace Rays
204
216
 
205
217
  void Painter_update_clip (Painter* painter);
206
218
 
219
+ void Painter_flush (Painter* painter);
220
+
207
221
  void Painter_draw (
208
222
  Painter* painter, PrimitiveMode mode, const Color* color,
209
223
  const Coord3* points, size_t npoints,
@@ -217,7 +231,6 @@ namespace Rays
217
231
  Painter* painter, const Image& image,
218
232
  coord src_x, coord src_y, coord src_w, coord src_h,
219
233
  coord dst_x, coord dst_y, coord dst_w, coord dst_h,
220
- bool nofill = false, bool nostroke = false,
221
234
  const Shader* shader = NULL);
222
235
 
223
236
  void Painter_draw_text_line (
data/src/sdl/font.cpp CHANGED
@@ -1,6 +1,13 @@
1
1
  #include "../font.h"
2
2
 
3
3
 
4
+ #ifdef WASM
5
+ #include <stdlib.h>
6
+ #include <memory>
7
+ #include <emscripten.h>
8
+ #endif
9
+ #include <SDL.h>
10
+ #include <SDL_ttf.h>
4
11
  #include "rays/exception.h"
5
12
 
6
13
 
@@ -11,22 +18,324 @@ namespace Rays
11
18
  struct RawFont::Data
12
19
  {
13
20
 
14
- String path;
21
+ String name;
22
+
23
+ coord size = 0;
24
+
25
+ virtual ~Data ()
26
+ {
27
+ }
28
+
29
+ virtual void draw_string (SDL_Surface* target, const char* str, coord x, coord y)
30
+ {
31
+ }
32
+
33
+ virtual coord get_width (const char* str)
34
+ {
35
+ return 0;
36
+ }
37
+
38
+ virtual coord get_height (coord* ascent, coord* descent, coord* leading)
39
+ {
40
+ if (ascent) *ascent = 0;
41
+ if (descent) *descent = 0;
42
+ if (leading) *leading = 0;
43
+ return 0;
44
+ }
45
+
46
+ virtual bool is_valid () const
47
+ {
48
+ return false;
49
+ }
50
+
51
+ virtual Data* dup (coord size) const
52
+ {
53
+ return NULL;
54
+ }
15
55
 
16
56
  };// RawFont::Data
17
57
 
18
58
 
59
+ struct SDLFontData : public RawFont::Data
60
+ {
61
+
62
+ TTF_Font* font = NULL;
63
+
64
+ String path;
65
+
66
+ SDLFontData (const char* path, coord size)
67
+ {
68
+ if (!path)
69
+ argument_error(__FILE__, __LINE__);
70
+
71
+ font = TTF_OpenFont(path, (int) size);
72
+ if (!font)
73
+ rays_error(__FILE__, __LINE__, "failed to open font: %s", TTF_GetError());
74
+
75
+ this->path = path;
76
+ this->size = size;
77
+
78
+ const char* family = TTF_FontFaceFamilyName(font);
79
+ if (family) this->name = family;
80
+ }
81
+
82
+ ~SDLFontData () override
83
+ {
84
+ if (font) TTF_CloseFont(font);
85
+ }
86
+
87
+ void draw_string (SDL_Surface* target, const char* str, coord x, coord y) override
88
+ {
89
+ SDL_Surface* surface = TTF_RenderUTF8_Blended(font, str, {255, 255, 255, 255});
90
+ if (!surface)
91
+ rays_error(__FILE__, __LINE__, "TTF_RenderUTF8_Blended failed: %s", TTF_GetError());
92
+
93
+ SDL_Rect dst = {(int) x, (int) y, surface->w, surface->h};
94
+ SDL_FillRect(target, &dst, 0);
95
+ SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
96
+ SDL_BlitSurface(surface, NULL, target, &dst);
97
+ SDL_FreeSurface(surface);
98
+ }
99
+
100
+ coord get_width (const char* str) override
101
+ {
102
+ int w = 0, h = 0;
103
+ if (TTF_SizeUTF8(font, str, &w, &h) < 0)
104
+ rays_error(__FILE__, __LINE__, "TTF_SizeUTF8 failed: %s", TTF_GetError());
105
+
106
+ return (coord) w;
107
+ }
108
+
109
+ coord get_height (coord* ascent, coord* descent, coord* leading) override
110
+ {
111
+ int asc = TTF_FontAscent(font);
112
+ int desc = -TTF_FontDescent(font);// TTF returns negative descent
113
+ int skip = TTF_FontLineSkip(font);
114
+
115
+ if (ascent) *ascent = (coord) asc;
116
+ if (descent) *descent = (coord) desc;
117
+ if (leading) *leading = (coord) (skip - asc - desc);
118
+
119
+ return (coord) (asc + desc);
120
+ }
121
+
122
+ bool is_valid () const override
123
+ {
124
+ return font;
125
+ }
126
+
127
+ Data* dup (coord size) const override
128
+ {
129
+ return new SDLFontData(path.c_str(), size);
130
+ }
131
+
132
+ };// SDLData
133
+
134
+
135
+ #ifdef WASM
136
+ // Browser-based font implementation: render text via an offscreen
137
+ // HTMLCanvasElement and copy the pixels into an SDL_Surface.
138
+
139
+ EM_JS(void, rays_wasm_font_init_, (),
140
+ {
141
+ if (Module._raysFontCanvas) return;
142
+ Module._raysFontCanvas = document.createElement('canvas');
143
+ Module._raysFontContext = Module._raysFontCanvas.getContext('2d');
144
+ });
145
+
146
+ EM_JS(double, rays_wasm_font_text_width_, (
147
+ const char* str, const char* name, double size),
148
+ {
149
+ const s = UTF8ToString(str);
150
+ const n = UTF8ToString(name);
151
+ const c = Module._raysFontContext;
152
+ c.font = `${size}px "${n}"`;
153
+ return c.measureText(s).width;
154
+ });
155
+
156
+ EM_JS(void, rays_wasm_font_get_metrics_, (
157
+ const char* name, double size,
158
+ double* out_ascent, double* out_descent, double* out_leading),
159
+ {
160
+ const n = UTF8ToString(name);
161
+ const c = Module._raysFontContext;
162
+ c.font = `${size}px "${n}"`;
163
+ const m = c.measureText('Mgjpqy');
164
+ const asc = Math.max(
165
+ m. fontBoundingBoxAscent ?? 0,
166
+ m.actualBoundingBoxAscent ?? size * 0.8);
167
+ const dsc = Math.max(
168
+ m. fontBoundingBoxDescent ?? 0,
169
+ m.actualBoundingBoxDescent ?? size * 0.2);
170
+
171
+ setValue(out_ascent, asc, 'double');
172
+ setValue(out_descent, dsc, 'double');
173
+ setValue(out_leading, size * 0.2, 'double');
174
+ });
175
+
176
+ EM_JS(void*, rays_wasm_font_render_, (
177
+ const char* str, const char* name, double size,
178
+ int* out_width, int* out_height, double* out_ascent, double* out_descent),
179
+ {
180
+ const s = UTF8ToString(str);
181
+ const n = UTF8ToString(name);
182
+ const c = Module._raysFontContext;
183
+ const cv = Module._raysFontCanvas;
184
+ c.font = `${size}px "${n}"`;
185
+ const m = c.measureText(s);
186
+ const asc = Math.max(
187
+ m. fontBoundingBoxAscent ?? 0,
188
+ m.actualBoundingBoxAscent ?? size * 0.8);
189
+ const dsc = Math.max(
190
+ m. fontBoundingBoxDescent ?? 0,
191
+ m.actualBoundingBoxDescent ?? size * 0.2);
192
+ const w = Math.max(1, Math.ceil(m.width));
193
+ const h = Math.max(1, Math.ceil(asc + dsc));
194
+
195
+ if (cv.width < w) cv.width = w;
196
+ if (cv.height < h) cv.height = h;
197
+
198
+ c.clearRect(0, 0, cv.width, cv.height);
199
+ c.font = `${size}px "${n}"`;// re-apply after canvas resize
200
+ c.fillStyle = 'white';
201
+ c.textBaseline = 'alphabetic';
202
+ c.fillText(s, 0, asc);
203
+
204
+ const data = c.getImageData(0, 0, w, h).data;
205
+ const ptr = _malloc(w * h * 4);
206
+ HEAPU8.set(data, ptr);
207
+ setValue(out_width, w, 'i32');
208
+ setValue(out_height, h, 'i32');
209
+ setValue(out_ascent, asc, 'double');
210
+ setValue(out_descent, dsc, 'double');
211
+ return ptr;
212
+ });
213
+
214
+ EM_JS(const char*, rays_wasm_font_families_, (),
215
+ {
216
+ const list = [
217
+ 'sans-serif', 'serif', 'monospace', 'cursive', 'fantasy',
218
+ 'system-ui', 'ui-sans-serif', 'ui-serif', 'ui-monospace',
219
+ 'Arial', 'Helvetica', 'Times New Roman', 'Times', 'Courier New',
220
+ 'Courier', 'Verdana', 'Georgia', 'Trebuchet MS', 'Comic Sans MS',
221
+ 'Impact', 'Tahoma', 'Lucida Sans Unicode', 'Lucida Console',
222
+ 'Palatino', 'Garamond', 'Book Antiqua', 'Hiragino Sans',
223
+ 'Hiragino Kaku Gothic ProN', 'Hiragino Mincho ProN',
224
+ 'Yu Gothic', 'Yu Mincho', 'Meiryo', 'MS Gothic', 'MS Mincho'
225
+ ];
226
+ const joined = list.join('\0') + '\0';
227
+ const len = lengthBytesUTF8(joined) + 1;
228
+ const ptr = _malloc(len);
229
+ stringToUTF8(joined, ptr, len);
230
+ return ptr;
231
+ });
232
+
233
+
234
+ struct CanvasFontData : public RawFont::Data
235
+ {
236
+
237
+ CanvasFontData (const char* name, coord size)
238
+ {
239
+ rays_wasm_font_init_();
240
+
241
+ this->name = name && *name != '\0' ? name : "sans-serif";
242
+ this->size = size;
243
+ }
244
+
245
+ void draw_string (SDL_Surface* target, const char* str, coord x, coord y) override
246
+ {
247
+ int w = 0, h = 0;
248
+ double ascent = 0, descent = 0;
249
+ std::shared_ptr<void> pixels(
250
+ rays_wasm_font_render_(
251
+ str, name.c_str(), (double) size,
252
+ &w, &h, &ascent, &descent),
253
+ free);
254
+ if (!pixels || w <= 0 || h <= 0) return;
255
+
256
+ SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(
257
+ pixels.get(), w, h, 32, w * 4,
258
+ 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
259
+ if (!surface)
260
+ rays_error(__FILE__, __LINE__, "SDL_CreateRGBSurfaceFrom failed: %s", SDL_GetError());
261
+
262
+ SDL_Rect dest = {(int) x, (int) y, w, h};
263
+ SDL_FillRect(target, &dest, 0);
264
+ SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
265
+ SDL_BlitSurface(surface, NULL, target, &dest);
266
+ SDL_FreeSurface(surface);
267
+ }
268
+
269
+ coord get_width (const char* str) override
270
+ {
271
+ return (coord) rays_wasm_font_text_width_(str, name.c_str(), (double) size);
272
+ }
273
+
274
+ coord get_height (coord* ascent, coord* descent, coord* leading) override
275
+ {
276
+ double asc = 0, desc = 0, lead = 0;
277
+ rays_wasm_font_get_metrics_(name.c_str(), (double) size, &asc, &desc, &lead);
278
+
279
+ if (ascent) *ascent = (coord) asc;
280
+ if (descent) *descent = (coord) desc;
281
+ if (leading) *leading = (coord) lead;
282
+
283
+ return (coord) (asc + desc);
284
+ }
285
+
286
+ bool is_valid () const override
287
+ {
288
+ return size > 0 && !name.empty();
289
+ }
290
+
291
+ Data* dup (coord size) const override
292
+ {
293
+ return new CanvasFontData(name.c_str(), size);
294
+ }
295
+
296
+ };// CanvasData
297
+ #endif
298
+
299
+
300
+
19
301
  const FontFamilyMap&
20
302
  get_font_families ()
21
303
  {
304
+ #ifdef WASM
305
+ static const FontFamilyMap MAP = []()
306
+ {
307
+ rays_wasm_font_init_();
308
+ FontFamilyMap map;
309
+ const char* head = rays_wasm_font_families_();
310
+ const char* base = head;
311
+ while (head && *head)
312
+ {
313
+ String name(head);
314
+ if (!name.empty())
315
+ {
316
+ FontFamilyMap::mapped_type array;
317
+ array.emplace_back(name);
318
+ map[name] = array;
319
+ }
320
+ head += name.size() + 1;
321
+ }
322
+ free((void*) base);
323
+ return map;
324
+ }();
325
+ return MAP;
326
+ #else
22
327
  static const FontFamilyMap MAP;
23
328
  return MAP;
329
+ #endif
24
330
  }
25
331
 
332
+
26
333
  RawFont
27
334
  RawFont_load (const char* path, coord size)
28
335
  {
29
- return RawFont();
336
+ RawFont rawfont;
337
+ rawfont.self.reset(new SDLFontData(path, size));
338
+ return rawfont;
30
339
  }
31
340
 
32
341
 
@@ -34,12 +343,28 @@ namespace Rays
34
343
  {
35
344
  }
36
345
 
346
+ static RawFont::Data*
347
+ create_data (const char* name, coord size)
348
+ {
349
+ #ifdef WASM
350
+ return new CanvasFontData(name, size);
351
+ #else
352
+ if (name)
353
+ not_implemented_error(__FILE__, __LINE__);
354
+ return new SDLFontData("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", size);
355
+ #endif
356
+ }
357
+
37
358
  RawFont::RawFont (const char* name, coord size)
359
+ : self(create_data(name, size))
38
360
  {
39
361
  }
40
362
 
41
363
  RawFont::RawFont (const This& obj, coord size)
42
364
  {
365
+ if (!obj) return;
366
+
367
+ self.reset(obj.self->dup(size));
43
368
  }
44
369
 
45
370
  RawFont::~RawFont ()
@@ -48,39 +373,63 @@ namespace Rays
48
373
 
49
374
  void
50
375
  RawFont::draw_string (
51
- void* context_, coord context_height,
376
+ void* context, coord context_height,
52
377
  const char* str, coord x, coord y) const
53
378
  {
379
+ SDL_Surface* target = (SDL_Surface*) context;
380
+
381
+ if (!target)
382
+ argument_error(__FILE__, __LINE__);
383
+ if (!str)
384
+ argument_error(__FILE__, __LINE__);
385
+
386
+ if (*str == '\0') return;
387
+
388
+ if (!*this)
389
+ invalid_state_error(__FILE__, __LINE__);
390
+
391
+ self->draw_string(target, str, x, y);
54
392
  }
55
393
 
56
394
  String
57
395
  RawFont::name () const
58
396
  {
59
- return "";
397
+ if (!*this) return "";
398
+ return self->name;
60
399
  }
61
400
 
62
401
  coord
63
402
  RawFont::size () const
64
403
  {
65
- return 0;
404
+ if (!*this) return 0;
405
+ return self->size;
66
406
  }
67
407
 
68
408
  coord
69
409
  RawFont::get_width (const char* str) const
70
410
  {
71
- if (!str || *str == '\0') return 0;
72
- return 0;
411
+ if (!str)
412
+ argument_error(__FILE__, __LINE__);
413
+ if (!*this)
414
+ invalid_state_error(__FILE__, __LINE__);
415
+
416
+ if (*str == '\0') return 0;
417
+
418
+ return self->get_width(str);
73
419
  }
74
420
 
75
421
  coord
76
422
  RawFont::get_height (coord* ascent, coord* descent, coord* leading) const
77
423
  {
78
- return 0;
424
+ if (!*this)
425
+ invalid_state_error(__FILE__, __LINE__);
426
+
427
+ return self->get_height(ascent, descent, leading);
79
428
  }
80
429
 
81
430
  RawFont::operator bool () const
82
431
  {
83
- return true;
432
+ return self && self->is_valid();
84
433
  }
85
434
 
86
435
  bool