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.
- checksums.yaml +4 -4
- data/.doc/ext/rays/image.cpp +10 -0
- data/.doc/ext/rays/painter.cpp +49 -1
- data/.doc/ext/rays/shader.cpp +8 -6
- data/.github/workflows/release-gem.yml +3 -0
- data/.github/workflows/utils.rb +88 -17
- data/ChangeLog.md +17 -0
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/ext/rays/extconf.rb +3 -4
- data/ext/rays/image.cpp +11 -0
- data/ext/rays/painter.cpp +53 -1
- data/ext/rays/shader.cpp +8 -6
- data/include/rays/coord.h +6 -6
- data/include/rays/defs.h +2 -0
- data/include/rays/image.h +11 -1
- data/include/rays/painter.h +19 -0
- data/include/rays/ruby.h +2 -2
- data/include/rays/shader.h +5 -3
- data/include/rays.h +2 -2
- data/lib/rays/extension.rb +8 -2
- data/lib/rays/image.rb +2 -1
- data/lib/rays/shader.rb +13 -4
- data/rays.gemspec +3 -4
- data/src/color_space.cpp +1 -1
- data/src/coord.h +10 -0
- data/src/font.cpp +1 -1
- data/src/image.cpp +85 -10
- data/src/opengl/painter.cpp +388 -124
- data/src/opengl/render_buffer.cpp +1 -1
- data/src/opengl/sdl/opengl.cpp +6 -0
- data/src/opengl/shader.cpp +68 -51
- data/src/opengl/shader.h +10 -6
- data/src/opengl/shader_program.cpp +21 -7
- data/src/opengl/shader_program.h +2 -1
- data/src/opengl/shader_source.cpp +2 -4
- data/src/opengl/texture.cpp +18 -6
- data/src/osx/bitmap.mm +1 -1
- data/src/painter.cpp +79 -24
- data/src/painter.h +15 -2
- data/src/sdl/font.cpp +358 -9
- data/src/sdl/rays.cpp +5 -0
- data/src/texture.h +6 -0
- data/test/test_painter.rb +36 -25
- data/test/test_painter_batch.rb +254 -0
- metadata +8 -6
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->
|
|
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->
|
|
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->
|
|
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->
|
|
337
|
+
if (!self->is_painting())
|
|
341
338
|
invalid_state_error(__FILE__, __LINE__, "painting flag should be true.");
|
|
342
339
|
|
|
343
|
-
|
|
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
|
-
|
|
369
|
-
|
|
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->
|
|
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
|
-
|
|
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->
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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*
|
|
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
|
|
72
|
-
|
|
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
|
-
|
|
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
|
|
432
|
+
return self && self->is_valid();
|
|
84
433
|
}
|
|
85
434
|
|
|
86
435
|
bool
|