-
Notifications
You must be signed in to change notification settings - Fork 9
/
quick_reference.h
1040 lines (803 loc) · 36.8 KB
/
quick_reference.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//**** Macros ****
// Macros you can define in your rl.h before including rouziclib.h
#define COL_FRGB
// Makes col_t be frgb_t rather than lrgb_t. Needed if not using the LRGB graphics system
#define LRGB_NODITHER
// Used when using the LRGB graphics system to disable dithering
#define LBD 12
// Sets the bit depth for the LRGB graphics system. Should be between 12 and 15, 15 is the default when this is not defined.
#define GAUSSLIMIT 0.001
// Intensity limit for drawing Gaussian drawing functions. Can be set if you want better quality or more trimmed Gaussians for speed. Default is 0.0002.
#define GAUSSRAD_HQ 3.
// Drawing radius limit for other Gaussian drawing functions. Can be set if you want better quality or more trimmed Gaussians for speed. Default is 4.
#define DQS_THREADS 8
// Number of CPU threads for software rendering of drawing queues (a sort of non-OpenCL fallback for the OpenCL drawing queue graphics system). Default is 1, a good value is ~8.
#define RL_DONT_ASSUME_LITTLE_ENDIAN
// If not defined then endianness-sensitive code asssumes little endianness. Definining RL_DONT_ASSUME_LITTLE_ENDIAN makes all code work properly if ever executed in big endian.
#define RL_INCL_UNICODE_DATA_MINI
#define RL_INCL_VECTOR_TYPE_FILEBALL
// Needed for vector font displaying used in the GUI system
#define RL_INCL_UNICODE_DATA
// Includes more detailed Unicode data for each Unicode character, which takes more room
#define RL_SDL
#define RL_OPENCL
#define RL_OPENCL_GL
#define RL_BUILTIN_GLEW
// Includes the parts of rouziclib that use SDL2, OpenCL, the OpenCL-OpenGL interop and my cut-down version of GLEW for using OpenGL
#define RL_EXCL_APPROX
// Excludes the approximation functions that use lookup tables
#define RL_GDI32
// Includes Windows code for taking screenshots. Needs linking to gdi32.lib
#define RL_CRASHDUMP
// Includes the Windows code for producing a crash dump
#define RL_INCL_NETWORK
// Includes network socket code
#define RL_MPFR
#define RL_OPENCL
#define RL_VULKAN
#define RL_CLFFT
#define RL_FFMPEG
#define RL_OPENCV
#define RL_DEVIL
#define RL_LIBSNDFILE
#define RL_LIBRAW
#define RL_LIBJPEG
#define RL_LIBCURL
#define RL_MINIAUDIO
#define RL_OPENAL
#define RL_TINYEXPR
#define RL_IMAGE_FILE
#define RL_SOUND_FILE
#define RL_FFTPACK
#define RL_CFFTPACK
// Include various libraries and the code that use them. Some are excluded by default due to requiring external dependencies, others are excluded due to code size.
#define RL_ZLIB
#define RL_MINIZ
// Includes Zlib or Miniz. If neither is defined then a cutdown Inflate-only version of Miniz is used.
//**** Colour ****
// Make RGBA colour (linear arguments)
col_t colour = make_colour(1., 0.184, 0., 1.);
// Make HSL colour
// make_colour_hsl(hue, sat, lum (linear), hue type, sec_boost)
col_t colour = make_colour_hsl(30., 1., 0.125, HUEDEG, 0);
// Get colour from sinusoidal colour sequence
// get_colour_seq(double x, xyz_t freq, xyz_t phase)
col = get_colour_seq((double) i+1.5, xyz(0.36, 0.187, 0.13), set_xyz(0.2));
//**** Draw elements ****
// Line
// pixel coordinates
draw_line_thin(sc_xy(p0), sc_xy(p1), drawing_thickness, white, blend_add, intensity);
// Point
// pixel coordinates
draw_point(sc_xy(p), drawing_thickness, white, blend_add, intensity);
// Rectangles
// pixel coordinates
draw_rect(sc_rect(box), drawing_thickness, colour, blend_add, intensity);
draw_rect_full(sc_rect(box), drawing_thickness, colour, blend_add, intensity);
draw_black_rect(sc_rect(box), drawing_thickness, intensity);
draw_black_rect_inverted(sc_rect(box), drawing_thickness, 1.);
// Circle (HOLLOWCIRCLE or FULLCIRCLE)
// pixel coordinates
draw_circle(HOLLOWCIRCLE, sc_xy(circle_centre), circle_radius, drawing_thickness, colour, blend_add, intensity);
// Full polygon
xy_t p[4] = {...};
draw_polygon_wc(p, 4, drawing_thickness, colour, blend_add, intensity);
// Text label
// world coordinates
draw_label("Control", offset_scale_rect(box, offset, sm), col, ALIG_CENTRE | MONODIGITS);
//**** Controls ****
// Single button
// returns 1 when released
if (ctrl_button_chamf("Load", offset_scale_rect(box, offset, sm), colour))
// invisible version
ctrl_button_invis(offset_scale_rect(box, offset, sm), NULL)
// or
ctrl_button_state_t butt_state={0};
ctrl_button_invis(offset_scale_rect(box, offset, sm), &butt_state)
// Single checkbox
// returns 1 if state changed
// state = ptr to single int value
ctrl_checkbox(&state, "Tick this", offset_scale_rect(box, offset, sm), colour);
if (ctrl_checkbox(&state, "Tick this", offset_scale_rect(box, offset, sm), colour))
change = 1;
// Single radio
// returns 1 when the radio button is selected (much like a normal button)
static int sel_id = 0;
if (ctrl_radio(sel_id==i, "Option 1", offset_scale_rect(box, offset, sm), colour))
sel_id = i;
// making it select nothing by clicking again
if (ctrl_radio(sel_id==i, "Option 1", offset_scale_rect(box, offset, sm), colour))
sel_id = (sel_id==i) ? -1 : i;
// List of radio controls
// init radio_sel with the default choice, not necessarily 0
// the box is for the first control, it's reproduced by an offset for the other controls
static int radio_sel=0;
const char *radio_labels[] = {"Radio 1", "Radio 2", "Radio 3", "Radio 4"};
ctrl_array_radio(&radio_sel, sizeof(radio_labels)/sizeof(*radio_labels), radio_labels, &col, 1, offset_scale_rect(box, offset, sm), xy(0., -step*sm));
// Knob
// returns 2 if the value is being changed, 1 when the change is final
// knobf functions are knobf_linear, knobf_log, knobf_recip, knobf_dboff, knobf_logoff, knobf_tan
static double value=NAN;
static knob_t value_knob={0};
if (value_knob.main_label==NULL)
value_knob = make_knob("Knob name", default_value, knobf_linear, min_value, max_value, VALFMT_DEFAULT);
ctrl_knob(&value, &value_knob, box, colour);
// free with this
textedit_free(&value_knob.edit);
// Text editor
static textedit_t te={0};
if (te.string==NULL) // if te.string is NULL it's not initialised
te = string_to_textedit(make_string_copy(string));
// or (not needed as ctrl_textedit() can do it too)
textedit_init(&te, 1);
// some options can be specified like:
te.edit_mode = te_mode_value;
te.max_scale = 1./6.;
te.rect_brightness = 0.25;
te.first_click_no_sel = 1;
// or
get_textedit_fromlayout(&layout, id)->first_click_no_sel = 1;
// returns 1 if Enter (or sometimes Tab) is pressed, 2 when clicked out of it, 3 when tabbed out, 4 when modified
if (ctrl_textedit(&te, offset_scale_rect(box, offset, sm), colour))
// set next text while saving the previous one in the undo
textedit_set_new_text(&te, "Label");
// remove all the undo history then set the next text
textedit_clear_then_set_new_text(&te, "Label");
// Resizing rectangle
static ctrl_resize_rect_t resize_state={0};
static rect_t resize_box={0};
if (resize_state.init==0)
resize_box = make_rect_centred( XY0, XY1 );
ctrl_resizing_rect(&resize_state, &resize_box);
// Disable input processing for some controls
// this works by saving the whole state of the control identification and restoring it to ignore any controls in between
static mouse_ctrl_id_t ctrl_id_save={0};
ctrl_id_save = *mouse.ctrl_id;
ctrl_something();
*mouse.ctrl_id = ctrl_id_save;
//**** Controls from text layout ****
// How to make a new layout
static gui_layout_t layout={0};
const char *layout_src[] = {""};
gui_layout_init_pos_scale(&layout, XY0, 1., XY0, 0);
make_gui_layout(&layout, layout_src, sizeof(layout_src)/sizeof(char *), "Layout name");
// Put it in the upper left corner
gui_layout_init_pos_scale(&layout, add_xy(neg_x(zc.limit_u), neg_y(set_xy(0.25))), 1., XY0, 0);
// Make it float permanently
layout.offset = zc.offset_u;
layout.sm = 1./zc.zoomscale;
// Make a floating window
// 2nd argument is a int * that can be NULL if a close button isn't needed
// 5th argument must be a layout ID which by its rect defines the size of the window in local coordinates
// the optional 3rd argument is a rect_t * representing the area where to put the window back if "closed" (actually undetached). If not NULL window must not be initialised to pinned
static flwindow_t window={0};
flwindow_init_defaults(&window);
flwindow_init_pinned(&window);
draw_dialog_window_fromlayout(&window, cur_wind_on, NULL, &layout, 0);
// Double-clicking the pin control moves the window to the upper-left corner with a default scale which can be modified
window.pinned_offset_preset = xy(1e9, 1e9); // upper right corner
window.pinned_sm_preset = 1.4;
// Background opacity and shadow intensity can be modified
window.bg_opacity = 0.94;
window.shadow_strength = 0.5*window.bg_opacity;
// The dark background can be rendered even when the window isn't detached which is needed if there might be something drawn beneath the undetached window
window.draw_bg_always = 1;
// Example of window-defining elem
"elem 0", "type none", "label Window Bar Title", "pos 0", "dim 8 6", "off 0 1", "",
// Floating pinned window full template
static gui_layout_t layout={0};
const char *layout_src[] = {
"elem 0", "type none", "label Window Bar Title", "pos 0", "dim 8 6", "off 0 1", "",
};
gui_layout_init_pos_scale(&layout, neg_x(zc.limit_u), 1., XY0, 0);
make_gui_layout(&layout, layout_src, sizeof(layout_src)/sizeof(char *), "Layout name");
if (mouse.window_minimised_flag > 0)
return;
static flwindow_t window={0};
flwindow_init_defaults(&window);
flwindow_init_pinned(&window);
draw_dialog_window_fromlayout(&window, NULL, NULL, &layout, 0);
// Fit a sublayout into a layout's rectangle
// offset decides towards which edges the fitting will go if the aspect ratios don't match
fit_sublayout_into_layout_rect(&window_layout, id_to_fit_into, &sublayout, id_of_frame, offset);
// Fit a rectangle into a layout's rectangle and get the offset and scale multiplier
// offset is the same as above, so set_xy(0.5) would centre everything
pos = fit_area_into_layout_rect(&layout, id, area, offset, &sm);
// Get the rect of a layout element
// the last argument is the provided offset
gui_layout_elem_comp_area_os(&layout, id, XY0);
// Set colour of layout element
gui_set_control_colour(col, &layout, id);
// Label
draw_label_fromlayout(&layout, id, ALIG_CENTRE | MONODIGITS);
// optionally the label can be set prior to drawing using this:
gui_printf_to_label(&layout, id, 0, "value %d", some_value); // the 3rd argument is append
// Rect
// the first argument is 0 for outline rect, 1 for full rect, 2 for black rect
draw_rect_fromlayout(0, &layout, id);
// Button
if (ctrl_button_fromlayout(&layout, id))
if (ctrl_button_invis_fromlayout(NULL, &layout, id))
ctrl_button_state_t butt_state={0};
if (ctrl_button_invis_fromlayout(&butt_state, &layout, id))
// Checkbox
static int state=0;
ctrl_checkbox_fromlayout(&state, &layout, id);
// Knob
static double value=NAN;
ctrl_knob_fromlayout(&value, &layout, id);
// set knob attribute
get_knob_data_fromlayout(&layout, id)->min = 0.;
// set knob circularity
set_knob_circularity_fromlayout(1, &layout, id);
// Text editor
// returns 1 if Enter used, 2 if clicked out, 3 if Tab used, 4 if probably modified
ctrl_textedit_fromlayout(&layout, id);
// set text
// the third argument is clear_undo
print_to_layout_textedit(&layout, id, 1, "");
// get textedit string
string = get_textedit_string_fromlayout(&layout, id);
// Example: using a text editor as a read-only log
get_textedit_fromlayout(&layout, id)->read_only = 1;
get_textedit_fromlayout(&layout, id)->draw_string_mode = ALIG_LEFT | MONOSPACE;
print_to_layout_textedit_append(&layout, id, 1, "");
ctrl_textedit_fromlayout(&layout, id);
// Scrollable textedit
get_textedit_fromlayout(&layout, id)->scroll_mode = 1;
get_textedit_fromlayout(&layout, id)->scroll_mode_scale_def = 40. * 4.5;
// Selection menu
const char *menu_opts[] = { "Opt 1", "Opt 2" };
int menu_count = sizeof(menu_opts)/sizeof(char*);
gui_layout_selmenu_set_count(menu_count, &layout, id);
if (ctrl_selmenu_fromlayout(&layout, id))
opt = menu_opts[get_selmenu_selid_fromlayout(&layout, id)];
for (i=0; i < menu_count; i++)
draw_selmenu_entry_fromlayout(i, menu_opts[i], &layout, id);
// set the default option by id
gui_layout_selmenu_set_entry_id(7, &layout, id);
//**** Window manager ****
// Add window_manager() where the windows are ran in a variable order in the main loop before mousecursor_logic_and_draw();
window_manager();
// Window functions must follow this template
void my_window_function(<my_ptr_type *ptr1, my_ptr_type *ptr2 ...>)
// Window functions are registered like this
window_register(1, my_window_function, NULL, gui_layout_elem_comp_area_os(&layout, 100, XY0), &diag_on, 2, &arg1, &arg2);
// or like this if the window is always floating and its position self-defined
window_register(1, my_window_function, NULL, RECTNAN, NULL, 2, &arg1, &arg2);
// Registered window function template with parent area
void my_window_function(double *arg1, double *arg2)
{
static gui_layout_t layout={0};
const char *layout_src[] = {
"elem 0", "type none", "label Window_title", "pos 0 0", "dim 4 4", "off 0 1", "",
};
layout.sm = 1.;
make_gui_layout(&layout, layout_src, sizeof(layout_src)/sizeof(char *), "my_window_function");
if (mouse.window_minimised_flag > 0)
return;
static flwindow_t window={0};
flwindow_init_defaults(&window);
window.bg_opacity = 0.94;
window.shadow_strength = 0.5*window.bg_opacity;
window.pinned_sm_preset = 1.2;
draw_dialog_window_fromlayout(&window, cur_wind_on, &cur_parent_area, &layout, 0);
// Controls
}
// Windows inside other windows
// The root function registers the parent window function and the child windows
// After registering the detached child window function window_set_parent() can be called so that the child window is always on top of the parent window
// Child windows must be registered before the parent window so that the parent window can set their parent areas in case it runs right away
void parent_window_function(int *child1_detach, int *child2_detach)
{
static flwindow_t window={0};
static gui_layout_t layout={0};
const char *layout_src[] = {
"elem 0", "type none", "label Parent window", "pos 0 0", "dim 7;2;6 5;9", "off 0 1", "",
"elem 100", "type rect", "pos 0;6 -1", "dim 1;9 4", "off 0 1", "",
"elem 110", "type rect", "link_pos_id 100", "pos 2;1 0", "dim 2;10 2;6", "off 0 1", "",
};
make_gui_layout(&layout, layout_src, sizeof(layout_src)/sizeof(char *), "Parent window layout");
if (mouse.window_minimised_flag > 0)
return;
// Window
flwindow_init_defaults(&window);
draw_dialog_window_fromlayout(&window, cur_wind_on, NULL, &layout, 0);
// Set parent area for child windows
window_set_parent_area(child1_func, NULL, gui_layout_elem_comp_area_os(&layout, 100, XY0));
window_set_parent_area(child2_func, NULL, gui_layout_elem_comp_area_os(&layout, 110, XY0));
}
void root_function(int *diag_on, rect_t parent_area)
{
static int child1_detach=0, child2_detach=0;
// Sub-windows
window_register(1, child1_func, NULL, RECTNAN, &child1_detach, 0);
window_set_parent(child1_func, NULL, parent_window_function, NULL);
window_register(1, child2_func, NULL, RECTNAN, &child2_detach, 0);
window_set_parent(child2_func, NULL, parent_window_function, NULL);
// Window
if (*diag_on)
window_register(1, parent_window_function, NULL, parent_area, diag_on, 2, &child1_detach, &child2_detach);
}
void child1_func()
{
static gui_layout_t layout={0};
const char *layout_src[] = {
"elem 0", "type none", "label Child1 window", "pos 0 0", "dim 3 3;6", "off 0 1", "",
};
make_gui_layout(&layout, layout_src, sizeof(layout_src)/sizeof(char *), "Child 1");
if (mouse.window_minimised_flag > 0)
return;
// Window
static flwindow_t window={0};
flwindow_init_defaults(&window);
draw_dialog_window_fromlayout(&window, cur_wind_on, &cur_parent_area, &layout, 0);
}
// A child window can be made to not always be above its parents by using a condition to setting its parent
window_set_parent(child1_func, NULL, child1_detach ? NULL : parent_window_function, NULL);
// Typical options window
void some_options_window(double *value)
{
static gui_layout_t layout={0};
const char *layout_src[] = {
"elem 0", "type none", "label Options", "pos -0;6 1", "dim 3 3;6", "off 0 1", "",
"elem 10", "type knob", "label Value", "knob 0 0 1 linear", "pos 0 0", "dim 2 2", "off 0 1", "",
};
make_gui_layout(&layout, layout_src, sizeof(layout_src)/sizeof(char *), "Options window");
if (mouse.window_minimised_flag > 0)
return;
// Window
static flwindow_t window={0};
flwindow_init_defaults(&window);
flwindow_init_pinned(&window);
draw_dialog_window_fromlayout(&window, cur_wind_on, &cur_parent_area, &layout, 0);
// Controls
ctrl_knob_fromlayout(value, &layout, 10);
}
static int options_detach=0;
static double knob_value = NAN;
window_register(1, some_options_window, NULL, make_rect_off(xy(-16., 9.), xy(3, 3.5), xy(0., 1.)), &options_detach, 1, &knob_value);
//**** Keyboard input ****
// Get state by scancode (see general/keyboard_struct.h)
// -2 newly up, -1 up, 1 = down, 2 = newly down, 3 = repeated down event
mouse.key_state[RL_SCANCODE_?]
// and by name (see https://wiki.libsdl.org/SDL_Keycode for names)
get_key_state_by_name("a")
// compare with >= 2 for once and repeating, == 2 for once and no repeating, <= 0 for down at all
if (cur_textedit==NULL && mouse.key_state[RL_SCANCODE_?] >= 2)
// Get modifier keys
// because remote desktoping releases mod keys at the same time as it sends associated key events it's best to test for get_kb_*() != -1
get_kb_shift(), get_kb_ctrl(), get_kb_guikey(), get_kb_alt()
// all of the above put together (returns either 0 or 1)
get_kb_all_mods()
// Get all the return keys at once
get_kb_enter()
//**** Images ****
// Creating a raster
// the first arg can be an array if it already exists
r = make_raster(NULL, dim, XYI0, IMAGE_USE_FRGB);
// Loading an image as a raster
free_raster(&r);
r = load_image(path, IMAGE_USE_FRGB);
// Rescale raster (aspect ratio not conserved)
blit_scale_float_autoscale(r1.f, r1.dim, r0.f, r0.dim, 4, get_pixel_address_contig);
// Loading an image as a tiled mipmap
mipmap_t image_mm={0};
free_mipmap(&image_mm);
image_mm = load_mipmap(path, IMAGE_USE_SQRGB);
// or by HTTP
image_mm = load_mipmap_from_http(url, IMAGE_USE_SQRGB);
// Saving a frgb raster image as a float RGB TIFF
// return of 0 if it failed
save_image_tiff(out_path, r.f, r.dim, 4, 3, 32);
// Saving a raster image in any non-.buf format
// return of 0 if it failed
save_image(out_path, r, 92);
// Displaying
// penultimate argument set to 1 keeps the pixel aspect ratio
// last argument can be LINEAR_INTERP or AA_NEAREST_INTERP
blit_in_rect(&r, sc_rect(image_frame), 1, LINEAR_INTERP);
blit_mipmap_in_rect(image_mm, sc_rect(image_frame), 1, LINEAR_INTERP);
// Updating
// if a raster's contents are updated the drawqueue data copy of the new contents can be triggered before blitting like this:
cl_unref_raster(&r);
cl_unref_mipmap(image_mm, IMAGE_USE_FRGB);
//**** Layout ****
// The function must take rect_t area as an argument
// it should then start with some of the following:
{
xy_t offset;
double sm;
rect_t main_frame;
const double margin = 12.;
main_frame = /* TODO define the full frame in local units to fit into the given area */;
offset = fit_into_area(area, main_frame, margin, &sm);
// check if any of the main_frame (+margins) gets displayed at all and draw the frame and possibly titled overlay
if (dialog_enclosing_frame(offset, sm, main_frame, margin, "Options", make_grey(0.25)))
return;
}
// check if a box is on screen or not
if (check_box_on_screen(box_os))
// Coordinates
// for a xy_t point:
offset_scale(p, offset, sm)
// for a rect_t rectangle:
offset_scale_rect(box, offset, sm)
// Insert-spacing rect in text
// An insert-spacing codepoint can be inserted into a text using \356\200\220 for cp_ins_w0 and \363\240\204\200 for an index of 0
// if there are more than one cp_ins_w* characters the widths are summed
// see vector_type/insert_rect.h for reference
sprintf(string, "Inserted element goes -> \356\200\226\363\240\204\200 <- here");
// reset the insert_rect array to clear outdated or bogus values
reset_insert_rect_array();
// the rectangle giving its position can be retrieved right after displaying
// the string (and until the next reset) and turned to world coordinates
// by its index (its order in the string) this way:
rect_t insert_area = get_insert_rect(0);
// the vertical range of the area can be changed from the default of 0 to 6 units
insert_rect_change_height(get_insert_rect(0), 0., 4.5)
// Fitting a rect inside a rect area
// the 3rd argument works just like the offset in make_rect_off()
frame = fit_rect_in_area(get_rect_dim(im_rect), area, xy(0.5, 0.5));
// Subdividing an area into a smaller one by ratio and offset
sub_area = get_subdiv_area(area, xy(1., 1./8.), xy(0.5, 1.));
//**** Parsing ****
// Load a file and have an array of lines out of it
// only array[0] then array need to be freed, since array[0] points to the original buffer
// free_2d(array, 1); does it
int linecount;
char **array = arrayise_text(load_raw_file_dos_conv(path, NULL), &linecount);
// Parse a dozenal fractional notation number
// from a string pointer p into a double v
// p is updated to point to after the number and its following whitespace
p = string_parse_fractional_12(p, &v);
// Read a UTF-8 character in a loop
// i is the iterator, it's incremented further in case of a multi-byte character
cp = utf8_to_unicode32(&string[i], &i);
// Print a Unicode codepoint as UTF-8 in a string
// generally the pointer should be able to accomodate 5 bytes
sprint_unicode(buf, cp);
//**** Memory ****
// Alloc more in an array if needed
alloc_enough(&array, needed_count, &alloc_count, size_elem, inc_ratio);
alloc_enough(&array, count+=1, &alloc_count, size_elem, inc_ratio);
alloc_count = alloc_enough_pattern(&array, needed_count, alloc_count, size_elem, inc_ratio, 0xFF);
// This one protects the realloc with a mutex only if needed
alloc_enough_mutex(&array, count+=1, &alloc_count, size_elem, inc_ratio, &my_mutex);
// This one does a copy of a source array
alloc_enough_and_copy(&array, src_array, src_count, &alloc_count, size_elem, inc_ratio);
// safe sprintf that reallocs the string if needed
// string can be NULL, then realloc will allocate it
// Arg 2 can be NULL if the alloc count isn't known, not really a problem
// alloc_count can also be 0 if the alloc count isn't know but we'd like to store the new alloc count
// Arg 3 is 0 for no appending (normal sprintf() behaviour) or 1 for appending, like sprintf(&string[strlen(string)],
sprintf_realloc(&string, &alloc_count, 1, "%g", value);
// or for simple allocation
string = sprintf_alloc("%g", value);
// vsprintf equivalent but with allocation
va_start(args, format);
string = vsprintf_alloc(format, args);
va_end(args);
free(string);
//**** Generic buffer ****
// Declare buffer
buffer_t buf={0};
// Free buffer
free_buf(&buf);
// Function equivalents
bufprintf(&buf, ...) = fprintf(stream, ...)
bufwrite(&buf, data, data_len) = fwrite
buf = buf_load_raw_file(path) = load_raw_file(path, ...)
buf = buf_load_raw_file_dos_conv(path) = load_raw_file_dos_conv(path, ...)
buf_save_raw_file(&buf, path, "wb") = save_raw_file(...)
buf_string_copy(string) = make_string_copy(string)
buf_alloc_enough(&buf, req_size) = alloc_enough(&buf->buf, req_size, &buf->as, 1, 1.)
//**** Threading ****
// Init thread handle (not for detached threads)
static rl_thread_t thread_handle=NULL;
// Declare the thread data
static my_thread_data_t data={0}, *d=&data;
// before rl_thread_join the caller should signal to the thread function to quit using this element in the data struct
volatile int thread_on;
d->thread_on = 0;
// Wait for thread to end (not for detached threads, use mutex instead)
rl_thread_join_and_null(&thread_handle);
// and before creating the thread:
d->thread_on = 1;
// Create thread
rl_thread_create(&thread_handle, thread_function, d);
// or detached:
rl_thread_create_detached(thread_function, d);
// Thread function prototype
int thread_function(void *ptr)
// Mutexes
rl_mutex_t my_mutex;
rl_mutex_init(&my_mutex);
rl_mutex_lock(&my_mutex);
if (rl_mutex_trylock(&my_mutex))
rl_mutex_unlock(&my_mutex);
rl_mutex_destroy(&my_mutex);
// or
rl_mutex_t *mutex_ptr;
mutex_ptr = rl_mutex_init_alloc();
rl_mutex_lock(mutex_ptr);
rl_mutex_unlock(mutex_ptr);
rl_mutex_destroy_free(&mutex_ptr);
// Semaphores
rl_sem_t sem;
// Initialising with 1 makes it like a lockable mutex, using 0 makes it similar to being already locked
rl_sem_init(&sem, 1);
rl_sem_wait(&sem);
rl_sem_post(&sem);
rl_sem_destroy(&sem);
// Set priority for the current thread
rl_thread_set_priority_low();
rl_thread_set_priority_high();
// Yield
rl_thread_yield();
// Atomic equivalent of if (v) v=0;
if (rl_atomic_get_and_set(&v, 0))
//**** Audio system ****
// Callback template
// The Deinit section is only needed if there's a separate processing thread, otherwise just return when stream==NULL
void my_audio_callback(float *stream, audiosys_t *sys, int bus_index, my_data_t *d)
{
int i;
double t;
// Deinit
if (stream==NULL) // this signals the shutdown of the bus for deinitialisation purposes
{
d->thread_on = 0;
if (bus_index == -1) // this signals a blocking deinitialisation
rl_thread_join_and_null(&d->thread_handle);
return;
}
for (t=sys->bus[bus_index].stime, i=0; i < sys->buffer_len; i++, t+=sys->sec_per_sample)
{
stream[i*2 ] += cos(t*1e3)*0.01;
stream[i*2+1] += sin(t*1e3)*0.01;
}
}
// Registering the callback (continuously, must be more often than the expiry duration)
int audio_bus_index = audiosys_bus_register(my_audio_callback, my_data, 1, 0.);
// Use the mutex in the main function (the callback is mutex-protected outside of itself)
rl_mutex_lock(&audiosys.bus[audio_bus_index].mutex);
// Stop the callback (in a blocking way)
audiosys_bus_unregister(my_data);
// right after this it's safe to free the data struct contents as needed
// Audio events can set their time (in the main thread) like this:
w->shipA->fire_sound_time = get_time_hr();
// then in the callback relative time is obtained like this:
dt = t - w->shipA->fire_sound_time;
//**** Misc ****
// Reset zoom and offset
zoom_reset(&zc, &mouse.zoom_flag);
// Change zoom or view offset
// if either argument is NAN it is unchanged
change_zoom(pos, NAN);
change_zoom_and_turn_off_zoom_mode(pos, 4.);
// Timing
double ts=0.;
get_time_diff_hr(&ts);
//< things to time >
fprintf_rl(stdout, "Things took %g sec\n\n", get_time_diff_hr(&ts));
// Benchmark average
static int bench_count=0;
static double ts_accu=0.;
double ts=0.;
get_time_diff_hr(&ts);
//< things to time >
ts_accu += get_time_diff_hr(&ts);
if (++bench_count==200)
{
fprintf_rl(stdout, "Took %.3f ms/frame\n", 1000.*ts_accu / bench_count);
exit(0);
}
// Benchmark high frequency short function in-situ in nanoseconds
static uint64_t bench_count=0;
static double ts_accu=0.;
double ts=0.;
get_time_diff_hr(&ts);
get_time_diff_hr(&ts);
//< things to time >
bench_function();
ts_accu += get_time_diff_hr(&ts);
get_time_diff_hr(&ts);
ts_accu -= get_time_diff_hr(&ts);
if ((++bench_count & 0xFFFFFF) == 0)
{
fprintf_rl(stdout, "Took %.3f nanoseconds/call\n", 1e9*ts_accu / bench_count);
ts_accu *= 0.75;
bench_count = bench_count*3/4;
}
// Paths
append_name_to_path(fullpath, path, name); // puts 'path/name' into char fullpath[PATH_MAX*4], fullpath can be NULL in which case the function returns the allocated string
remove_name_from_path(dirpath, fullpath); // makes dirpath from fullpath without the final name (can be a folder) nor the last /. Any trailing input / is ignored
remove_extension_from_path(outpath, fullpath); // removes the extension
create_dirs_for_file(filepath); // create the necessary folders, good to call before writing a file
// Folders
// This loads a folder, the 3rd argument is -1 for full tree loading, 0 for excluding subfolders, >0 for a given depth level
fs_dir_t dir={0};
load_dir_depth(dir_path, &dir, 0);
sort_dir_logical(&dir);
free_dir(&dir);
// Go through each file of a subfolder and create the full path
for (i=0; i < dir.subfile_count; i++)
full_path[i] = append_name_to_path(NULL, dir.path, dir.subfile[i].name);
// Drag and drop of files
if (dropfile_get_count())
path = dropfile_pop_first();
// Generate a video from a raster struct
ff_videnc_t d = ff_video_enc_init_file(path, r.dim, 29.97, AV_CODEC_ID_H265, bit_depth, crf);
for (...)
ff_video_enc_write_raster(&d, &r);
ff_video_enc_finalise_file(&d);
// Generate a video from tls framebuffer and layout
static gui_layout_t layout={0};
const char *layout_src[] = {
"elem 10", "type none", "pos 0 0", "dim 32 18", "off 0.5", "",
"elem 20", "type label", "pos -8 8", "dim 3 2", "off 0 1", "",
};
layout.sm = 1.;
make_gui_layout(&layout, layout_src, sizeof(layout_src)/sizeof(char *), "Video framebuffer layout");
init_tls_fb(xyi(960, 540));
ff_videnc_t d = ff_video_enc_init_file(path, fb->r.dim, 29.97, AV_CODEC_ID_H265, bit_depth, crf);
for (...)
{
gui_printf_to_label(&layout, 20, 0, "%d", i);
draw_label_fromlayout(&layout, 20, ALIG_RIGHT | MONODIGITS);
ff_video_enc_write_raster(&d, &fb->r);
memset(fb->r.f, 0, mul_x_by_y_xyi(fb->r.dim)*sizeof(frgb_t));
}
ff_video_enc_finalise_file(&d);
free_raster(&fb->r);
free_null(&fb);
// FFTs
// Choose out_size this way
out_size = next_fast_fft_size(out_size);
// 1D real to complex. For size n the output layout is c0, c1, c2, ..., cn/2-2, cn/2-1, cn/2, c-(n/2-1), c-(n/2-2), ..., c-2, c-1
// therefore the positive frequency at c[i] is found in negative at c[n-i]
static cfft_plan_t plan={0};
size_t out_as=0;
cfft_1D_r2c_padded_fft(&plan, in, sizeof(*in), &out, sizeof(*out), &out_as, in_size, out_size);
// Access positive frequency complex pairs this way, the negative frequencies are beyond out[out_size]
// This includes the Nyquist frequency for even counts and ends properly for odd counts
for (i=0; i <= out_size>>1; i++)
{
real = out[i<<1];
imag = out[(i<<1)+1];
// or out[i] if out is xy_t
// negative frequency mirroring is done like this (using xy_t):
if (i > 0)
out[out_size-i] = neg_y(out[i]);
}
// Loading the vector typeface
// add this to rl.h
#define RL_INCL_UNICODE_DATA_MINI
#define RL_INCL_VECTOR_TYPE_FILEBALL
// and in the program initialisation call using this
vector_font_load_from_header();
// from a folder (RL_INCL_VECTOR_TYPE_FILEBALL not needed)
font = remake_font("vector_type/type_index.txt", font);
// Preferences
// Initiating the default prefs struct
pref_def = pref_set_file_by_appdata_path("Program name", "config.txt");
// Getting and setting values
samplerate = pref_get_double(&pref_def, "Audio output:Sample rate", 44100, " Hz");
pref_set_double(&pref_def, "Audio output:Sample rate", samplerate, " Hz");
// Strings (return value of pref_get_string() can't be freed)
const char *driver_name = pref_get_string(&pref_def, "Audio output:Preferred driver", "directsound");
pref_set_string(&pref_def, "Audio output:Preferred driver", "winmm");
// On/off
key[livesynth_on] = pref_get_onoff(&pref_def, "Audio output:Live synthesis", 1);
pref_set_onoff(&pref_def, "Audio output:Live synthesis", key[livesynth_on]);
// Two values
// the last parameter can be NULL if there is no suffix
xy_t res = pref_get_2val(&pref_def, "Interface:Window dimensions", SCRN_W, "x", SCRN_H, NULL);
pref_set_2val(&pref_def, "Analysis:Frequency range", sound->anal.min, " - ", sound->anal.max, " Hz");
// Save sound (for debugging purposes) using an OS dialog
save_sound_fl32_file(save_file_dialog("WAVE file\1*.wav\1"), snd, sample_count, channels, samplerate, NULL);
// Print message to SDL message box
sdl_box_printf("Message box title", "value = %d", value);
// Show file in Explorer (Windows only for now)
show_file_in_explorer(path);
// Open file using the system
system_open(path);
// Set up Windows crash dump (add #define RL_CRASHDUMP to your rl.h)
#ifdef RL_CRASHDUMP
#ifdef _WIN32
crashdump_init(make_appdata_path("Spacewar", NULL, 1));
#endif
#endif
// Reversed bits iteration
for (i2=i=0; i < count; i++)
{
ir = reverse_iterator_bits32(&i2, count);
// and in 2D
uint64_t i2, pix_count = mul_x_by_y_xyi(r.dim);
for (i2=i=0; i < pix_count; i++)
{
ip = reverse_iterator_bits_2d(&i2, r.dim);
// Accessing an unsorted array in sorted order
size_t *sorted_order_index_array = make_order_index_array(some_unsorted_double_array, NULL, count, sizeof(double), cmp_double, 1);
for (i=0; i < count; i++)
some_unsorted_double_array[sorted_order_index_array[i]];
// Go through each line in a string
for (const char *line = message; line; line = strstr_after(line, "\n"))
//**** C syntax I can't ever remember ****
// Function pointers as function arguments
void some_function(int (*func_ptr_name)(void*,int))
// Typedef function pointer
typedef double (*knob_func_t)(double, double, double, const int);
// a function pointer can then be declared like this:
knob_func_t func;
// Force a CALL instruction to a function using a volatile function pointer
static __m128 (*volatile bench_function)(__m128) = some_function;
v = bench_function(a);
// A volatile pointer to non-volatile data is declared like this:
int *volatile ptr;
// Thread-local static variable inside a function
static _Thread_local int var=0;
// How to get one line from a string
n=0;
sscanf(p, "%[^\n]\n%n", line, &n);
// How to get one line from a file
while (fgets(line, sizeof(line), file))
// How to write a date/time stamp
char datestamp[32];
time_t now = time(NULL);
strftime(datestamp, sizeof(datestamp), "%Y-%m-%d %H.%M.%S", localtime(&now));
// Adding some stack checking
#pragma strict_gs_check(on)
#pragma check_stack(on)
#pragma check_stack()
#pragma strict_gs_check()
// Flashing in the taskbar through SDL
// SDL_FLASH_BRIEFLY can also be used, as well as SDL_FLASH_CANCEL to cancel
SDL_FlashWindow(fb->window, SDL_FLASH_UNTIL_FOCUSED);
// Print UTF-8 to the console
#ifdef _WIN32
SetConsoleOutputCP(65001);
#endif
// Print a string to a certain length
printf("%.*s", len, string);
// Print block elements (lvl is between 1 and 8)
fprintf_rl(stdout, "\342\226%c", 0200+lvl);
// Set console colours, see https://www.nayab.xyz/linux/escapecodes
fprintf_rl(stdout, "\033[0;32m"); // green
fprintf_rl(stdout, "\033[0m"); // reset colour
// Macro to printf/fprintf
#define REPORT(fmt, ...) { fprintf(stderr, (fmt"\n"), ##__VA_ARGS__); fflush(stderr); }
// Convert between float and __m128
__m128 vf = _mm_load_ps((float *) &cf);
_mm_storeu_ps((float *) &cf, vf);