summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am33
-rw-r--r--src/filter.c562
-rw-r--r--src/filter.h160
-rw-r--r--src/lowpass.c371
-rw-r--r--src/lowpass.h46
-rw-r--r--src/notes.h51
-rw-r--r--src/raygui.h4647
-rw-r--r--src/synth.c114
-rw-r--r--src/synth_engine.c332
-rw-r--r--src/synth_engine.h77
-rw-r--r--src/synth_gui.c173
-rw-r--r--src/synth_gui.h10
-rw-r--r--src/synth_math.h22
13 files changed, 6598 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..91e1be5
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,33 @@
+#bin_PROGRAMS = food cookbook cook synth
+bin_PROGRAMS = synth
+
+common_sources = filter.c \
+ filter.h \
+ lowpass.c \
+ lowpass.h \
+ Makefile.am \
+ notes.h \
+ raygui.h \
+ synth_engine.c \
+ synth_engine.h \
+ synth_gui.c \
+ synth_gui.h \
+ synth_math.h
+
+# -fwhole-program allows cross-file inlining, but only works when you put all
+# the source files on one gcc command-line. -flto is another way to get the
+# same effect. (Link-Time Optimization). clang supports -flto but not
+# -fwhole-program.
+
+# If your program doesn't depend on strict FP rounding
+# behaviour, use -ffast-math. If it does, you can usually still use
+# -fno-math-errno and stuff like that, without enabling
+# -funsafe-math-optimizations. Some FP code can get big speedups from
+# fast-math, like auto-vectorization.
+AM_CFLAGS = -O3 -march=native -fno-math-errno -funroll-loops -flto
+
+synth_SOURCES = synth.c $(common_sources)
+synth_LDADD = -lportaudio -lrt -lm -lasound -lraylib
+#cookbook_SOURCES = cookbook.c $(common_sources)
+#cookbook_SOURCES = cookbook.c $(common_sources)
+#cook_SOURCES = cook.c $(common_sources)
diff --git a/src/filter.c b/src/filter.c
new file mode 100644
index 0000000..69d7bb6
--- /dev/null
+++ b/src/filter.c
@@ -0,0 +1,562 @@
+/*
+ * filter.c
+ *
+ * Copyright (c) 2018 Disi A
+ *
+ * Author: Disi A
+ * Email: adis@live.cn
+ * https://www.mathworks.com/matlabcentral/profile/authors/3734620-disi-a
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "filter.h"
+#include <stddef.h>
+
+#if DOUBLE_PRECISION
+#define COS cos
+#define SIN sin
+#define TAN tan
+#define COSH cosh
+#define SINH sinh
+#define SQRT sqrt
+#define LOG log
+#else
+#define COS cosf
+#define SIN sinf
+#define TAN tanf
+#define COSH coshf
+#define SINH sinhf
+#define SQRT sqrtf
+#define LOG logf
+#endif
+
+void update_bw_low_pass_filter(BWLowPass* filter, FTR_PRECISION s, FTR_PRECISION f, FTR_PRECISION q)
+{
+ FTR_PRECISION a = TAN((FTR_PRECISION)(M_PI * f / s));
+ FTR_PRECISION a2 = a * a;
+ FTR_PRECISION r;
+
+ int i;
+
+ for(i=0; i < filter -> n; ++i){
+ r = SIN((FTR_PRECISION)(M_PI * (2.0 * i + 1.0) / (4.0 * filter->n)));
+ s = (FTR_PRECISION) (a2 + 2.0 * a * r + 1.0);
+ filter->A[i] = a2 / s;
+ filter->d1[i] = (FTR_PRECISION) (2.0 * (1 - a2) / s);
+ filter->d2[i] = (FTR_PRECISION)(-(a2 - 2.0 * a * r + 1.0) / s);
+ }
+}
+
+BWLowPass* create_bw_low_pass_filter(int order, FTR_PRECISION s, FTR_PRECISION f) {
+ BWLowPass* filter = (BWLowPass *) malloc(sizeof(BWLowPass));
+ filter -> n = order/2;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+
+ if (filter->d2 == NULL)
+ {
+ free_bw_low_pass(filter);
+ return NULL;
+ }
+
+ FTR_PRECISION a = TAN((FTR_PRECISION)(M_PI * f / s));
+ FTR_PRECISION a2 = a * a;
+ FTR_PRECISION r;
+
+ int i;
+
+ for(i=0; i < filter -> n; ++i){
+ r = SIN((FTR_PRECISION)(M_PI * (2.0 * i + 1.0) / (4.0 * filter->n)));
+ s = (FTR_PRECISION) (a2 + 2.0 * a * r + 1.0);
+ filter->A[i] = a2 / s;
+ filter->d1[i] = (FTR_PRECISION) (2.0 * (1 - a2) / s);
+ filter->d2[i] = (FTR_PRECISION)(-(a2 - 2.0 * a * r + 1.0) / s);
+ }
+ return filter;
+}
+
+BWHighPass* create_bw_high_pass_filter(int order, FTR_PRECISION s, FTR_PRECISION f){
+ BWHighPass* filter = (BWHighPass *) malloc(sizeof(BWHighPass));
+ filter -> n = order/2;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+
+ if (filter->d2 == NULL)
+ {
+ free_bw_high_pass(filter);
+ return NULL;
+ }
+
+ FTR_PRECISION a = TAN((FTR_PRECISION) (M_PI * f/s));
+ FTR_PRECISION a2 = a * a;
+ FTR_PRECISION r;
+
+ int i;
+
+ for(i=0; i < filter -> n; ++i){
+ r = SIN((FTR_PRECISION)(M_PI * (2.0 * i + 1.0) / ( 4.0 * filter->n)));
+ s = (FTR_PRECISION) (a2 + 2.0 * a * r + 1.0);
+ filter -> A[i] = (FTR_PRECISION) (1.0 / s);
+ filter -> d1[i] = (FTR_PRECISION) (2.0 * (1 - a2) / s);
+ filter -> d2[i] = (FTR_PRECISION) (- (a2 - 2.0 * a * r + 1.0) / s);
+ }
+ return filter;
+}
+
+BWBandPass* create_bw_band_pass_filter(int order, FTR_PRECISION s, FTR_PRECISION fl, FTR_PRECISION fu){
+ if(fu <= fl){
+ printf("ERROR:Lower half-power frequency is smaller than higher half-power frequency");
+ return NULL;
+ }
+ BWBandPass* filter = (BWBandPass *) malloc(sizeof(BWBandPass));
+ filter -> n = order/4;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d3 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d4 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w3 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w4 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+
+
+ FTR_PRECISION a = COS(M_PI*(fu+fl)/s)/COS(M_PI*(fu-fl)/s);
+ FTR_PRECISION a2 = a*a;
+ FTR_PRECISION b = TAN(M_PI*(fu-fl)/s);
+ FTR_PRECISION b2 = b*b;
+ FTR_PRECISION r;
+ int i;
+ for(i=0; i<filter->n; ++i){
+ r = SIN( (FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / (4.0 * filter->n)));
+ s = (FTR_PRECISION)(b2 + 2.0 * b * r + 1.0);
+ filter->A[i] = b2/s;
+ filter->d1[i] = (FTR_PRECISION) (4.0 * a * (1.0 + b * r) / s);
+ filter->d2[i] = (FTR_PRECISION) (2.0 * (b2 - 2.0 * a2 - 1.0)/ s);
+ filter->d3[i] = (FTR_PRECISION) (4.0 * a * (1.0 - b * r ) / s);
+ filter->d4[i] = (FTR_PRECISION) (- (b2 - 2.0 * b * r + 1.0) / s);
+ }
+ return filter;
+}
+
+BWBandStop* create_bw_band_stop_filter(int order, FTR_PRECISION s, FTR_PRECISION fl, FTR_PRECISION fu){
+ if(fu <= fl){
+ printf("ERROR:Lower half-power frequency is smaller than higher half-power frequency");
+ return NULL;
+ }
+
+ BWBandStop* filter = (BWBandStop *) malloc(sizeof(BWBandStop));
+ filter -> n = order/4;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d3 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+ filter -> d4 = (FTR_PRECISION *)malloc(filter -> n*sizeof(FTR_PRECISION));
+
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w3 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+ filter -> w4 = (FTR_PRECISION *)calloc(filter -> n, sizeof(FTR_PRECISION));
+
+ FTR_PRECISION a = COS(M_PI*(fu+fl)/s)/COS(M_PI*(fu-fl)/s);
+ FTR_PRECISION a2 = a*a;
+ FTR_PRECISION b = TAN(M_PI*(fu-fl)/s);
+ FTR_PRECISION b2 = b*b;
+ FTR_PRECISION r;
+
+ int i;
+ for(i=0; i<filter->n; ++i){
+ r = SIN((FTR_PRECISION) (M_PI * (2.0 * i + 1.0 ) / ( 4.0 * filter->n)));
+ s = (FTR_PRECISION) (b2 + 2.0 * b * r + 1.0);
+ filter->A[i] = (FTR_PRECISION) (1.0 / s);
+ filter->d1[i] = (FTR_PRECISION) (4.0 * a * (1.0 + b * r ) / s);
+ filter->d2[i] = (FTR_PRECISION) (2.0 * (b2 - 2.0 * a2- 1.0) / s);
+ filter->d3[i] = (FTR_PRECISION) (4.0 * a * (1.0 - b * r) / s);
+ filter->d4[i] = (FTR_PRECISION) (- (b2 - 2.0 * b * r + 1.0) / s);
+ }
+ filter->r = (FTR_PRECISION) (4.0 * a);
+ filter->s = (FTR_PRECISION) (4.0 * a2 + 2.0);
+ return filter;
+}
+
+CHELowPass* create_che_low_pass_filter(int n, FTR_PRECISION epsilon, FTR_PRECISION s, FTR_PRECISION f){
+ CHELowPass* filter = (CHELowPass *) malloc(sizeof(CHELowPass));
+ filter -> m = n/2;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+
+ if (filter->d2 == NULL)
+ {
+ free_che_low_pass(filter);
+ return NULL;
+ }
+
+ FTR_PRECISION a = TAN((FTR_PRECISION) (M_PI * f/ s));
+ FTR_PRECISION a2 = a * a;
+
+ FTR_PRECISION u = LOG((FTR_PRECISION) (1.0 + SQRT((FTR_PRECISION) (1.0 + epsilon * epsilon)) / epsilon));
+ FTR_PRECISION su = SINH(u/(FTR_PRECISION)n);
+ FTR_PRECISION cu = COSH(u/(FTR_PRECISION)n);
+ FTR_PRECISION b,c;
+
+ int i;
+ for(i=0; i<filter->m; ++i){
+ b = SIN((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / (2.0 * n))) * su;
+ c = COS((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / (2.0 * n))) * cu;
+ c = b*b + c*c;
+ s = (FTR_PRECISION) (a2 * c + 2.0 * a * b + 1.0);
+ filter->A[i] = (FTR_PRECISION) (a2 / (4.0 * s));
+ filter->d1[i] = (FTR_PRECISION) (2.0 * (1 - a2 * c) / s);
+ filter->d2[i] = (FTR_PRECISION) (- (a2 * c - 2.0 * a * b + 1.0) / s);
+ }
+ filter->ep = (FTR_PRECISION) (2.0 / epsilon); // used to normalize
+
+ return filter;
+}
+
+CHEHighPass* create_che_high_pass_filter(int n, FTR_PRECISION epsilon, FTR_PRECISION s, FTR_PRECISION f){
+ CHEHighPass* filter = (CHELowPass *) malloc(sizeof(CHEHighPass));
+ filter -> m = n/2;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+
+ if (filter->d2 == NULL)
+ {
+ free_che_high_pass(filter);
+ return NULL;
+ }
+
+ FTR_PRECISION a = TAN((FTR_PRECISION) (M_PI * f/ s));
+ FTR_PRECISION a2 = a * a;
+
+ FTR_PRECISION u = LOG((FTR_PRECISION) (1.0 + SQRT((FTR_PRECISION) (1.0 + epsilon*epsilon))) / epsilon);
+ FTR_PRECISION su = SINH(u/(FTR_PRECISION)n);
+ FTR_PRECISION cu = COSH(u/(FTR_PRECISION)n);
+ FTR_PRECISION b,c;
+
+ int i;
+ for(i=0; i<filter->m; ++i){
+ b = SIN((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / (2.0 * n))) * su;
+ c = COS((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / (2.0 * n))) * cu;
+ c = b*b + c*c;
+ s = (FTR_PRECISION) (a2 + 2.0 * a * b + c);
+ filter->A[i] = (FTR_PRECISION) (1.0 / (4.0 * s));
+ filter->d1[i] = (FTR_PRECISION) (2.0 * (c - a2)/s);
+ filter->d2[i] = (FTR_PRECISION) (- (a2 - 2.0 * a * b + c) / s);
+ }
+
+ filter->ep = (FTR_PRECISION) (2.0 / epsilon); // used to normalize
+ return filter;
+}
+
+CHEBandPass* create_che_band_pass_filter(int n, FTR_PRECISION epsilon, FTR_PRECISION s, FTR_PRECISION f_lower, FTR_PRECISION f_upper){
+ CHEBandPass* filter = (CHEBandPass *) malloc(sizeof(CHEBandPass));
+ filter -> m = n/4;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d3 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d4 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w3 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w4 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+
+ FTR_PRECISION a = COS(M_PI*(f_lower+f_upper)/s)/COS(M_PI*(f_upper-f_lower)/s);
+ FTR_PRECISION a2 = a*a;
+ FTR_PRECISION b = TAN(M_PI*(f_upper-f_lower)/s);
+ FTR_PRECISION b2 = b*b;
+ FTR_PRECISION u = LOG((FTR_PRECISION) (1.0 + SQRT((FTR_PRECISION) (1.0 + epsilon * epsilon))) / epsilon);
+ FTR_PRECISION su = SINH((FTR_PRECISION) (2.0 * u / (FTR_PRECISION)n));
+ FTR_PRECISION cu = COSH((FTR_PRECISION) (2.0 * u / (FTR_PRECISION)n));
+ FTR_PRECISION r,c;
+
+ int i;
+ for(i=0; i < filter->m; ++i){
+ r = SIN((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / n)) * su;
+ c = COS((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / n)) * cu;
+ c = r*r + c*c;
+ s = (FTR_PRECISION) (b2 * c + 2.0 * b * r + 1.0);
+ filter->A[i] = (FTR_PRECISION) (b2 / (4.0 * s));
+ filter->d1[i] = (FTR_PRECISION) (4.0 * a * (1.0 + b * r) / s);
+ filter->d2[i] = (FTR_PRECISION) (2.0 * (b2 * c - 2.0 * a2 - 1.0) / s);
+ filter->d3[i] = (FTR_PRECISION) (4.0 * a * (1.0 - b * r) / s);
+ filter->d4[i] = (FTR_PRECISION) (- (b2 * c - 2.0 * b * r + 1.0) / s);
+ }
+ filter->ep = (FTR_PRECISION) (2.0 / epsilon); // used to normalize
+ return filter;
+}
+
+CHEBandStop* create_che_band_stop_filter(int n, FTR_PRECISION epsilon, FTR_PRECISION s, FTR_PRECISION f_lower, FTR_PRECISION f_upper){
+ CHEBandStop* filter = (CHEBandStop *) malloc(sizeof(CHEBandStop));
+ filter -> m = n/4;
+ filter -> A = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d1 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d2 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d3 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> d4 = (FTR_PRECISION *)malloc(filter -> m*sizeof(FTR_PRECISION));
+ filter -> w0 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w1 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w2 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w3 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+ filter -> w4 = (FTR_PRECISION *)calloc(filter -> m, sizeof(FTR_PRECISION));
+
+ FTR_PRECISION a = COS(M_PI*(f_lower+f_upper)/s)/COS(M_PI*(f_upper-f_lower)/s);
+ FTR_PRECISION a2 = a*a;
+ FTR_PRECISION b = TAN(M_PI*(f_upper-f_lower)/s);
+ FTR_PRECISION b2 = b*b;
+ FTR_PRECISION u = LOG((FTR_PRECISION) (1.0 + SQRT((FTR_PRECISION) (1.0 + epsilon * epsilon))) / epsilon);
+ FTR_PRECISION su = SINH((FTR_PRECISION) (2.0*u/(FTR_PRECISION)n));
+ FTR_PRECISION cu = COSH((FTR_PRECISION) (2.0*u/(FTR_PRECISION)n));
+ FTR_PRECISION r,c;
+ int i;
+ for(i=0; i < filter->m; ++i){
+ r = SIN((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / n)) * su;
+ c = COS((FTR_PRECISION) (M_PI * (2.0 * i + 1.0) / n))*cu;
+ c = r*r + c*c;
+ s = (FTR_PRECISION) (b2 + 2.0 * b * r + c);
+ filter->A[i] = (FTR_PRECISION) (1.0 / (4.0 * s));
+ filter->d1[i] = (FTR_PRECISION) (4.0 * a * (c + b * r)/s);
+ filter->d2[i] = (FTR_PRECISION) (2.0 * (b2 - 2.0 * a2 * c - c) / s);
+ filter->d3[i] = (FTR_PRECISION) (4.0 * a * (c - b * r ) / s);
+ filter->d4[i] = (FTR_PRECISION) (- (b2 - 2.0 * b * r + c) / s);
+ }
+ filter->r = (FTR_PRECISION) (4.0 * a);
+ filter->s = (FTR_PRECISION) (4.0 * a2 + 2.0);
+ filter->ep = (FTR_PRECISION) (2.0 / epsilon); // used to normalize
+ return filter;
+}
+
+void free_bw_low_pass(BWLowPass* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter);
+}
+void free_bw_high_pass(BWHighPass* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter);
+}
+void free_bw_band_pass(BWBandPass* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> d3);
+ free(filter -> d4);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter -> w3);
+ free(filter -> w4);
+ free(filter);
+}
+void free_bw_band_stop(BWBandStop* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> d3);
+ free(filter -> d4);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter -> w3);
+ free(filter -> w4);
+ free(filter);
+}
+
+void free_che_low_pass(CHELowPass* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter);
+}
+
+void free_che_high_pass(CHEHighPass* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter);
+}
+void free_che_band_pass(CHEBandPass* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> d3);
+ free(filter -> d4);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter -> w3);
+ free(filter -> w4);
+ free(filter);
+}
+void free_che_band_stop(CHEBandStop* filter){
+ free(filter -> A);
+ free(filter -> d1);
+ free(filter -> d2);
+ free(filter -> d3);
+ free(filter -> d4);
+ free(filter -> w0);
+ free(filter -> w1);
+ free(filter -> w2);
+ free(filter -> w3);
+ free(filter -> w4);
+ free(filter);
+}
+
+FTR_PRECISION bw_low_pass(BWLowPass* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->n; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i] + x;
+ x = filter->A[i] * (filter->w0[i] + 2.0f * filter->w1[i] + filter->w2[i]);
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x;
+}
+FTR_PRECISION bw_high_pass(BWHighPass* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->n; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i] + x;
+ x = filter->A[i] * (filter->w0[i] - 2.0f * filter->w1[i] + filter->w2[i]);
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x;
+}
+FTR_PRECISION bw_band_pass(BWBandPass* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->n; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i]+ filter->d3[i]*filter->w3[i]+ filter->d4[i]*filter->w4[i] + x;
+ x = filter->A[i] * (filter->w0[i] - 2.0f * filter->w2[i] + filter->w4[i]);
+ filter->w4[i] = filter->w3[i];
+ filter->w3[i] = filter->w2[i];
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x;
+}
+FTR_PRECISION bw_band_stop(BWBandStop* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->n; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i]+ filter->d3[i]*filter->w3[i]+ filter->d4[i]*filter->w4[i] + x;
+ x = filter->A[i]*(filter->w0[i] - filter->r*filter->w1[i] + filter->s*filter->w2[i]- filter->r*filter->w3[i] + filter->w4[i]);
+ filter->w4[i] = filter->w3[i];
+ filter->w3[i] = filter->w2[i];
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x;
+}
+
+FTR_PRECISION che_low_pass(CHELowPass* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->m; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i] + x;
+ x = filter->A[i] * (filter->w0[i] + 2.0f * filter->w1[i] + filter->w2[i]);
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x * filter->ep;
+}
+FTR_PRECISION che_high_pass(CHEHighPass* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->m; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i] + x;
+ x = filter->A[i] * (filter->w0[i] - 2.0f * filter->w1[i] + filter->w2[i]);
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x * filter->ep;
+}
+
+FTR_PRECISION che_band_pass(CHEBandPass* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->m; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i]+ filter->d3[i]*filter->w3[i]+ filter->d4[i]*filter->w4[i] + x;
+ x = filter->A[i] * (filter->w0[i] - 2.0f * filter->w2[i] + filter->w4[i]);
+ filter->w4[i] = filter->w3[i];
+ filter->w3[i] = filter->w2[i];
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x * filter->ep;
+}
+
+FTR_PRECISION che_band_stop(CHEBandStop* filter, FTR_PRECISION x){
+ int i;
+ for(i=0; i<filter->m; ++i){
+ filter->w0[i] = filter->d1[i]*filter->w1[i] + filter->d2[i]*filter->w2[i]+ filter->d3[i]*filter->w3[i]+ filter->d4[i]*filter->w4[i] + x;
+ x = filter->A[i]*(filter->w0[i] - filter->r*filter->w1[i] + filter->s*filter->w2[i]- filter->r*filter->w3[i] + filter->w4[i]);
+ filter->w4[i] = filter->w3[i];
+ filter->w3[i] = filter->w2[i];
+ filter->w2[i] = filter->w1[i];
+ filter->w1[i] = filter->w0[i];
+ }
+ return x * filter->ep;
+}
+
+FTR_PRECISION softmax(FTR_PRECISION* data, int size, int target_ind){
+ FTR_PRECISION sum = 0;
+ for(int i = 0; i < size; i++) sum += data[i];
+ return data[target_ind]/sum;
+}
+
+static const FTR_PRECISION SPIKE_KERNEL[] = {-1.0, 2.0, -1.0};
+void spike_filter_upward(FTR_PRECISION * input, int size, FTR_PRECISION * output, FTR_PRECISION strength){
+ FTR_PRECISION mean = 0.0;
+ FTR_PRECISION std = 0.0;
+ FTR_PRECISION diff = 0.0;
+ for(int i=0; i < size; i++) mean += input[i];
+ mean /= size;
+
+ for(int i=0; i < size; i++){
+ diff = input[i] - mean;
+ std += diff * diff;
+ }
+ std = SQRT(std/size);
+
+ output[0] = 0.0;
+ output[size - 1] = 0.0;
+ for(int i=1; i<size-1; i++){
+ FTR_PRECISION val = input[i-1] * SPIKE_KERNEL[0] + input[i] * SPIKE_KERNEL[1] + input[i+1] * SPIKE_KERNEL[2];
+ if(val < strength * std) output[i] = 0.0;
+ else output[i] = val;
+ }
+}
diff --git a/src/filter.h b/src/filter.h
new file mode 100644
index 0000000..e72d384
--- /dev/null
+++ b/src/filter.h
@@ -0,0 +1,160 @@
+/*
+ * filter.h
+ *
+ * Copyright (c) 2018 Disi A
+ *
+ * Author: Disi A
+ * Email: adis@live.cn
+ * https://www.mathworks.com/matlabcentral/profile/authors/3734620-disi-a
+ */
+#ifndef filter_h
+#define filter_h
+
+#if __cplusplus
+extern "C"{
+#endif
+
+#define DOUBLE_PRECISION 0
+
+#if DOUBLE_PRECISION
+#define FTR_PRECISION double
+#if defined(_WIN32) || defined(__ZEPHYR__)
+#define M_PI 3.141592653589793238462643383279502884197163993751
+#endif
+#else
+#define FTR_PRECISION float
+#if defined(_WIN32) || defined(__ZEPHYR__)
+#define M_PI 3.1415927f
+#endif
+#endif
+
+typedef struct {
+ int n;
+ FTR_PRECISION *A;
+ FTR_PRECISION *d1;
+ FTR_PRECISION *d2;
+ FTR_PRECISION *w0;
+ FTR_PRECISION *w1;
+ FTR_PRECISION *w2;
+} BWLowPass;
+// BWHighPass uses exactly the same struct
+typedef BWLowPass BWHighPass;
+
+typedef struct {
+ int m;
+ FTR_PRECISION ep;
+ FTR_PRECISION *A;
+ FTR_PRECISION *d1;
+ FTR_PRECISION *d2;
+ FTR_PRECISION *w0;
+ FTR_PRECISION *w1;
+ FTR_PRECISION *w2;
+} CHELowPass;
+typedef CHELowPass CHEHighPass;
+
+typedef struct {
+ int n;
+ FTR_PRECISION *A;
+ FTR_PRECISION *d1;
+ FTR_PRECISION *d2;
+ FTR_PRECISION *d3;
+ FTR_PRECISION *d4;
+ FTR_PRECISION *w0;
+ FTR_PRECISION *w1;
+ FTR_PRECISION *w2;
+ FTR_PRECISION *w3;
+ FTR_PRECISION *w4;
+} BWBandPass;
+
+typedef struct {
+ int m;
+ FTR_PRECISION ep;
+ FTR_PRECISION *A;
+ FTR_PRECISION *d1;
+ FTR_PRECISION *d2;
+ FTR_PRECISION *d3;
+ FTR_PRECISION *d4;
+ FTR_PRECISION *w0;
+ FTR_PRECISION *w1;
+ FTR_PRECISION *w2;
+ FTR_PRECISION *w3;
+ FTR_PRECISION *w4;
+} CHEBandPass;
+
+typedef struct {
+ int n;
+ FTR_PRECISION r;
+ FTR_PRECISION s;
+ FTR_PRECISION *A;
+ FTR_PRECISION *d1;
+ FTR_PRECISION *d2;
+ FTR_PRECISION *d3;
+ FTR_PRECISION *d4;
+ FTR_PRECISION *w0;
+ FTR_PRECISION *w1;
+ FTR_PRECISION *w2;
+ FTR_PRECISION *w3;
+ FTR_PRECISION *w4;
+} BWBandStop;
+
+typedef struct {
+ int m;
+ FTR_PRECISION ep;
+ FTR_PRECISION r;
+ FTR_PRECISION s;
+ FTR_PRECISION *A;
+ FTR_PRECISION *d1;
+ FTR_PRECISION *d2;
+ FTR_PRECISION *d3;
+ FTR_PRECISION *d4;
+ FTR_PRECISION *w0;
+ FTR_PRECISION *w1;
+ FTR_PRECISION *w2;
+ FTR_PRECISION *w3;
+ FTR_PRECISION *w4;
+} CHEBandStop;
+
+void update_bw_low_pass_filter(BWLowPass* filter, FTR_PRECISION s, FTR_PRECISION f, FTR_PRECISION q);
+
+BWLowPass* create_bw_low_pass_filter(int order, FTR_PRECISION sampling_frequency, FTR_PRECISION half_power_frequency);
+BWHighPass* create_bw_high_pass_filter(int order, FTR_PRECISION sampling_frequency, FTR_PRECISION half_power_frequency);
+BWBandPass* create_bw_band_pass_filter(int order, FTR_PRECISION sampling_frequency, FTR_PRECISION lower_half_power_frequency, FTR_PRECISION upper_half_power_frequency);
+BWBandStop* create_bw_band_stop_filter(int order, FTR_PRECISION sampling_frequency, FTR_PRECISION lower_half_power_frequency, FTR_PRECISION upper_half_power_frequency);
+
+CHELowPass* create_che_low_pass_filter(int order, FTR_PRECISION epsilon, FTR_PRECISION sampling_frequency, FTR_PRECISION half_power_frequency);
+CHEHighPass* create_che_high_pass_filter(int order, FTR_PRECISION epsilon, FTR_PRECISION sampling_frequency, FTR_PRECISION half_power_frequency);
+CHEBandPass* create_che_band_pass_filter(int order, FTR_PRECISION epsilon, FTR_PRECISION sampling_frequency, FTR_PRECISION lower_half_power_frequency, FTR_PRECISION upper_half_power_frequency);
+CHEBandStop* create_che_band_stop_filter(int order, FTR_PRECISION epsilon, FTR_PRECISION sampling_frequency, FTR_PRECISION lower_half_power_frequency, FTR_PRECISION upper_half_power_frequency);
+
+
+void free_bw_low_pass(BWLowPass* filter);
+void free_bw_high_pass(BWHighPass* filter);
+void free_bw_band_pass(BWBandPass* filter);
+void free_bw_band_stop(BWBandStop* filter);
+
+void free_che_low_pass(CHELowPass* filter);
+void free_che_high_pass(CHEHighPass* filter);
+void free_che_band_pass(CHEBandPass* filter);
+void free_che_band_stop(CHEBandStop* filter);
+
+
+FTR_PRECISION bw_low_pass(BWLowPass* filter, FTR_PRECISION input);
+FTR_PRECISION bw_high_pass(BWHighPass* filter, FTR_PRECISION input);
+FTR_PRECISION bw_band_pass(BWBandPass* filter, FTR_PRECISION input);
+FTR_PRECISION bw_band_stop(BWBandStop* filter, FTR_PRECISION input);
+
+FTR_PRECISION che_low_pass(CHELowPass* filter, FTR_PRECISION input);
+FTR_PRECISION che_high_pass(CHEHighPass* filter, FTR_PRECISION input);
+FTR_PRECISION che_band_pass(CHEBandPass* filter, FTR_PRECISION input);
+FTR_PRECISION che_band_stop(CHEBandStop* filter, FTR_PRECISION input);
+
+FTR_PRECISION softmax(FTR_PRECISION* data, int size, int target_ind);
+
+// Output should be pre-allocated which has the same(or larger) size as the input.
+void spike_filter_upward(FTR_PRECISION * input, int size, FTR_PRECISION * output, FTR_PRECISION strength);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* filter_h */
diff --git a/src/lowpass.c b/src/lowpass.c
new file mode 100644
index 0000000..304ad9f
--- /dev/null
+++ b/src/lowpass.c
@@ -0,0 +1,371 @@
+/*
+Resonant low pass filter source code.
+By baltrax@hotmail.com (Zxform)
+
+- little changes and optimizations by Brett Paterson for FMOD example.
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "lowpass.h"
+
+/**************************************************************************
+
+FILTER.C - Source code for filter functions
+
+ iir_filter IIR filter floats sample by sample (real time)
+
+*************************************************************************/
+
+FILTER iir;
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * iir_filter - Perform IIR filtering sample by sample on floats
+ *
+ * Implements cascaded direct form II second order sections.
+ * Requires FILTER structure for history and coefficients.
+ * The length in the filter structure specifies the number of sections.
+ * The size of the history array is 2*iir.length.
+ * The size of the coefficient array is 4*iir.length + 1 because
+ * the first coefficient is the overall scale factor for the filter.
+ * Returns one output sample for each input sample. Allocates history
+ * array if not previously allocated.
+ *
+ * float iir_filter(float input,FILTER *iir)
+ *
+ * float input new float input sample
+ * FILTER *iir pointer to FILTER structure
+ *
+ * Returns float value giving the current output.
+ *
+ * Allocation errors cause an error message and a call to exit.
+ * --------------------------------------------------------------------
+ */
+float LowPass_Filter(float input)
+{
+ unsigned int i;
+ float *hist1_ptr,*hist2_ptr,*coef_ptr;
+ float output,new_hist,history1,history2;
+ static float dc = (float)1E-25;
+ input += dc;
+ dc = -dc;
+
+ /* allocate history array if different size than last call */
+
+ coef_ptr = iir.coef; /* coefficient pointer */
+
+ hist1_ptr = iir.history; /* first history */
+ hist2_ptr = hist1_ptr + 1; /* next history */
+
+ /* 1st number of coefficients array is overall input scale factor,
+ * or filter gain */
+ output = input * (*coef_ptr++);
+
+ for (i = 0 ; i < iir.length; i++)
+ {
+ history1 = *hist1_ptr; /* history values */
+ history2 = *hist2_ptr;
+
+ output = output - history1 * coef_ptr[0];
+ new_hist = output - history2 * coef_ptr[1]; /* poles */
+
+ output = new_hist + history1 * coef_ptr[2];
+ output = output + history2 * coef_ptr[3]; /* zeros */
+
+ coef_ptr += 4;
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+ hist1_ptr++;
+ hist2_ptr++;
+ }
+
+ return(output);
+}
+
+
+void LowPass_Update(float resonance, float cutoff, int samplerate)
+{
+ unsigned nInd;
+ double a0, a1, a2, b0, b1, b2;
+ double fs; /* Sampling frequency, cutoff frequency */
+ double k; /* overall gain factor */
+ float *coef;
+
+ k = 1.0; /* Set overall filter gain */
+ coef = iir.coef + 1; /* Skip k, or gain */
+ fs = (double)samplerate; /* Sampling frequency (Hz) */
+
+/*
+ * Compute z-domain coefficients for each biquad section
+ * for new Cutoff Frequency and Resonance
+ */
+ for (nInd = 0; nInd < iir.length; nInd++)
+ {
+ a0 = ProtoCoef[nInd].a0;
+ a1 = ProtoCoef[nInd].a1;
+ a2 = ProtoCoef[nInd].a2;
+
+ b0 = ProtoCoef[nInd].b0;
+ b1 = ProtoCoef[nInd].b1 / resonance; /* Divide by resonance or Q */
+ b2 = ProtoCoef[nInd].b2;
+ szxform(&a0, &a1, &a2, &b0, &b1, &b2, cutoff, fs, &k, coef);
+ coef += 4; /* Point to next filter section */
+ }
+
+ /* Update overall filter gain in coef array */
+ iir.coef[0] = (float)k;
+}
+
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * initn()
+ *
+ * Example main function to show how to update filter coefficients.
+ * We create a 4th order filter (24 db/oct roloff), consisting
+ * of two second order sections.
+ * --------------------------------------------------------------------
+ */
+signed char LowPass_Init()
+{
+
+/*
+ * Setup filter s-domain coefficients
+ */
+ /* Section 1 */
+ ProtoCoef[0].a0 = 1.0;
+ ProtoCoef[0].a1 = 0;
+ ProtoCoef[0].a2 = 0;
+ ProtoCoef[0].b0 = 1.0;
+ ProtoCoef[0].b1 = 0.765367;
+ ProtoCoef[0].b2 = 1.0;
+
+ /* Section 2 */
+ ProtoCoef[1].a0 = 1.0;
+ ProtoCoef[1].a1 = 0;
+ ProtoCoef[1].a2 = 0;
+ ProtoCoef[1].b0 = 1.0;
+ ProtoCoef[1].b1 = 1.847759;
+ ProtoCoef[1].b2 = 1.0;
+
+ iir.length = FILTER_SECTIONS; /* Number of filter sections */
+
+/*
+ * Allocate array of z-domain coefficients for each filter section
+ * plus filter gain variable
+ */
+ iir.coef = (float *) calloc(4 * iir.length + 1, sizeof(float));
+ if (!iir.coef)
+ {
+// printf("Unable to allocate coef array, exiting\n");
+ return 0;
+ }
+
+ LowPass_Update(1.0, 5000.0, 44100);
+
+ /* Display filter coefficients */
+// for (nInd = 0; nInd < (iir.length * 4 + 1); nInd++)
+// printf("C[%d] = %15.10f\n", nInd, iir.coef[nInd]);
+/*
+ * To process audio samples, call function iir_filter()
+ * for each audio sample
+ */
+ return 1;
+}
+
+void LowPass_Close()
+{
+}
+
+
+/*
+ * ----------------------------------------------------------
+ * bilinear.c
+ *
+ * Perform bilinear transformation on s-domain coefficients
+ * of 2nd order biquad section.
+ * First design an analog filter and use s-domain coefficients
+ * as input to szxform() to convert them to z-domain.
+ *
+ * Here's the butterworth polinomials for 2nd, 4th and 6th order sections.
+ * When we construct a 24 db/oct filter, we take to 2nd order
+ * sections and compute the coefficients separately for each section.
+ *
+ * n Polinomials
+ * --------------------------------------------------------------------
+ * 2 s^2 + 1.4142s +1
+ * 4 (s^2 + 0.765367s + 1) (s^2 + 1.847759s + 1)
+ * 6 (s^2 + 0.5176387s + 1) (s^2 + 1.414214 + 1) (s^2 + 1.931852s + 1)
+ *
+ * Where n is a filter order.
+ * For n=4, or two second order sections, we have following equasions for each
+ * 2nd order stage:
+ *
+ * (1 / (s^2 + (1/Q) * 0.765367s + 1)) * (1 / (s^2 + (1/Q) * 1.847759s + 1))
+ *
+ * Where Q is filter quality factor in the range of
+ * 1 to 1000. The overall filter Q is a product of all
+ * 2nd order stages. For example, the 6th order filter
+ * (3 stages, or biquads) with individual Q of 2 will
+ * have filter Q = 2 * 2 * 2 = 8.
+ *
+ * The nominator part is just 1.
+ * The denominator coefficients for stage 1 of filter are:
+ * b2 = 1; b1 = 0.765367; b0 = 1;
+ * numerator is
+ * a2 = 0; a1 = 0; a0 = 1;
+ *
+ * The denominator coefficients for stage 1 of filter are:
+ * b2 = 1; b1 = 1.847759; b0 = 1;
+ * numerator is
+ * a2 = 0; a1 = 0; a0 = 1;
+ *
+ * These coefficients are used directly by the szxform()
+ * and bilinear() functions. For all stages the numerator
+ * is the same and the only thing that is different between
+ * different stages is 1st order coefficient. The rest of
+ * coefficients are the same for any stage and equal to 1.
+ *
+ * Any filter could be constructed using this approach.
+ *
+ * References:
+ * Van Valkenburg, "Analog Filter Design"
+ * Oxford University Press 1982
+ * ISBN 0-19-510734-9
+ *
+ * C Language Algorithms for Digital Signal Processing
+ * Paul Embree, Bruce Kimble
+ * Prentice Hall, 1991
+ * ISBN 0-13-133406-9
+ *
+ * Digital Filter Designer's Handbook
+ * With C++ Algorithms
+ * Britton Rorabaugh
+ * McGraw Hill, 1997
+ * ISBN 0-07-053806-9
+ * ----------------------------------------------------------
+ */
+
+void prewarp(double *a0, double *a1, double *a2, double fc, double fs);
+void bilinear(
+ double a0, double a1, double a2, /* numerator coefficients */
+ double b0, double b1, double b2, /* denominator coefficients */
+ double *k, /* overall gain factor */
+ double fs, /* sampling rate */
+ float *coef); /* pointer to 4 iir coefficients */
+
+
+/*
+ * ----------------------------------------------------------
+ * Pre-warp the coefficients of a numerator or denominator.
+ * Note that a0 is assumed to be 1, so there is no wrapping
+ * of it.
+ * ----------------------------------------------------------
+ */
+void prewarp(
+ double *a0, double *a1, double *a2,
+ double fc, double fs)
+{
+ double wp, pi;
+
+ pi = 4.0 * atan(1.0);
+ wp = 2.0 * fs * tan(pi * fc / fs);
+
+ *a2 = (*a2) / (wp * wp);
+ *a1 = (*a1) / wp;
+}
+
+
+/*
+ * ----------------------------------------------------------
+ * bilinear()
+ *
+ * Transform the numerator and denominator coefficients
+ * of s-domain biquad section into corresponding
+ * z-domain coefficients.
+ *
+ * Store the 4 IIR coefficients in array pointed by coef
+ * in following order:
+ * beta1, beta2 (denominator)
+ * alpha1, alpha2 (numerator)
+ *
+ * Arguments:
+ * a0-a2 - s-domain numerator coefficients
+ * b0-b2 - s-domain denominator coefficients
+ * k - filter gain factor. initially set to 1
+ * and modified by each biquad section in such
+ * a way, as to make it the coefficient by
+ * which to multiply the overall filter gain
+ * in order to achieve a desired overall filter gain,
+ * specified in initial value of k.
+ * fs - sampling rate (Hz)
+ * coef - array of z-domain coefficients to be filled in.
+ *
+ * Return:
+ * On return, set coef z-domain coefficients
+ * ----------------------------------------------------------
+ */
+void bilinear(
+ double a0, double a1, double a2, /* numerator coefficients */
+ double b0, double b1, double b2, /* denominator coefficients */
+ double *k, /* overall gain factor */
+ double fs, /* sampling rate */
+ float *coef /* pointer to 4 iir coefficients */
+)
+{
+ double ad, bd;
+
+ /* alpha (Numerator in s-domain) */
+ ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
+ /* beta (Denominator in s-domain) */
+ bd = 4. * b2 * fs * fs + 2. * b1* fs + b0;
+
+ /* update gain constant for this section */
+ *k *= ad/bd;
+
+ /* Denominator */
+ *coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd); /* beta1 */
+ *coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); /* beta2 */
+
+ /* Nominator */
+ *coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad); /* alpha1 */
+ *coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad); /* alpha2 */
+}
+
+
+/*
+ * ----------------------------------------------------------
+ * Transform from s to z domain using bilinear transform
+ * with prewarp.
+ *
+ * Arguments:
+ * For argument description look at bilinear()
+ *
+ * coef - pointer to array of floating point coefficients,
+ * corresponding to output of bilinear transofrm
+ * (z domain).
+ *
+ * Note: frequencies are in Hz.
+ * ----------------------------------------------------------
+ */
+void szxform(
+ double *a0, double *a1, double *a2, /* numerator coefficients */
+ double *b0, double *b1, double *b2, /* denominator coefficients */
+ double fc, /* Filter cutoff frequency */
+ double fs, /* sampling rate */
+ double *k, /* overall gain factor */
+ float *coef) /* pointer to 4 iir coefficients */
+{
+ /* Calculate a1 and a2 and overwrite the original values */
+ prewarp(a0, a1, a2, fc, fs);
+ prewarp(b0, b1, b2, fc, fs);
+ bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
+}
+
+
diff --git a/src/lowpass.h b/src/lowpass.h
new file mode 100644
index 0000000..6c99212
--- /dev/null
+++ b/src/lowpass.h
@@ -0,0 +1,46 @@
+#ifndef _LOWPASS_H
+#define _LOWPASS_H
+
+
+/* FILTER INFORMATION STRUCTURE FOR FILTER ROUTINES */
+
+#define FILTER_SECTIONS 2 /* 2 filter sections for 24 db/oct filter */
+
+typedef struct {
+ unsigned int length; /* size of filter */
+ float history[2 * FILTER_SECTIONS]; /* history in filter */
+ float *coef; /* pointer to coefficients of filter */
+} FILTER;
+
+typedef struct {
+ double a0, a1, a2; /* numerator coefficients */
+ double b0, b1, b2; /* denominator coefficients */
+} BIQUAD;
+
+static BIQUAD ProtoCoef[FILTER_SECTIONS]; /* Filter prototype coefficients,
+ 1 for each filter section */
+
+void szxform(
+ double *a0, double *a1, double *a2, /* numerator coefficients */
+ double *b0, double *b1, double *b2, /* denominator coefficients */
+ double fc, /* Filter cutoff frequency */
+ double fs, /* sampling rate */
+ double *k, /* overall gain factor */
+ float *coef); /* pointer to 4 iir coefficients */
+
+
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+ signed char LowPass_Init();
+ void LowPass_Close();
+ float LowPass_Filter(float input);
+ void LowPass_Update(float resonance, float cutoff, int samplerate);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/notes.h b/src/notes.h
new file mode 100644
index 0000000..ed6b3e6
--- /dev/null
+++ b/src/notes.h
@@ -0,0 +1,51 @@
+#ifndef NOTES_H
+#define NOTES_H
+
+static float note_C[] = {16.35, 32.70, 65.41, 130.81, 261.63, 523.25, 1046.50, 2093.00, 4186.01};
+static float note_Db[] = {17.32, 34.65, 69.30, 138.59, 277.18, 554.37, 1108.73, 2217.46, 4434.92};
+static float note_D[] = {18.35, 36.71, 73.42, 146.83, 293.66, 587.33, 1174.66, 2349.32, 4698.64};
+static float note_Eb[] = {19.45, 38.89, 77.78, 155.56, 311.13, 622.25, 1244.51, 2489.02, 4978.03};
+static float note_E[] = {20.60, 41.20, 82.41, 164.81, 329.63, 659.26, 1318.51, 2637.02};
+static float note_F[] = {21.83, 43.65, 87.31, 174.61, 349.23, 698.46, 1396.91, 2793.83};
+static float note_Gb[] = {23.12, 46.25, 92.50, 185.00, 369.99, 739.99, 1479.98, 2959.96};
+static float note_G[] = {24.50, 49.00, 98.00, 196.00, 392.00, 783.99, 1567.98, 3135.96};
+static float note_Ab[] = {25.96, 51.91, 103.83, 207.65, 415.30, 830.61, 1661.22, 3322.44};
+static float note_A[] = {27.50, 55.00, 110.00, 220.00, 440.00, 880.00, 1760.00, 3520.00};
+static float note_Bb[] = {29.14, 58.27, 116.54, 233.08, 466.16, 932.33, 1864.66, 3729.31};
+static float note_B[] = {30.87, 61.74, 123.47, 246.94, 493.88, 987.77, 1975.53, 3951.0};
+
+static float *notes[12] = {note_C, note_Db, note_D, note_Eb, note_E, note_F, note_Gb, note_G, note_Ab, note_A, note_Bb, note_B };
+
+static char *
+int_to_note(int n)
+{
+ switch (n) {
+ case 0:
+ return "C";
+ case 1:
+ return "Db";
+ case 2:
+ return "D";
+ case 3:
+ return "Eb";
+ case 4:
+ return "E";
+ case 5:
+ return "F";
+ case 6:
+ return "Gb";
+ case 7:
+ return "G";
+ case 8:
+ return "Ab";
+ case 9:
+ return "A";
+ case 10:
+ return "Bb";
+ case 11:
+ return "B";
+ }
+ return "";
+}
+
+#endif /* NOTES_H */
diff --git a/src/raygui.h b/src/raygui.h
new file mode 100644
index 0000000..833725d
--- /dev/null
+++ b/src/raygui.h
@@ -0,0 +1,4647 @@
+/*******************************************************************************************
+*
+* raygui v3.5-dev - A simple and easy-to-use immediate-mode gui library
+*
+* DESCRIPTION:
+*
+* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
+* available as a standalone library, as long as input and drawing functions are provided.
+*
+* Controls provided:
+*
+* # Container/separators Controls
+* - WindowBox --> StatusBar, Panel
+* - GroupBox --> Line
+* - Line
+* - Panel --> StatusBar
+* - ScrollPanel --> StatusBar
+*
+* # Basic Controls
+* - Label
+* - Button
+* - LabelButton --> Label
+* - Toggle
+* - ToggleGroup --> Toggle
+* - CheckBox
+* - ComboBox
+* - DropdownBox
+* - TextBox
+* - TextBoxMulti
+* - ValueBox --> TextBox
+* - Spinner --> Button, ValueBox
+* - Slider
+* - SliderBar --> Slider
+* - ProgressBar
+* - StatusBar
+* - DummyRec
+* - Grid
+*
+* # Advance Controls
+* - ListView
+* - ColorPicker --> ColorPanel, ColorBarHue
+* - MessageBox --> Window, Label, Button
+* - TextInputBox --> Window, Label, TextBox, Button
+*
+* It also provides a set of functions for styling the controls based on its properties (size, color).
+*
+*
+* RAYGUI STYLE (guiStyle):
+*
+* raygui uses a global data array for all gui style properties (allocated on data segment by default),
+* when a new style is loaded, it is loaded over the global style... but a default gui style could always be
+* recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one
+*
+* The global style array size is fixed and depends on the number of controls and properties:
+*
+* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
+*
+* guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+*
+* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
+* used for all controls, when any of those base values is set, it is automatically populated to all
+* controls, so, specific control values overwriting generic style should be set after base values.
+*
+* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
+* properties are actually common to all controls and can not be overwritten individually (like BASE ones)
+* Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR
+*
+* Custom control properties can be defined using the EXTENDED properties for each independent control.
+*
+* TOOL: rGuiStyler is a visual tool to customize raygui style.
+*
+*
+* RAYGUI ICONS (guiIcons):
+*
+* raygui could use a global array containing icons data (allocated on data segment by default),
+* a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set
+* must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded
+*
+* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
+* requires 8 integers (16*16/32) to be stored in memory.
+*
+* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set.
+*
+* The global icons array size is fixed and depends on the number of icons and size:
+*
+* static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS];
+*
+* guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+*
+* TOOL: rGuiIcons is a visual tool to customize raygui icons and create new ones.
+*
+*
+* CONFIGURATION:
+*
+* #define RAYGUI_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+* #define RAYGUI_STANDALONE
+* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
+* internally in the library and input management and drawing functions must be provided by
+* the user (check library implementation for further details).
+*
+* #define RAYGUI_NO_ICONS
+* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
+*
+* #define RAYGUI_CUSTOM_ICONS
+* Includes custom ricons.h header defining a set of custom icons,
+* this file can be generated using rGuiIcons tool
+*
+*
+* VERSIONS HISTORY:
+* 3.5 (xx-xxx-2022) ADDED: Multiple new icons, useful for code editing tools
+* ADDED: GuiTabBar(), based on GuiToggle()
+* REMOVED: Unneeded icon editing functions
+* REDESIGNED: GuiDrawText() to divide drawing by lines
+* REMOVED: MeasureTextEx() dependency, logic directly implemented
+* REMOVED: DrawTextEx() dependency, logic directly implemented
+* ADDED: Helper functions to split text in separate lines
+* 3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes
+* REMOVED: GuiScrollBar(), only internal
+* REDESIGNED: GuiPanel() to support text parameter
+* REDESIGNED: GuiScrollPanel() to support text parameter
+* REDESIGNED: GuiColorPicker() to support text parameter
+* REDESIGNED: GuiColorPanel() to support text parameter
+* REDESIGNED: GuiColorBarAlpha() to support text parameter
+* REDESIGNED: GuiColorBarHue() to support text parameter
+* REDESIGNED: GuiTextInputBox() to support password
+* 3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool)
+* REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures
+* REVIEWED: External icons usage logic
+* REVIEWED: GuiLine() for centered alignment when including text
+* RENAMED: Multiple controls properties definitions to prepend RAYGUI_
+* RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency
+* Projects updated and multiple tweaks
+* 3.0 (04-Nov-2021) Integrated ricons data to avoid external file
+* REDESIGNED: GuiTextBoxMulti()
+* REMOVED: GuiImageButton*()
+* Multiple minor tweaks and bugs corrected
+* 2.9 (17-Mar-2021) REMOVED: Tooltip API
+* 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle()
+* 2.7 (20-Feb-2020) ADDED: Possible tooltips API
+* 2.6 (09-Sep-2019) ADDED: GuiTextInputBox()
+* REDESIGNED: GuiListView*(), GuiDropdownBox(), GuiSlider*(), GuiProgressBar(), GuiMessageBox()
+* REVIEWED: GuiTextBox(), GuiSpinner(), GuiValueBox(), GuiLoadStyle()
+* Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties
+* ADDED: 8 new custom styles ready to use
+* Multiple minor tweaks and bugs corrected
+* 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner()
+* 2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed
+* Refactor all controls drawing mechanism to use control state
+* 2.2 (05-Feb-2019) ADDED: GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls
+* 2.1 (26-Dec-2018) REDESIGNED: GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string
+* REDESIGNED: Style system (breaking change)
+* 2.0 (08-Nov-2018) ADDED: Support controls guiLock and custom fonts
+* REVIEWED: GuiComboBox(), GuiListView()...
+* 1.9 (09-Oct-2018) REVIEWED: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()...
+* 1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout
+* 1.5 (21-Jun-2017) Working in an improved styles system
+* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
+* 1.3 (12-Jun-2017) Complete redesign of style system
+* 1.1 (01-Jun-2017) Complete review of the library
+* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
+* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
+* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
+*
+*
+* CONTRIBUTORS:
+*
+* Ramon Santamaria: Supervision, review, redesign, update and maintenance
+* Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019)
+* Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018)
+* Adria Arranz: Testing and Implementation of additional controls (2018)
+* Jordi Jorba: Testing and Implementation of additional controls (2018)
+* Albert Martos: Review and testing of the library (2015)
+* Ian Eito: Review and testing of the library (2015)
+* Kevin Gato: Initial implementation of basic components (2014)
+* Daniel Nicolas: Initial implementation of basic components (2014)
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RAYGUI_H
+#define RAYGUI_H
+
+#define RAYGUI_VERSION "3.2"
+
+#if !defined(RAYGUI_STANDALONE)
+ #include "raylib.h"
+#endif
+
+// Function specifiers in case library is build/used as a shared library (Windows)
+// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
+#if defined(_WIN32)
+ #if defined(BUILD_LIBTYPE_SHARED)
+ #define RAYGUIAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
+ #elif defined(USE_LIBTYPE_SHARED)
+ #define RAYGUIAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
+ #endif
+#endif
+
+// Function specifiers definition
+#ifndef RAYGUIAPI
+ #define RAYGUIAPI // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Allow custom memory allocators
+#ifndef RAYGUI_MALLOC
+ #define RAYGUI_MALLOC(sz) malloc(sz)
+#endif
+#ifndef RAYGUI_CALLOC
+ #define RAYGUI_CALLOC(n,sz) calloc(n,sz)
+#endif
+#ifndef RAYGUI_FREE
+ #define RAYGUI_FREE(p) free(p)
+#endif
+
+// Simple log system to avoid printf() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+#define RAYGUI_SUPPORT_LOG_INFO
+#if defined(RAYGUI_SUPPORT_LOG_INFO)
+ #define RAYGUI_LOG(...) printf(__VA_ARGS__)
+#else
+ #define RAYGUI_LOG(...)
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+// NOTE: Some types are required for RAYGUI_STANDALONE usage
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+ #ifndef __cplusplus
+ // Boolean type
+ #ifndef true
+ typedef enum { false, true } bool;
+ #endif
+ #endif
+
+ // Vector2 type
+ typedef struct Vector2 {
+ float x;
+ float y;
+ } Vector2;
+
+ // Vector3 type // -- ConvertHSVtoRGB(), ConvertRGBtoHSV()
+ typedef struct Vector3 {
+ float x;
+ float y;
+ float z;
+ } Vector3;
+
+ // Color type, RGBA (32bit)
+ typedef struct Color {
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+ unsigned char a;
+ } Color;
+
+ // Rectangle type
+ typedef struct Rectangle {
+ float x;
+ float y;
+ float width;
+ float height;
+ } Rectangle;
+
+ // TODO: Texture2D type is very coupled to raylib, required by Font type
+ // It should be redesigned to be provided by user
+ typedef struct Texture2D {
+ unsigned int id; // OpenGL texture id
+ int width; // Texture base width
+ int height; // Texture base height
+ int mipmaps; // Mipmap levels, 1 by default
+ int format; // Data format (PixelFormat type)
+ } Texture2D;
+
+ // Image, pixel data stored in CPU memory (RAM)
+ typedef struct Image {
+ void *data; // Image raw data
+ int width; // Image base width
+ int height; // Image base height
+ int mipmaps; // Mipmap levels, 1 by default
+ int format; // Data format (PixelFormat type)
+ } Image;
+
+ // GlyphInfo, font characters glyphs info
+ typedef struct GlyphInfo {
+ int value; // Character value (Unicode)
+ int offsetX; // Character offset X when drawing
+ int offsetY; // Character offset Y when drawing
+ int advanceX; // Character advance position X
+ Image image; // Character image data
+ } GlyphInfo;
+
+ // TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle()
+ // It should be redesigned to be provided by user
+ typedef struct Font {
+ int baseSize; // Base size (default chars height)
+ int glyphCount; // Number of glyph characters
+ int glyphPadding; // Padding around the glyph characters
+ Texture2D texture; // Texture atlas containing the glyphs
+ Rectangle *recs; // Rectangles in texture for the glyphs
+ GlyphInfo *glyphs; // Glyphs info data
+ } Font;
+#endif
+
+// Style property
+typedef struct GuiStyleProp {
+ unsigned short controlId;
+ unsigned short propertyId;
+ unsigned int propertyValue;
+} GuiStyleProp;
+
+// Gui control state
+typedef enum {
+ STATE_NORMAL = 0,
+ STATE_FOCUSED,
+ STATE_PRESSED,
+ STATE_DISABLED,
+} GuiState;
+
+// Gui control text alignment
+typedef enum {
+ TEXT_ALIGN_LEFT = 0,
+ TEXT_ALIGN_CENTER,
+ TEXT_ALIGN_RIGHT,
+} GuiTextAlignment;
+
+// Gui controls
+typedef enum {
+ // Default -> populates to all controls when set
+ DEFAULT = 0,
+ // Basic controls
+ LABEL, // Used also for: LABELBUTTON
+ BUTTON,
+ TOGGLE, // Used also for: TOGGLEGROUP
+ SLIDER, // Used also for: SLIDERBAR
+ PROGRESSBAR,
+ CHECKBOX,
+ COMBOBOX,
+ DROPDOWNBOX,
+ TEXTBOX, // Used also for: TEXTBOXMULTI
+ VALUEBOX,
+ SPINNER, // Uses: BUTTON, VALUEBOX
+ LISTVIEW,
+ COLORPICKER,
+ SCROLLBAR,
+ STATUSBAR
+} GuiControl;
+
+// Gui base properties for every control
+// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties)
+typedef enum {
+ BORDER_COLOR_NORMAL = 0,
+ BASE_COLOR_NORMAL,
+ TEXT_COLOR_NORMAL,
+ BORDER_COLOR_FOCUSED,
+ BASE_COLOR_FOCUSED,
+ TEXT_COLOR_FOCUSED,
+ BORDER_COLOR_PRESSED,
+ BASE_COLOR_PRESSED,
+ TEXT_COLOR_PRESSED,
+ BORDER_COLOR_DISABLED,
+ BASE_COLOR_DISABLED,
+ TEXT_COLOR_DISABLED,
+ BORDER_WIDTH,
+ TEXT_PADDING,
+ TEXT_ALIGNMENT,
+ RESERVED
+} GuiControlProperty;
+
+// Gui extended properties depend on control
+// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default 8 properties)
+//----------------------------------------------------------------------------------
+
+// DEFAULT extended properties
+// NOTE: Those properties are common to all controls or global
+typedef enum {
+ TEXT_SIZE = 16, // Text size (glyphs max height)
+ TEXT_SPACING, // Text spacing between glyphs
+ LINE_COLOR, // Line control color
+ BACKGROUND_COLOR, // Background color
+} GuiDefaultProperty;
+
+// Label
+//typedef enum { } GuiLabelProperty;
+
+// Button/Spinner
+//typedef enum { } GuiButtonProperty;
+
+// Toggle/ToggleGroup
+typedef enum {
+ GROUP_PADDING = 16, // ToggleGroup separation between toggles
+} GuiToggleProperty;
+
+// Slider/SliderBar
+typedef enum {
+ SLIDER_WIDTH = 16, // Slider size of internal bar
+ SLIDER_PADDING // Slider/SliderBar internal bar padding
+} GuiSliderProperty;
+
+// ProgressBar
+typedef enum {
+ PROGRESS_PADDING = 16, // ProgressBar internal padding
+} GuiProgressBarProperty;
+
+// ScrollBar
+typedef enum {
+ ARROWS_SIZE = 16,
+ ARROWS_VISIBLE,
+ SCROLL_SLIDER_PADDING, // (SLIDERBAR, SLIDER_PADDING)
+ SCROLL_SLIDER_SIZE,
+ SCROLL_PADDING,
+ SCROLL_SPEED,
+} GuiScrollBarProperty;
+
+// CheckBox
+typedef enum {
+ CHECK_PADDING = 16 // CheckBox internal check padding
+} GuiCheckBoxProperty;
+
+// ComboBox
+typedef enum {
+ COMBO_BUTTON_WIDTH = 16, // ComboBox right button width
+ COMBO_BUTTON_SPACING // ComboBox button separation
+} GuiComboBoxProperty;
+
+// DropdownBox
+typedef enum {
+ ARROW_PADDING = 16, // DropdownBox arrow separation from border and items
+ DROPDOWN_ITEMS_SPACING // DropdownBox items separation
+} GuiDropdownBoxProperty;
+
+// TextBox/TextBoxMulti/ValueBox/Spinner
+typedef enum {
+ TEXT_INNER_PADDING = 16, // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding
+ TEXT_LINES_SPACING, // TextBoxMulti lines separation
+} GuiTextBoxProperty;
+
+// Spinner
+typedef enum {
+ SPIN_BUTTON_WIDTH = 16, // Spinner left/right buttons width
+ SPIN_BUTTON_SPACING, // Spinner buttons separation
+} GuiSpinnerProperty;
+
+// ListView
+typedef enum {
+ LIST_ITEMS_HEIGHT = 16, // ListView items height
+ LIST_ITEMS_SPACING, // ListView items separation
+ SCROLLBAR_WIDTH, // ListView scrollbar size (usually width)
+ SCROLLBAR_SIDE, // ListView scrollbar side (0-left, 1-right)
+} GuiListViewProperty;
+
+// ColorPicker
+typedef enum {
+ COLOR_SELECTOR_SIZE = 16,
+ HUEBAR_WIDTH, // ColorPicker right hue bar width
+ HUEBAR_PADDING, // ColorPicker right hue bar separation from panel
+ HUEBAR_SELECTOR_HEIGHT, // ColorPicker right hue bar selector height
+ HUEBAR_SELECTOR_OVERFLOW // ColorPicker right hue bar selector overflow
+} GuiColorPickerProperty;
+
+#define SCROLLBAR_LEFT_SIDE 0
+#define SCROLLBAR_RIGHT_SIDE 1
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+#if defined(__cplusplus)
+extern "C" { // Prevents name mangling of functions
+#endif
+
+// Global gui state control functions
+RAYGUIAPI void GuiEnable(void); // Enable gui controls (global state)
+RAYGUIAPI void GuiDisable(void); // Disable gui controls (global state)
+RAYGUIAPI void GuiLock(void); // Lock gui controls (global state)
+RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state)
+RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state)
+RAYGUIAPI void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f
+RAYGUIAPI void GuiSetState(int state); // Set gui state (global state)
+RAYGUIAPI int GuiGetState(void); // Get gui state (global state)
+
+// Font set/get functions
+RAYGUIAPI void GuiSetFont(Font font); // Set gui custom font (global state)
+RAYGUIAPI Font GuiGetFont(void); // Get gui custom font (global state)
+
+// Style set/get functions
+RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property
+RAYGUIAPI int GuiGetStyle(int control, int property); // Get one style property
+
+// Container/separator controls, useful for controls organization
+RAYGUIAPI bool GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed
+RAYGUIAPI void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name
+RAYGUIAPI void GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text
+RAYGUIAPI void GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls
+RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1
+RAYGUIAPI Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll); // Scroll Panel control
+
+// Basic controls set
+RAYGUIAPI void GuiLabel(Rectangle bounds, const char *text); // Label control, shows text
+RAYGUIAPI bool GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked
+RAYGUIAPI bool GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked
+RAYGUIAPI bool GuiToggle(Rectangle bounds, const char *text, bool active); // Toggle Button control, returns true when active
+RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int active); // Toggle Group control, returns active toggle index
+RAYGUIAPI bool GuiCheckBox(Rectangle bounds, const char *text, bool checked); // Check Box control, returns true when active
+RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int active); // Combo Box control, returns selected item index
+RAYGUIAPI bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item
+RAYGUIAPI bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value
+RAYGUIAPI bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
+RAYGUIAPI bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text
+RAYGUIAPI bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines
+RAYGUIAPI float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider control, returns selected value
+RAYGUIAPI float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider Bar control, returns selected value
+RAYGUIAPI float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Progress Bar control, shows current progress value
+RAYGUIAPI void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text
+RAYGUIAPI void GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders
+RAYGUIAPI Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs); // Grid control, returns mouse cell position
+
+// Advance controls set
+RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active); // List View control, returns selected list item index
+RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, int *scrollIndex, int active); // List View with extended parameters
+RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message
+RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, int *secretViewActive); // Text Input Box control, ask for text, supports secret
+RAYGUIAPI Color GuiColorPicker(Rectangle bounds, const char *text, Color color); // Color Picker control (multiple color controls)
+RAYGUIAPI Color GuiColorPanel(Rectangle bounds, const char *text, Color color); // Color Panel control
+RAYGUIAPI float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha); // Color Bar Alpha control
+RAYGUIAPI float GuiColorBarHue(Rectangle bounds, const char *text, float value); // Color Bar Hue control
+
+// Styles loading functions
+RAYGUIAPI void GuiLoadStyle(const char *fileName); // Load style file over global style variable (.rgs)
+RAYGUIAPI void GuiLoadStyleDefault(void); // Load style default over global style
+
+// Tooltips management functions
+RAYGUIAPI void GuiEnableTooltip(void); // Enable gui tooltips (global state)
+RAYGUIAPI void GuiDisableTooltip(void); // Disable gui tooltips (global state)
+RAYGUIAPI void GuiSetTooltip(const char *tooltip); // Set tooltip string
+
+// Icons functionality
+RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported)
+
+#if !defined(RAYGUI_NO_ICONS)
+RAYGUIAPI unsigned int *GuiGetIcons(void); // Get raygui icons data pointer
+RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data
+RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color);
+RAYGUIAPI void GuiSetIconScale(int scale); // Set icon drawing size
+
+#if !defined(RAYGUI_CUSTOM_ICONS)
+//----------------------------------------------------------------------------------
+// Icons enumeration
+//----------------------------------------------------------------------------------
+typedef enum {
+ ICON_NONE = 0,
+ ICON_FOLDER_FILE_OPEN = 1,
+ ICON_FILE_SAVE_CLASSIC = 2,
+ ICON_FOLDER_OPEN = 3,
+ ICON_FOLDER_SAVE = 4,
+ ICON_FILE_OPEN = 5,
+ ICON_FILE_SAVE = 6,
+ ICON_FILE_EXPORT = 7,
+ ICON_FILE_ADD = 8,
+ ICON_FILE_DELETE = 9,
+ ICON_FILETYPE_TEXT = 10,
+ ICON_FILETYPE_AUDIO = 11,
+ ICON_FILETYPE_IMAGE = 12,
+ ICON_FILETYPE_PLAY = 13,
+ ICON_FILETYPE_VIDEO = 14,
+ ICON_FILETYPE_INFO = 15,
+ ICON_FILE_COPY = 16,
+ ICON_FILE_CUT = 17,
+ ICON_FILE_PASTE = 18,
+ ICON_CURSOR_HAND = 19,
+ ICON_CURSOR_POINTER = 20,
+ ICON_CURSOR_CLASSIC = 21,
+ ICON_PENCIL = 22,
+ ICON_PENCIL_BIG = 23,
+ ICON_BRUSH_CLASSIC = 24,
+ ICON_BRUSH_PAINTER = 25,
+ ICON_WATER_DROP = 26,
+ ICON_COLOR_PICKER = 27,
+ ICON_RUBBER = 28,
+ ICON_COLOR_BUCKET = 29,
+ ICON_TEXT_T = 30,
+ ICON_TEXT_A = 31,
+ ICON_SCALE = 32,
+ ICON_RESIZE = 33,
+ ICON_FILTER_POINT = 34,
+ ICON_FILTER_BILINEAR = 35,
+ ICON_CROP = 36,
+ ICON_CROP_ALPHA = 37,
+ ICON_SQUARE_TOGGLE = 38,
+ ICON_SYMMETRY = 39,
+ ICON_SYMMETRY_HORIZONTAL = 40,
+ ICON_SYMMETRY_VERTICAL = 41,
+ ICON_LENS = 42,
+ ICON_LENS_BIG = 43,
+ ICON_EYE_ON = 44,
+ ICON_EYE_OFF = 45,
+ ICON_FILTER_TOP = 46,
+ ICON_FILTER = 47,
+ ICON_TARGET_POINT = 48,
+ ICON_TARGET_SMALL = 49,
+ ICON_TARGET_BIG = 50,
+ ICON_TARGET_MOVE = 51,
+ ICON_CURSOR_MOVE = 52,
+ ICON_CURSOR_SCALE = 53,
+ ICON_CURSOR_SCALE_RIGHT = 54,
+ ICON_CURSOR_SCALE_LEFT = 55,
+ ICON_UNDO = 56,
+ ICON_REDO = 57,
+ ICON_REREDO = 58,
+ ICON_MUTATE = 59,
+ ICON_ROTATE = 60,
+ ICON_REPEAT = 61,
+ ICON_SHUFFLE = 62,
+ ICON_EMPTYBOX = 63,
+ ICON_TARGET = 64,
+ ICON_TARGET_SMALL_FILL = 65,
+ ICON_TARGET_BIG_FILL = 66,
+ ICON_TARGET_MOVE_FILL = 67,
+ ICON_CURSOR_MOVE_FILL = 68,
+ ICON_CURSOR_SCALE_FILL = 69,
+ ICON_CURSOR_SCALE_RIGHT_FILL = 70,
+ ICON_CURSOR_SCALE_LEFT_FILL = 71,
+ ICON_UNDO_FILL = 72,
+ ICON_REDO_FILL = 73,
+ ICON_REREDO_FILL = 74,
+ ICON_MUTATE_FILL = 75,
+ ICON_ROTATE_FILL = 76,
+ ICON_REPEAT_FILL = 77,
+ ICON_SHUFFLE_FILL = 78,
+ ICON_EMPTYBOX_SMALL = 79,
+ ICON_BOX = 80,
+ ICON_BOX_TOP = 81,
+ ICON_BOX_TOP_RIGHT = 82,
+ ICON_BOX_RIGHT = 83,
+ ICON_BOX_BOTTOM_RIGHT = 84,
+ ICON_BOX_BOTTOM = 85,
+ ICON_BOX_BOTTOM_LEFT = 86,
+ ICON_BOX_LEFT = 87,
+ ICON_BOX_TOP_LEFT = 88,
+ ICON_BOX_CENTER = 89,
+ ICON_BOX_CIRCLE_MASK = 90,
+ ICON_POT = 91,
+ ICON_ALPHA_MULTIPLY = 92,
+ ICON_ALPHA_CLEAR = 93,
+ ICON_DITHERING = 94,
+ ICON_MIPMAPS = 95,
+ ICON_BOX_GRID = 96,
+ ICON_GRID = 97,
+ ICON_BOX_CORNERS_SMALL = 98,
+ ICON_BOX_CORNERS_BIG = 99,
+ ICON_FOUR_BOXES = 100,
+ ICON_GRID_FILL = 101,
+ ICON_BOX_MULTISIZE = 102,
+ ICON_ZOOM_SMALL = 103,
+ ICON_ZOOM_MEDIUM = 104,
+ ICON_ZOOM_BIG = 105,
+ ICON_ZOOM_ALL = 106,
+ ICON_ZOOM_CENTER = 107,
+ ICON_BOX_DOTS_SMALL = 108,
+ ICON_BOX_DOTS_BIG = 109,
+ ICON_BOX_CONCENTRIC = 110,
+ ICON_BOX_GRID_BIG = 111,
+ ICON_OK_TICK = 112,
+ ICON_CROSS = 113,
+ ICON_ARROW_LEFT = 114,
+ ICON_ARROW_RIGHT = 115,
+ ICON_ARROW_DOWN = 116,
+ ICON_ARROW_UP = 117,
+ ICON_ARROW_LEFT_FILL = 118,
+ ICON_ARROW_RIGHT_FILL = 119,
+ ICON_ARROW_DOWN_FILL = 120,
+ ICON_ARROW_UP_FILL = 121,
+ ICON_AUDIO = 122,
+ ICON_FX = 123,
+ ICON_WAVE = 124,
+ ICON_WAVE_SINUS = 125,
+ ICON_WAVE_SQUARE = 126,
+ ICON_WAVE_TRIANGULAR = 127,
+ ICON_CROSS_SMALL = 128,
+ ICON_PLAYER_PREVIOUS = 129,
+ ICON_PLAYER_PLAY_BACK = 130,
+ ICON_PLAYER_PLAY = 131,
+ ICON_PLAYER_PAUSE = 132,
+ ICON_PLAYER_STOP = 133,
+ ICON_PLAYER_NEXT = 134,
+ ICON_PLAYER_RECORD = 135,
+ ICON_MAGNET = 136,
+ ICON_LOCK_CLOSE = 137,
+ ICON_LOCK_OPEN = 138,
+ ICON_CLOCK = 139,
+ ICON_TOOLS = 140,
+ ICON_GEAR = 141,
+ ICON_GEAR_BIG = 142,
+ ICON_BIN = 143,
+ ICON_HAND_POINTER = 144,
+ ICON_LASER = 145,
+ ICON_COIN = 146,
+ ICON_EXPLOSION = 147,
+ ICON_1UP = 148,
+ ICON_PLAYER = 149,
+ ICON_PLAYER_JUMP = 150,
+ ICON_KEY = 151,
+ ICON_DEMON = 152,
+ ICON_TEXT_POPUP = 153,
+ ICON_GEAR_EX = 154,
+ ICON_CRACK = 155,
+ ICON_CRACK_POINTS = 156,
+ ICON_STAR = 157,
+ ICON_DOOR = 158,
+ ICON_EXIT = 159,
+ ICON_MODE_2D = 160,
+ ICON_MODE_3D = 161,
+ ICON_CUBE = 162,
+ ICON_CUBE_FACE_TOP = 163,
+ ICON_CUBE_FACE_LEFT = 164,
+ ICON_CUBE_FACE_FRONT = 165,
+ ICON_CUBE_FACE_BOTTOM = 166,
+ ICON_CUBE_FACE_RIGHT = 167,
+ ICON_CUBE_FACE_BACK = 168,
+ ICON_CAMERA = 169,
+ ICON_SPECIAL = 170,
+ ICON_LINK_NET = 171,
+ ICON_LINK_BOXES = 172,
+ ICON_LINK_MULTI = 173,
+ ICON_LINK = 174,
+ ICON_LINK_BROKE = 175,
+ ICON_TEXT_NOTES = 176,
+ ICON_NOTEBOOK = 177,
+ ICON_SUITCASE = 178,
+ ICON_SUITCASE_ZIP = 179,
+ ICON_MAILBOX = 180,
+ ICON_MONITOR = 181,
+ ICON_PRINTER = 182,
+ ICON_PHOTO_CAMERA = 183,
+ ICON_PHOTO_CAMERA_FLASH = 184,
+ ICON_HOUSE = 185,
+ ICON_HEART = 186,
+ ICON_CORNER = 187,
+ ICON_VERTICAL_BARS = 188,
+ ICON_VERTICAL_BARS_FILL = 189,
+ ICON_LIFE_BARS = 190,
+ ICON_INFO = 191,
+ ICON_CROSSLINE = 192,
+ ICON_HELP = 193,
+ ICON_FILETYPE_ALPHA = 194,
+ ICON_FILETYPE_HOME = 195,
+ ICON_LAYERS_VISIBLE = 196,
+ ICON_LAYERS = 197,
+ ICON_WINDOW = 198,
+ ICON_HIDPI = 199,
+ ICON_FILETYPE_BINARY = 200,
+ ICON_HEX = 201,
+ ICON_SHIELD = 202,
+ ICON_FILE_NEW = 203,
+ ICON_FOLDER_ADD = 204,
+ ICON_ALARM = 205,
+ ICON_CPU = 206,
+ ICON_ROM = 207,
+ ICON_STEP_OVER = 208,
+ ICON_STEP_INTO = 209,
+ ICON_STEP_OUT = 210,
+ ICON_RESTART = 211,
+ ICON_BREAKPOINT_ON = 212,
+ ICON_BREAKPOINT_OFF = 213,
+ ICON_BURGER_MENU = 214,
+ ICON_CASE_SENSITIVE = 215,
+ ICON_REG_EXP = 216,
+ ICON_FOLDER = 217,
+ ICON_FILE = 218,
+ ICON_219 = 219,
+ ICON_220 = 220,
+ ICON_221 = 221,
+ ICON_222 = 222,
+ ICON_223 = 223,
+ ICON_224 = 224,
+ ICON_225 = 225,
+ ICON_226 = 226,
+ ICON_227 = 227,
+ ICON_228 = 228,
+ ICON_229 = 229,
+ ICON_230 = 230,
+ ICON_231 = 231,
+ ICON_232 = 232,
+ ICON_233 = 233,
+ ICON_234 = 234,
+ ICON_235 = 235,
+ ICON_236 = 236,
+ ICON_237 = 237,
+ ICON_238 = 238,
+ ICON_239 = 239,
+ ICON_240 = 240,
+ ICON_241 = 241,
+ ICON_242 = 242,
+ ICON_243 = 243,
+ ICON_244 = 244,
+ ICON_245 = 245,
+ ICON_246 = 246,
+ ICON_247 = 247,
+ ICON_248 = 248,
+ ICON_249 = 249,
+ ICON_250 = 250,
+ ICON_251 = 251,
+ ICON_252 = 252,
+ ICON_253 = 253,
+ ICON_254 = 254,
+ ICON_255 = 255,
+} GuiIconName;
+#endif
+
+#endif
+
+#if defined(__cplusplus)
+} // Prevents name mangling of functions
+#endif
+
+#endif // RAYGUI_H
+
+/***********************************************************************************
+*
+* RAYGUI IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RAYGUI_IMPLEMENTATION)
+
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
+#include <stdlib.h> // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+#include <string.h> // Required for: strlen() [GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()], memset(), memcpy()
+#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
+#include <math.h> // Required for: roundf() [GuiColorPicker()]
+
+#ifdef __cplusplus
+ #define RAYGUI_CLITERAL(name) name
+#else
+ #define RAYGUI_CLITERAL(name) (name)
+#endif
+
+#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
+
+// Embedded icons, no external file provided
+#define RAYGUI_ICON_SIZE 16 // Size of icons in pixels (squared)
+#define RAYGUI_ICON_MAX_ICONS 256 // Maximum number of icons
+#define RAYGUI_ICON_MAX_NAME_LENGTH 32 // Maximum length of icon name id
+
+// Icons data is defined by bit array (every bit represents one pixel)
+// Those arrays are stored as unsigned int data arrays, so,
+// every array element defines 32 pixels (bits) of information
+// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels)
+// NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels)
+#define RAYGUI_ICON_DATA_ELEMENTS (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32)
+
+//----------------------------------------------------------------------------------
+// Icons data for all gui possible icons (allocated on data segment by default)
+//
+// NOTE 1: Every icon is codified in binary form, using 1 bit per pixel, so,
+// every 16x16 icon requires 8 integers (16*16/32) to be stored
+//
+// NOTE 2: A different icon set could be loaded over this array using GuiLoadIcons(),
+// but loaded icons set must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS
+//
+// guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_NONE
+ 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // ICON_FOLDER_FILE_OPEN
+ 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // ICON_FILE_SAVE_CLASSIC
+ 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // ICON_FOLDER_OPEN
+ 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // ICON_FOLDER_SAVE
+ 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // ICON_FILE_OPEN
+ 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // ICON_FILE_SAVE
+ 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // ICON_FILE_EXPORT
+ 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // ICON_FILE_ADD
+ 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // ICON_FILE_DELETE
+ 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_FILETYPE_TEXT
+ 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // ICON_FILETYPE_AUDIO
+ 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // ICON_FILETYPE_IMAGE
+ 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // ICON_FILETYPE_PLAY
+ 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // ICON_FILETYPE_VIDEO
+ 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // ICON_FILETYPE_INFO
+ 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // ICON_FILE_COPY
+ 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // ICON_FILE_CUT
+ 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // ICON_FILE_PASTE
+ 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_CURSOR_HAND
+ 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000, // ICON_CURSOR_POINTER
+ 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // ICON_CURSOR_CLASSIC
+ 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000, // ICON_PENCIL
+ 0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // ICON_PENCIL_BIG
+ 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // ICON_BRUSH_CLASSIC
+ 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // ICON_BRUSH_PAINTER
+ 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // ICON_WATER_DROP
+ 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // ICON_COLOR_PICKER
+ 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // ICON_RUBBER
+ 0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // ICON_COLOR_BUCKET
+ 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // ICON_TEXT_T
+ 0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // ICON_TEXT_A
+ 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // ICON_SCALE
+ 0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // ICON_RESIZE
+ 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_POINT
+ 0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_BILINEAR
+ 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // ICON_CROP
+ 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // ICON_CROP_ALPHA
+ 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002, // ICON_SQUARE_TOGGLE
+ 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // ICON_SYMMETRY
+ 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // ICON_SYMMETRY_HORIZONTAL
+ 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // ICON_SYMMETRY_VERTICAL
+ 0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // ICON_LENS
+ 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // ICON_LENS_BIG
+ 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // ICON_EYE_ON
+ 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // ICON_EYE_OFF
+ 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100, // ICON_FILTER_TOP
+ 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // ICON_FILTER
+ 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_POINT
+ 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL
+ 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG
+ 0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // ICON_TARGET_MOVE
+ 0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // ICON_CURSOR_MOVE
+ 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // ICON_CURSOR_SCALE
+ 0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // ICON_CURSOR_SCALE_RIGHT
+ 0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // ICON_CURSOR_SCALE_LEFT
+ 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO
+ 0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO
+ 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // ICON_REREDO
+ 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // ICON_MUTATE
+ 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // ICON_ROTATE
+ 0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // ICON_REPEAT
+ 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // ICON_SHUFFLE
+ 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // ICON_EMPTYBOX
+ 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // ICON_TARGET
+ 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL_FILL
+ 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG_FILL
+ 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // ICON_TARGET_MOVE_FILL
+ 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // ICON_CURSOR_MOVE_FILL
+ 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // ICON_CURSOR_SCALE_FILL
+ 0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // ICON_CURSOR_SCALE_RIGHT_FILL
+ 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // ICON_CURSOR_SCALE_LEFT_FILL
+ 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO_FILL
+ 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO_FILL
+ 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // ICON_REREDO_FILL
+ 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // ICON_MUTATE_FILL
+ 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // ICON_ROTATE_FILL
+ 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // ICON_REPEAT_FILL
+ 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // ICON_SHUFFLE_FILL
+ 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // ICON_EMPTYBOX_SMALL
+ 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX
+ 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP
+ 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_RIGHT
+ 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_RIGHT
+ 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // ICON_BOX_BOTTOM_RIGHT
+ 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // ICON_BOX_BOTTOM
+ 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // ICON_BOX_BOTTOM_LEFT
+ 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_LEFT
+ 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_LEFT
+ 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_CENTER
+ 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // ICON_BOX_CIRCLE_MASK
+ 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // ICON_POT
+ 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // ICON_ALPHA_MULTIPLY
+ 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // ICON_ALPHA_CLEAR
+ 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // ICON_DITHERING
+ 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // ICON_MIPMAPS
+ 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // ICON_BOX_GRID
+ 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // ICON_GRID
+ 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // ICON_BOX_CORNERS_SMALL
+ 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // ICON_BOX_CORNERS_BIG
+ 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // ICON_FOUR_BOXES
+ 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // ICON_GRID_FILL
+ 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // ICON_BOX_MULTISIZE
+ 0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_SMALL
+ 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_MEDIUM
+ 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // ICON_ZOOM_BIG
+ 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // ICON_ZOOM_ALL
+ 0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // ICON_ZOOM_CENTER
+ 0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // ICON_BOX_DOTS_SMALL
+ 0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000, // ICON_BOX_DOTS_BIG
+ 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // ICON_BOX_CONCENTRIC
+ 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // ICON_BOX_GRID_BIG
+ 0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000, // ICON_OK_TICK
+ 0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000, // ICON_CROSS
+ 0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000, // ICON_ARROW_LEFT
+ 0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000, // ICON_ARROW_RIGHT
+ 0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000, // ICON_ARROW_DOWN
+ 0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP
+ 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // ICON_ARROW_LEFT_FILL
+ 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // ICON_ARROW_RIGHT_FILL
+ 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // ICON_ARROW_DOWN_FILL
+ 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP_FILL
+ 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // ICON_AUDIO
+ 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // ICON_FX
+ 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // ICON_WAVE
+ 0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000, // ICON_WAVE_SINUS
+ 0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // ICON_WAVE_SQUARE
+ 0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000, // ICON_WAVE_TRIANGULAR
+ 0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000, // ICON_CROSS_SMALL
+ 0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // ICON_PLAYER_PREVIOUS
+ 0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000, // ICON_PLAYER_PLAY_BACK
+ 0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000, // ICON_PLAYER_PLAY
+ 0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // ICON_PLAYER_PAUSE
+ 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // ICON_PLAYER_STOP
+ 0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // ICON_PLAYER_NEXT
+ 0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // ICON_PLAYER_RECORD
+ 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // ICON_MAGNET
+ 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_CLOSE
+ 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_OPEN
+ 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // ICON_CLOCK
+ 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // ICON_TOOLS
+ 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // ICON_GEAR
+ 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // ICON_GEAR_BIG
+ 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // ICON_BIN
+ 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // ICON_HAND_POINTER
+ 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // ICON_LASER
+ 0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // ICON_COIN
+ 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // ICON_EXPLOSION
+ 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // ICON_1UP
+ 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // ICON_PLAYER
+ 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // ICON_PLAYER_JUMP
+ 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // ICON_KEY
+ 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // ICON_DEMON
+ 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // ICON_TEXT_POPUP
+ 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // ICON_GEAR_EX
+ 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK
+ 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK_POINTS
+ 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // ICON_STAR
+ 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // ICON_DOOR
+ 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // ICON_EXIT
+ 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // ICON_MODE_2D
+ 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D
+ 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE
+ 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP
+ 0x7fe00000, 0x50386030, 0x47fe483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT
+ 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT
+ 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM
+ 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT
+ 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK
+ 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA
+ 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL
+ 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET
+ 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // ICON_LINK_BOXES
+ 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // ICON_LINK_MULTI
+ 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // ICON_LINK
+ 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // ICON_LINK_BROKE
+ 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_TEXT_NOTES
+ 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // ICON_NOTEBOOK
+ 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // ICON_SUITCASE
+ 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // ICON_SUITCASE_ZIP
+ 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // ICON_MAILBOX
+ 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // ICON_MONITOR
+ 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // ICON_PRINTER
+ 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA
+ 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA_FLASH
+ 0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // ICON_HOUSE
+ 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // ICON_HEART
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // ICON_CORNER
+ 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // ICON_VERTICAL_BARS
+ 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // ICON_VERTICAL_BARS_FILL
+ 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // ICON_LIFE_BARS
+ 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // ICON_INFO
+ 0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002, // ICON_CROSSLINE
+ 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // ICON_HELP
+ 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // ICON_FILETYPE_ALPHA
+ 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // ICON_FILETYPE_HOME
+ 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS_VISIBLE
+ 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS
+ 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_WINDOW
+ 0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // ICON_HIDPI
+ 0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc, // ICON_FILETYPE_BINARY
+ 0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000, // ICON_HEX
+ 0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180, // ICON_SHIELD
+ 0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8, // ICON_FILE_NEW
+ 0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000, // ICON_FOLDER_ADD
+ 0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420, // ICON_ALARM
+ 0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0, // ICON_CPU
+ 0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0, // ICON_ROM
+ 0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000, // ICON_STEP_OVER
+ 0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_INTO
+ 0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_OUT
+ 0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000, // ICON_RESTART
+ 0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000, // ICON_BREAKPOINT_ON
+ 0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000, // ICON_BREAKPOINT_OFF
+ 0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000, // ICON_BURGER_MENU
+ 0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000, // ICON_CASE_SENSITIVE
+ 0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000, // ICON_REG_EXP
+ 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER
+ 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_219
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_223
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_224
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_225
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_226
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_227
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_228
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_231
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_232
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_233
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_234
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_235
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_236
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_237
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_238
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_239
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_240
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_241
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_242
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_243
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_244
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_245
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_246
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_247
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_248
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_249
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_253
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_254
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255
+};
+
+// NOTE: We keep a pointer to the icons array, useful to point to other sets if required
+static unsigned int *guiIconsPtr = guiIcons;
+
+#endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS
+
+#ifndef RAYGUI_ICON_SIZE
+ #define RAYGUI_ICON_SIZE 0
+#endif
+
+#define RAYGUI_MAX_CONTROLS 16 // Maximum number of standard controls
+#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of standard properties
+#define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Gui control property style color element
+typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_NORMAL, forces defined state
+
+static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib)
+static bool guiLocked = false; // Gui lock state (no inputs processed)
+static float guiAlpha = 1.0f; // Gui element transpacency on drawing
+
+static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled)
+
+static bool guiTooltip = false; // Tooltip enabled/disabled
+static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user)
+
+
+//----------------------------------------------------------------------------------
+// Style data array for all gui style properties (allocated on data segment by default)
+//
+// NOTE 1: First set of BASE properties are generic to all controls but could be individually
+// overwritten per control, first set of EXTENDED properties are generic to all controls and
+// can not be overwritten individually but custom EXTENDED properties can be used by control
+//
+// NOTE 2: A new style set could be loaded over this array using GuiLoadStyle(),
+// but default gui style could always be recovered with GuiLoadStyleDefault()
+//
+// guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 };
+
+static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization
+
+//----------------------------------------------------------------------------------
+// Standalone Mode Functions Declaration
+//
+// NOTE: raygui depend on some raylib input and drawing functions
+// To use raygui as standalone library, below functions must be defined by the user
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+
+#define KEY_RIGHT 262
+#define KEY_LEFT 263
+#define KEY_DOWN 264
+#define KEY_UP 265
+#define KEY_BACKSPACE 259
+#define KEY_ENTER 257
+
+#define MOUSE_LEFT_BUTTON 0
+
+// Input required functions
+//-------------------------------------------------------------------------------
+static Vector2 GetMousePosition(void);
+static float GetMouseWheelMove(void);
+static bool IsMouseButtonDown(int button);
+static bool IsMouseButtonPressed(int button);
+static bool IsMouseButtonReleased(int button);
+
+static bool IsKeyDown(int key);
+static bool IsKeyPressed(int key);
+static int GetCharPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()
+//-------------------------------------------------------------------------------
+
+// Drawing required functions
+//-------------------------------------------------------------------------------
+static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle(), GuiDrawIcon()
+
+static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
+//-------------------------------------------------------------------------------
+
+// Text required functions
+//-------------------------------------------------------------------------------
+static Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // -- GuiLoadStyle()
+static Font GetFontDefault(void); // -- GuiLoadStyleDefault()
+static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle()
+static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle()
+static char *LoadFileText(const char *fileName); // -- GuiLoadStyle()
+static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle()
+//-------------------------------------------------------------------------------
+
+// raylib functions already implemented in raygui
+//-------------------------------------------------------------------------------
+static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value
+static int ColorToInt(Color color); // Returns hexadecimal value for a Color
+static Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle
+static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
+static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings
+static int TextToInteger(const char *text); // Get integer value from text
+
+static int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded text
+static const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode codepoint into UTF-8 text (char array size returned as parameter)
+
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient
+//-------------------------------------------------------------------------------
+
+#endif // RAYGUI_STANDALONE
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+static int GetTextWidth(const char *text); // Gui get text width using gui font and style
+static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds
+static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor
+
+static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint); // Gui draw text using default font
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style
+
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings
+static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB
+static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV
+
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll bar control, used by GuiScrollPanel()
+static void GuiTooltip(Rectangle controlRec); // Draw tooltip using control rec position
+
+
+//----------------------------------------------------------------------------------
+// Gui Setup Functions Definition
+//----------------------------------------------------------------------------------
+// Enable gui global state
+// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups
+void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; }
+
+// Disable gui global state
+// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups
+void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; }
+
+// Lock gui global state
+void GuiLock(void) { guiLocked = true; }
+
+// Unlock gui global state
+void GuiUnlock(void) { guiLocked = false; }
+
+// Check if gui is locked (global state)
+bool GuiIsLocked(void) { return guiLocked; }
+
+// Set gui controls alpha global state
+void GuiFade(float alpha)
+{
+ if (alpha < 0.0f) alpha = 0.0f;
+ else if (alpha > 1.0f) alpha = 1.0f;
+
+ guiAlpha = alpha;
+}
+
+// Set gui state (global state)
+void GuiSetState(int state) { guiState = (GuiState)state; }
+
+// Get gui state (global state)
+int GuiGetState(void) { return guiState; }
+
+// Set custom gui font
+// NOTE: Font loading/unloading is external to raygui
+void GuiSetFont(Font font)
+{
+ if (font.texture.id > 0)
+ {
+ // NOTE: If we try to setup a font but default style has not been
+ // lazily loaded before, it will be overwritten, so we need to force
+ // default style loading first
+ if (!guiStyleLoaded) GuiLoadStyleDefault();
+
+ guiFont = font;
+ GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize);
+ }
+}
+
+// Get custom gui font
+Font GuiGetFont(void)
+{
+ return guiFont;
+}
+
+// Set control style property value
+void GuiSetStyle(int control, int property, int value)
+{
+ if (!guiStyleLoaded) GuiLoadStyleDefault();
+ guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+
+ // Default properties are propagated to all controls
+ if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE))
+ {
+ for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+ }
+}
+
+// Get control style property value
+int GuiGetStyle(int control, int property)
+{
+ if (!guiStyleLoaded) GuiLoadStyleDefault();
+ return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property];
+}
+
+//----------------------------------------------------------------------------------
+// Gui Controls Functions Definition
+//----------------------------------------------------------------------------------
+
+// Window Box control
+bool GuiWindowBox(Rectangle bounds, const char *title)
+{
+ // Window title bar height (including borders)
+ // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox()
+ #if !defined(RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT)
+ #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24
+ #endif
+
+ //GuiState state = guiState;
+ bool clicked = false;
+
+ int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT;
+
+ Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
+ if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
+
+ Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
+ Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20,
+ statusBar.y + statusBarHeight/2.0f - 18.0f/2.0f, 18, 18 };
+
+ // Update control
+ //--------------------------------------------------------------------
+ // NOTE: Logic is directly managed by button
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiStatusBar(statusBar, title); // Draw window header as status bar
+ GuiPanel(windowPanel, NULL); // Draw window base
+
+ // Draw window close button
+ int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+ int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+ GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+ clicked = GuiButton(closeButtonRec, "x");
+#else
+ clicked = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL));
+#endif
+ GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+ //--------------------------------------------------------------------
+
+ return clicked;
+}
+
+// Group Box control with text name
+void GuiGroupBox(Rectangle bounds, const char *text)
+{
+ #if !defined(RAYGUI_GROUPBOX_LINE_THICK)
+ #define RAYGUI_GROUPBOX_LINE_THICK 1
+ #endif
+
+ GuiState state = guiState;
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha));
+
+ GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text);
+ //--------------------------------------------------------------------
+}
+
+// Line control
+void GuiLine(Rectangle bounds, const char *text)
+{
+ #if !defined(RAYGUI_LINE_ORIGIN_SIZE)
+ #define RAYGUI_LINE_MARGIN_TEXT 12
+ #endif
+ #if !defined(RAYGUI_LINE_TEXT_PADDING)
+ #define RAYGUI_LINE_TEXT_PADDING 4
+ #endif
+
+ GuiState state = guiState;
+
+ Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha);
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (text == NULL) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, bounds.width, 1 }, 0, BLANK, color);
+ else
+ {
+ Rectangle textBounds = { 0 };
+ textBounds.width = (float)GetTextWidth(text);
+ textBounds.height = bounds.height;
+ textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
+ textBounds.y = bounds.y;
+
+ // Draw line with embedded text label: "--- text --------------"
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+ GuiDrawText(text, textBounds, TEXT_ALIGN_LEFT, color);
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+ }
+ //--------------------------------------------------------------------
+}
+
+// Panel control
+void GuiPanel(Rectangle bounds, const char *text)
+{
+ #if !defined(RAYGUI_PANEL_BORDER_WIDTH)
+ #define RAYGUI_PANEL_BORDER_WIDTH 1
+ #endif
+
+ GuiState state = guiState;
+
+ // Text will be drawn as a header bar (if provided)
+ Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+ if ((text != NULL) && (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f)) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+ if (text != NULL)
+ {
+ // Move panel bounds after the header bar
+ bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+ bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1;
+ }
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar
+
+ GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha),
+ Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha));
+ //--------------------------------------------------------------------
+}
+
+// Tab Bar control
+// NOTE: Using GuiToggle() for the TABS
+int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
+{
+ #define RAYGUI_TABBAR_ITEM_WIDTH 160
+
+ GuiState state = guiState;
+
+ int closing = -1;
+ Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height };
+
+ if (*active < 0) *active = 0;
+ else if (*active > count - 1) *active = count - 1;
+
+ int offsetX = 0; // Required in case tabs go out of screen
+ offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth();
+ if (offsetX < 0) offsetX = 0;
+
+ // Draw control
+ //--------------------------------------------------------------------
+ for (int i = 0; i < count; i++)
+ {
+ tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX;
+
+ if (tabBounds.x < GetScreenWidth())
+ {
+ // Draw tabs as toggle controls
+ int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT);
+ int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING);
+ GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+ GuiSetStyle(TOGGLE, TEXT_PADDING, 8);
+ if (i == *active) GuiToggle(tabBounds, GuiIconText(12, text[i]), true);
+ else if (GuiToggle(tabBounds, GuiIconText(12, text[i]), false) == true) *active = i;
+ GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding);
+ GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment);
+
+ // Draw tab close button
+ // NOTE: Only draw close button for curren tab: if (CheckCollisionPointRec(mousePoint, tabBounds))
+ int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+ int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+ GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+ if (GuiButton(closeButtonRec, "x")) closing = i;
+#else
+ if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) closing = i;
+#endif
+ GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+ }
+ }
+
+ // Draw tab-bar bottom line
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL)));
+ //--------------------------------------------------------------------
+
+ return closing; // Return closing tab requested
+}
+
+// Scroll Panel control
+Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll)
+{
+ GuiState state = guiState;
+
+ Vector2 scrollPos = { 0.0f, 0.0f };
+ if (scroll != NULL) scrollPos = *scroll;
+
+ // Text will be drawn as a header bar (if provided)
+ Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+ if (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+ if (text != NULL)
+ {
+ // Move panel bounds after the header bar
+ bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+ bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1;
+ }
+
+ bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+ bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+
+ // Recheck to account for the other scrollbar being visible
+ if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+ if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+
+ int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+ int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+ Rectangle horizontalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)horizontalScrollBarWidth };
+ Rectangle verticalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)verticalScrollBarWidth, (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) };
+
+ // Calculate view area (area without the scrollbars)
+ Rectangle view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)?
+ RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } :
+ RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth };
+
+ // Clip view area to the actual content size
+ if (view.width > content.width) view.width = content.width;
+ if (view.height > content.height) view.height = content.height;
+
+ float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH);
+ float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+ float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f;
+ float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ // Check button state
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else state = STATE_FOCUSED;
+
+#if defined(SUPPORT_SCROLLBAR_KEY_INPUT)
+ if (hasHorizontalScrollBar)
+ {
+ if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+ if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+ }
+
+ if (hasVerticalScrollBar)
+ {
+ if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+ if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+ }
+#endif
+ float wheelMove = GetMouseWheelMove();
+
+ // Horizontal scroll (Shift + Mouse wheel)
+ if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_SHIFT))) scrollPos.x += wheelMove*20;
+ else scrollPos.y += wheelMove*20; // Vertical scroll
+ }
+ }
+
+ // Normalize scroll values
+ if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin;
+ if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax;
+ if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin;
+ if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax;
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar
+
+ GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); // Draw background
+
+ // Save size of the scrollbar slider
+ const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+
+ // Draw horizontal scrollbar if visible
+ if (hasHorizontalScrollBar)
+ {
+ // Change scrollbar slider size to show the diff in size between the content width and the widget width
+ GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/(int)content.width)*((int)bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)));
+ scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, (int)horizontalMin, (int)horizontalMax);
+ }
+ else scrollPos.x = 0.0f;
+
+ // Draw vertical scrollbar if visible
+ if (hasVerticalScrollBar)
+ {
+ // Change scrollbar slider size to show the diff in size between the content height and the widget height
+ GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/(int)content.height)*((int)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)));
+ scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, (int)verticalMin, (int)verticalMax);
+ }
+ else scrollPos.y = 0.0f;
+
+ // Draw detail corner rectangle if both scroll bars are visible
+ if (hasHorizontalScrollBar && hasVerticalScrollBar)
+ {
+ Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 };
+ GuiDrawRectangle(corner, 0, BLANK, Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))), guiAlpha));
+ }
+
+ // Draw scrollbar lines depending on current state
+ GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), guiAlpha), BLANK);
+
+ // Set scrollbar slider size back to the way it was before
+ GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider);
+ //--------------------------------------------------------------------
+
+ if (scroll != NULL) *scroll = scrollPos;
+
+ return view;
+}
+
+// Label control
+void GuiLabel(Rectangle bounds, const char *text)
+{
+ GuiState state = guiState;
+
+ // Update control
+ //--------------------------------------------------------------------
+ //...
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
+ //--------------------------------------------------------------------
+}
+
+// Button control, returns true when clicked
+bool GuiButton(Rectangle bounds, const char *text)
+{
+ GuiState state = guiState;
+ bool pressed = false;
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ // Check button state
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else state = STATE_FOCUSED;
+
+ if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha));
+ GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha));
+
+ if (state == STATE_FOCUSED) GuiTooltip(bounds);
+ //------------------------------------------------------------------
+
+ return pressed;
+}
+
+// Label button control
+bool GuiLabelButton(Rectangle bounds, const char *text)
+{
+ GuiState state = guiState;
+ bool pressed = false;
+
+ // NOTE: We force bounds.width to be all text
+ float textWidth = (float)GetTextWidth(text);
+ if (bounds.width < textWidth) bounds.width = textWidth;
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ // Check checkbox state
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else state = STATE_FOCUSED;
+
+ if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
+ //--------------------------------------------------------------------
+
+ return pressed;
+}
+
+// Toggle Button control, returns true when active
+bool GuiToggle(Rectangle bounds, const char *text, bool active)
+{
+ GuiState state = guiState;
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ // Check toggle button state
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+ {
+ state = STATE_NORMAL;
+ active = !active;
+ }
+ else state = STATE_FOCUSED;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (state == STATE_NORMAL)
+ {
+ GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha));
+ GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, (active? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha));
+ }
+ else
+ {
+ GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha));
+ GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha));
+ }
+
+ if (state == STATE_FOCUSED) GuiTooltip(bounds);
+ //--------------------------------------------------------------------
+
+ return active;
+}
+
+// Toggle Group control, returns toggled button codepointIndex
+int GuiToggleGroup(Rectangle bounds, const char *text, int active)
+{
+ #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS)
+ #define RAYGUI_TOGGLEGROUP_MAX_ITEMS 32
+ #endif
+
+ float initBoundsX = bounds.x;
+
+ // Get substrings items from text (items pointers)
+ int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 };
+ int itemCount = 0;
+ const char **items = GuiTextSplit(text, ';', &itemCount, rows);
+
+ int prevRow = rows[0];
+
+ for (int i = 0; i < itemCount; i++)
+ {
+ if (prevRow != rows[i])
+ {
+ bounds.x = initBoundsX;
+ bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING));
+ prevRow = rows[i];
+ }
+
+ if (i == active) GuiToggle(bounds, items[i], true);
+ else if (GuiToggle(bounds, items[i], false) == true) active = i;
+
+ bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING));
+ }
+
+ return active;
+}
+
+// Check Box control, returns true when active
+bool GuiCheckBox(Rectangle bounds, const char *text, bool checked)
+{
+ GuiState state = guiState;
+
+ Rectangle textBounds = { 0 };
+
+ if (text != NULL)
+ {
+ textBounds.width = (float)GetTextWidth(text);
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+ if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING);
+ }
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ Rectangle totalBounds = {
+ (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x,
+ bounds.y,
+ bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING),
+ bounds.height,
+ };
+
+ // Check checkbox state
+ if (CheckCollisionPointRec(mousePoint, totalBounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else state = STATE_FOCUSED;
+
+ if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) checked = !checked;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha), BLANK);
+
+ if (checked)
+ {
+ Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+ bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+ bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)),
+ bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) };
+ GuiDrawRectangle(check, 0, BLANK, Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)), guiAlpha));
+ }
+
+ GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
+ //--------------------------------------------------------------------
+
+ return checked;
+}
+
+// Combo Box control, returns selected item codepointIndex
+int GuiComboBox(Rectangle bounds, const char *text, int active)
+{
+ GuiState state = guiState;
+
+ bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING));
+
+ Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING),
+ (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height };
+
+ // Get substrings items from text (items pointers, lengths and count)
+ int itemCount = 0;
+ const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+ if (active < 0) active = 0;
+ else if (active > itemCount - 1) active = itemCount - 1;
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1))
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (CheckCollisionPointRec(mousePoint, bounds) ||
+ CheckCollisionPointRec(mousePoint, selector))
+ {
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+ {
+ active += 1;
+ if (active >= itemCount) active = 0;
+ }
+
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else state = STATE_FOCUSED;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ // Draw combo box main
+ GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha));
+ GuiDrawText(items[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha));
+
+ // Draw selector using a custom button
+ // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+ int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+ int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+ GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+ GuiButton(selector, TextFormat("%i/%i", active + 1, itemCount));
+
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+ GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+ //--------------------------------------------------------------------
+
+ return active;
+}
+
+// Dropdown Box control
+// NOTE: Returns mouse click
+bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode)
+{
+ GuiState state = guiState;
+ int itemSelected = *active;
+ int itemFocused = -1;
+
+ // Get substrings items from text (items pointers, lengths and count)
+ int itemCount = 0;
+ const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+ Rectangle boundsOpen = bounds;
+ boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+ Rectangle itemBounds = bounds;
+
+ bool pressed = false; // Check mouse button pressed
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1))
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (editMode)
+ {
+ state = STATE_PRESSED;
+
+ // Check if mouse has been pressed or released outside limits
+ if (!CheckCollisionPointRec(mousePoint, boundsOpen))
+ {
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
+ }
+
+ // Check if already selected item has been pressed again
+ if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
+
+ // Check focused and selected item
+ for (int i = 0; i < itemCount; i++)
+ {
+ // Update item rectangle y position for next item
+ itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+ if (CheckCollisionPointRec(mousePoint, itemBounds))
+ {
+ itemFocused = i;
+ if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+ {
+ itemSelected = i;
+ pressed = true; // Item selected, change to editMode = false
+ }
+ break;
+ }
+ }
+
+ itemBounds = bounds;
+ }
+ else
+ {
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+ {
+ pressed = true;
+ state = STATE_PRESSED;
+ }
+ else state = STATE_FOCUSED;
+ }
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (editMode) GuiPanel(boundsOpen, NULL);
+
+ GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)), guiAlpha));
+ GuiDrawText(items[itemSelected], GetTextBounds(DEFAULT, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)), guiAlpha));
+
+ if (editMode)
+ {
+ // Draw visible items
+ for (int i = 0; i < itemCount; i++)
+ {
+ // Update item rectangle y position for next item
+ itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+ if (i == itemSelected)
+ {
+ GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)), guiAlpha));
+ GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)), guiAlpha));
+ }
+ else if (i == itemFocused)
+ {
+ GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)), guiAlpha));
+ GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)), guiAlpha));
+ }
+ else GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)), guiAlpha));
+ }
+ }
+
+ // Draw arrows (using icon if available)
+#if defined(RAYGUI_NO_ICONS)
+ GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
+ TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha));
+#else
+ GuiDrawText("#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
+ TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); // ICON_ARROW_DOWN_FILL
+#endif
+ //--------------------------------------------------------------------
+
+ *active = itemSelected;
+ return pressed;
+}
+
+// Text Box control, updates input text
+// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation)
+bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+ GuiState state = guiState;
+ Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
+
+ bool pressed = false;
+ int textWidth = GetTextWidth(text);
+
+ Rectangle cursor = {
+ bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + textWidth + 2,
+ bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE),
+ 4,
+ (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2
+ };
+
+ if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
+ if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (editMode)
+ {
+ state = STATE_PRESSED;
+
+ int key = GetCharPressed(); // Returns codepoint as Unicode
+ int keyCount = (int)strlen(text);
+ int byteSize = 0;
+ const char *textUTF8 = CodepointToUTF8(key, &byteSize);
+
+ // Only allow keys in range [32..125]
+ if ((keyCount + byteSize) < textSize)
+ {
+ //float maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)*2));
+
+ if (key >= 32)
+ {
+ for (int i = 0; i < byteSize; i++)
+ {
+ text[keyCount] = textUTF8[i];
+ keyCount++;
+ }
+
+ text[keyCount] = '\0';
+ }
+ }
+
+ // Delete text
+ if (keyCount > 0)
+ {
+ if (IsKeyPressed(KEY_BACKSPACE))
+ {
+ while ((keyCount > 0) && ((text[--keyCount] & 0xc0) == 0x80));
+ text[keyCount] = '\0';
+ }
+ }
+
+ if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true;
+ }
+ else
+ {
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ state = STATE_FOCUSED;
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
+ }
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (state == STATE_PRESSED)
+ {
+ GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
+ }
+ else if (state == STATE_DISABLED)
+ {
+ GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha));
+ }
+ else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK);
+
+ if (editMode)
+ {
+ // In case we edit and text does not fit in the textbox,
+ // we move text pointer to a position it fits inside the text box
+ while ((textWidth >= textBounds.width) && (text[0] != '\0'))
+ {
+ int codepointSize = 0;
+ GetCodepointNext(text, &codepointSize);
+ text += codepointSize;
+ textWidth = GetTextWidth(text);
+ cursor.x = textBounds.x + textWidth + 2;
+ }
+ }
+
+ GuiDrawText(text, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha));
+
+ // Draw cursor
+ if (editMode) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
+ else if (state == STATE_FOCUSED) GuiTooltip(bounds);
+ //--------------------------------------------------------------------
+
+ return pressed;
+}
+
+// Spinner control, returns selected value
+bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+ GuiState state = guiState;
+
+ bool pressed = false;
+ int tempValue = *value;
+
+ Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y,
+ bounds.width - 2*(GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING)), bounds.height };
+ Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
+ Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
+
+ Rectangle textBounds = { 0 };
+ if (text != NULL)
+ {
+ textBounds.width = (float)GetTextWidth(text);
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+ if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING);
+ }
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ // Check spinner state
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else state = STATE_FOCUSED;
+ }
+ }
+
+#if defined(RAYGUI_NO_ICONS)
+ if (GuiButton(leftButtonBound, "<")) tempValue--;
+ if (GuiButton(rightButtonBound, ">")) tempValue++;
+#else
+ if (GuiButton(leftButtonBound, GuiIconText(ICON_ARROW_LEFT_FILL, NULL))) tempValue--;
+ if (GuiButton(rightButtonBound, GuiIconText(ICON_ARROW_RIGHT_FILL, NULL))) tempValue++;
+#endif
+
+ if (!editMode)
+ {
+ if (tempValue < minValue) tempValue = minValue;
+ if (tempValue > maxValue) tempValue = maxValue;
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ // TODO: Set Spinner properties for ValueBox
+ pressed = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode);
+
+ // Draw value selector custom buttons
+ // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+ int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+ int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+ GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+ GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+
+ // Draw text label if provided
+ GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
+ //--------------------------------------------------------------------
+
+ *value = tempValue;
+ return pressed;
+}
+
+// Value Box control, updates input text with numbers
+// NOTE: Requires static variables: frameCounter
+bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+ #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+ #define RAYGUI_VALUEBOX_MAX_CHARS 32
+ #endif
+
+ GuiState state = guiState;
+ bool pressed = false;
+
+ char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
+ sprintf(textValue, "%i", *value);
+
+ Rectangle textBounds = { 0 };
+ if (text != NULL)
+ {
+ textBounds.width = (float)GetTextWidth(text);
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+ if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+ }
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ bool valueHasChanged = false;
+
+ if (editMode)
+ {
+ state = STATE_PRESSED;
+
+ int keyCount = (int)strlen(textValue);
+
+ // Only allow keys in range [48..57]
+ if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ {
+ if (GetTextWidth(textValue) < bounds.width)
+ {
+ int key = GetCharPressed();
+ if ((key >= 48) && (key <= 57))
+ {
+ textValue[keyCount] = (char)key;
+ keyCount++;
+ valueHasChanged = true;
+ }
+ }
+ }
+
+ // Delete text
+ if (keyCount > 0)
+ {
+ if (IsKeyPressed(KEY_BACKSPACE))
+ {
+ keyCount--;
+ textValue[keyCount] = '\0';
+ valueHasChanged = true;
+ }
+ }
+
+ if (valueHasChanged) *value = TextToInteger(textValue);
+
+ // NOTE: We are not clamp values until user input finishes
+ //if (*value > maxValue) *value = maxValue;
+ //else if (*value < minValue) *value = minValue;
+
+ if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true;
+ }
+ else
+ {
+ if (*value > maxValue) *value = maxValue;
+ else if (*value < minValue) *value = minValue;
+
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ state = STATE_FOCUSED;
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
+ }
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ Color baseColor = BLANK;
+ if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+ else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+ // WARNING: BLANK color does not work properly with Fade()
+ GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha), baseColor);
+ GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha));
+
+ // Draw cursor
+ if (editMode)
+ {
+ // NOTE: ValueBox internal text is always centered
+ Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
+ GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha));
+ }
+
+ // Draw text label if provided
+ GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
+ //--------------------------------------------------------------------
+
+ return pressed;
+}
+
+// Text Box control with multiple lines
+bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+ GuiState state = guiState;
+ bool pressed = false;
+
+ Rectangle textAreaBounds = {
+ bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING),
+ bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING),
+ bounds.width - 2*(GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)),
+ bounds.height - 2*(GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING))
+ };
+
+ // Cursor position, [x, y] values should be updated
+ Rectangle cursor = { 0, -1, 4, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) + 2 };
+
+ float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; // Character rectangle scaling factor
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (editMode)
+ {
+ state = STATE_PRESSED;
+
+ // We get an Unicode codepoint
+ int codepoint = GetCharPressed();
+ int textLength = (int)strlen(text); // Length in bytes (UTF-8 string)
+ int byteSize = 0;
+ const char *textUTF8 = CodepointToUTF8(codepoint, &byteSize);
+
+ // Introduce characters
+ if ((textLength + byteSize) < textSize)
+ {
+ if (IsKeyPressed(KEY_ENTER))
+ {
+ text[textLength] = '\n';
+ textLength++;
+ }
+ else if (codepoint >= 32)
+ {
+ // Supports Unicode inputs -> Encoded to UTF-8
+ int charUTF8Length = 0;
+ const char *charEncoded = CodepointToUTF8(codepoint, &charUTF8Length);
+ memcpy(text + textLength, charEncoded, charUTF8Length);
+ textLength += charUTF8Length;
+ }
+ }
+
+ // Delete characters
+ if (textLength > 0)
+ {
+ if (IsKeyPressed(KEY_BACKSPACE))
+ {
+ if ((unsigned char)text[textLength - 1] < 127)
+ {
+ // Remove ASCII equivalent character (1 byte)
+ textLength--;
+ text[textLength] = '\0';
+ }
+ else
+ {
+ // Remove latest UTF-8 unicode character introduced (n bytes)
+ int charUTF8Length = 0;
+ while ((charUTF8Length < textLength) && ((unsigned char)text[textLength - 1 - charUTF8Length] & 0b01000000) == 0) charUTF8Length++;
+
+ textLength -= (charUTF8Length + 1);
+ text[textLength] = '\0';
+ }
+ }
+ }
+
+ // Exit edit mode
+ if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
+ }
+ else
+ {
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ state = STATE_FOCUSED;
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
+ }
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (state == STATE_PRESSED)
+ {
+ GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
+ }
+ else if (state == STATE_DISABLED)
+ {
+ GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha));
+ }
+ else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK);
+
+ int wrapMode = 1; // 0-No wrap, 1-Char wrap, 2-Word wrap
+ Vector2 cursorPos = { textAreaBounds.x, textAreaBounds.y };
+
+ //int lastSpacePos = 0;
+ //int lastSpaceWidth = 0;
+ //int lastSpaceCursorPos = 0;
+
+ for (int i = 0, codepointSize = 0; (text != NULL) && (text[i] != '\0'); i += codepointSize)
+ {
+ int codepoint = GetCodepointNext(text + i, &codepointSize);
+ int index = GetGlyphIndex(guiFont, codepoint); // If requested codepoint is not found, we get '?' (0x3f)
+ Rectangle atlasRec = guiFont.recs[index];
+ GlyphInfo glyphInfo = guiFont.glyphs[index]; // Glyph measures
+
+ if ((codepointSize == 1) && (codepoint == '\n'))
+ {
+ cursorPos.y += (guiFont.baseSize*scaleFactor + GuiGetStyle(TEXTBOX, TEXT_LINES_SPACING)); // Line feed
+ cursorPos.x = textAreaBounds.x; // Carriage return
+ }
+ else
+ {
+ if (wrapMode == 1)
+ {
+ int glyphWidth = 0;
+ if (glyphInfo.advanceX != 0) glyphWidth += glyphInfo.advanceX;
+ else glyphWidth += (int)(atlasRec.width + glyphInfo.offsetX);
+
+ // Jump line if the end of the text box area has been reached
+ if ((cursorPos.x + (glyphWidth*scaleFactor)) > (textAreaBounds.x + textAreaBounds.width))
+ {
+ cursorPos.y += (guiFont.baseSize*scaleFactor + GuiGetStyle(TEXTBOX, TEXT_LINES_SPACING)); // Line feed
+ cursorPos.x = textAreaBounds.x; // Carriage return
+ }
+ }
+ else if (wrapMode == 2)
+ {
+ /*
+ if ((codepointSize == 1) && (codepoint == ' '))
+ {
+ lastSpacePos = i;
+ lastSpaceWidth = 0;
+ lastSpaceCursorPos = cursorPos.x;
+ }
+
+ // Jump line if last word reaches end of text box area
+ if ((lastSpaceCursorPos + lastSpaceWidth) > (textAreaBounds.x + textAreaBounds.width))
+ {
+ cursorPos.y += 12; // Line feed
+ cursorPos.x = textAreaBounds.x; // Carriage return
+ }
+ */
+ }
+
+ // Draw current character glyph
+ DrawTextCodepoint(guiFont, codepoint, cursorPos, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha));
+
+ int glyphWidth = 0;
+ if (glyphInfo.advanceX != 0) glyphWidth += glyphInfo.advanceX;
+ else glyphWidth += (int)(atlasRec.width + glyphInfo.offsetX);
+
+ cursorPos.x += (glyphWidth*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ //if (i > lastSpacePos) lastSpaceWidth += (atlasRec.width + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ }
+ }
+
+ cursor.x = cursorPos.x;
+ cursor.y = cursorPos.y;
+
+ // Draw cursor position considering text glyphs
+ if (editMode) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
+ //--------------------------------------------------------------------
+
+ return pressed;
+}
+
+// Slider control with pro parameters
+// NOTE: Other GuiSlider*() controls use this one
+float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue, int sliderWidth)
+{
+ GuiState state = guiState;
+
+ int sliderValue = (int)(((value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
+
+ Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
+ 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
+
+ if (sliderWidth > 0) // Slider
+ {
+ slider.x += (sliderValue - sliderWidth/2);
+ slider.width = (float)sliderWidth;
+ }
+ else if (sliderWidth == 0) // SliderBar
+ {
+ slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
+ slider.width = (float)sliderValue;
+ }
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+ {
+ state = STATE_PRESSED;
+
+ // Get equivalent value and slider position from mousePoint.x
+ value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
+
+ if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider
+ else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar
+ }
+ else state = STATE_FOCUSED;
+ }
+
+ if (value > maxValue) value = maxValue;
+ else if (value < minValue) value = minValue;
+ }
+
+ // Bar limits check
+ if (sliderWidth > 0) // Slider
+ {
+ if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
+ else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
+ }
+ else if (sliderWidth == 0) // SliderBar
+ {
+ if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha));
+
+ // Draw slider internal bar (depends on state)
+ if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha));
+ else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha));
+
+ // Draw left/right text if provided
+ if (textLeft != NULL)
+ {
+ Rectangle textBounds = { 0 };
+ textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha));
+ }
+
+ if (textRight != NULL)
+ {
+ Rectangle textBounds = { 0 };
+ textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha));
+ }
+ //--------------------------------------------------------------------
+
+ return value;
+}
+
+// Slider control extended, returns selected value and has text
+float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue)
+{
+ return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
+}
+
+// Slider Bar control extended, returns selected value
+float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue)
+{
+ return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0);
+}
+
+// Progress Bar control extended, shows current progress value
+float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue)
+{
+ GuiState state = guiState;
+
+ Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
+ bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
+ bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) };
+
+ // Update control
+ //--------------------------------------------------------------------
+ if (value > maxValue) value = maxValue;
+
+ if (state != STATE_DISABLED) progress.width = ((float)(value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)));
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha), BLANK);
+
+ // Draw slider internal progress bar (depends on state)
+ if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha));
+ else if (state == STATE_FOCUSED) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha));
+
+ // Draw left/right text if provided
+ if (textLeft != NULL)
+ {
+ Rectangle textBounds = { 0 };
+ textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha));
+ }
+
+ if (textRight != NULL)
+ {
+ Rectangle textBounds = { 0 };
+ textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha));
+ }
+ //--------------------------------------------------------------------
+
+ return value;
+}
+
+// Status Bar control
+void GuiStatusBar(Rectangle bounds, const char *text)
+{
+ GuiState state = guiState;
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha),
+ Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha));
+ GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha));
+ //--------------------------------------------------------------------
+}
+
+// Dummy rectangle control, intended for placeholding
+void GuiDummyRec(Rectangle bounds, const char *text)
+{
+ GuiState state = guiState;
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ // Check button state
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+ else state = STATE_FOCUSED;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha));
+ GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha));
+ //------------------------------------------------------------------
+}
+
+// List View control
+int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active)
+{
+ int itemCount = 0;
+ const char **items = NULL;
+
+ if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+ return GuiListViewEx(bounds, items, itemCount, NULL, scrollIndex, active);
+}
+
+// List View control with extended parameters
+int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, int *scrollIndex, int active)
+{
+ GuiState state = guiState;
+ int itemFocused = (focus == NULL)? -1 : *focus;
+ int itemSelected = active;
+
+ // Check if we need a scroll bar
+ bool useScrollBar = false;
+ if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true;
+
+ // Define base item rectangle [0]
+ Rectangle itemBounds = { 0 };
+ itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING);
+ itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+ itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
+ itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT);
+ if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
+
+ // Get items on the list
+ int visibleItems = (int)bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+ if (visibleItems > count) visibleItems = count;
+
+ int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex;
+ if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0;
+ int endIndex = startIndex + visibleItems;
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ // Check mouse inside list view
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ state = STATE_FOCUSED;
+
+ // Check focused and selected item
+ for (int i = 0; i < visibleItems; i++)
+ {
+ if (CheckCollisionPointRec(mousePoint, itemBounds))
+ {
+ itemFocused = startIndex + i;
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+ {
+ if (itemSelected == (startIndex + i)) itemSelected = -1;
+ else itemSelected = startIndex + i;
+ }
+ break;
+ }
+
+ // Update item rectangle y position for next item
+ itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+ }
+
+ if (useScrollBar)
+ {
+ int wheelMove = (int)GetMouseWheelMove();
+ startIndex -= wheelMove;
+
+ if (startIndex < 0) startIndex = 0;
+ else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems;
+
+ endIndex = startIndex + visibleItems;
+ if (endIndex > count) endIndex = count;
+ }
+ }
+ else itemFocused = -1;
+
+ // Reset item rectangle y to [0]
+ itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background
+
+ // Draw visible items
+ for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
+ {
+ if (state == STATE_DISABLED)
+ {
+ if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha));
+
+ GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha));
+ }
+ else
+ {
+ if ((startIndex + i) == itemSelected)
+ {
+ // Draw item selected
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha));
+ GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), guiAlpha));
+ }
+ else if ((startIndex + i) == itemFocused)
+ {
+ // Draw item focused
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha));
+ GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), guiAlpha));
+ }
+ else
+ {
+ // Draw item normal
+ GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha));
+ }
+ }
+
+ // Update item rectangle y position for next item
+ itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+ }
+
+ if (useScrollBar)
+ {
+ Rectangle scrollBarBounds = {
+ bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+ bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+ bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
+ };
+
+ // Calculate percentage of visible items and apply same percentage to scrollbar
+ float percentVisible = (float)(endIndex - startIndex)/count;
+ float sliderSize = bounds.height*percentVisible;
+
+ int prevSliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); // Save default slider size
+ int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
+ GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)sliderSize); // Change slider size
+ GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed
+
+ startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems);
+
+ GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default
+ GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, prevSliderSize); // Reset slider size to default
+ }
+ //--------------------------------------------------------------------
+
+ if (focus != NULL) *focus = itemFocused;
+ if (scrollIndex != NULL) *scrollIndex = startIndex;
+
+ return itemSelected;
+}
+
+// Color Panel control
+Color GuiColorPanel(Rectangle bounds, const char *text, Color color)
+{
+ const Color colWhite = { 255, 255, 255, 255 };
+ const Color colBlack = { 0, 0, 0, 255 };
+
+ GuiState state = guiState;
+ Vector2 pickerSelector = { 0 };
+
+ Vector3 vcolor = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
+ Vector3 hsv = ConvertRGBtoHSV(vcolor);
+
+ pickerSelector.x = bounds.x + (float)hsv.y*bounds.width; // HSV: Saturation
+ pickerSelector.y = bounds.y + (1.0f - (float)hsv.z)*bounds.height; // HSV: Value
+
+ float hue = -1.0f;
+ Vector3 maxHue = { hue >= 0.0f ? hue : hsv.x, 1.0f, 1.0f };
+ Vector3 rgbHue = ConvertHSVtoRGB(maxHue);
+ Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x),
+ (unsigned char)(255.0f*rgbHue.y),
+ (unsigned char)(255.0f*rgbHue.z), 255 };
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+ {
+ state = STATE_PRESSED;
+ pickerSelector = mousePoint;
+
+ // Calculate color from picker
+ Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+ colorPick.x /= (float)bounds.width; // Get normalized value on x
+ colorPick.y /= (float)bounds.height; // Get normalized value on y
+
+ hsv.y = colorPick.x;
+ hsv.z = 1.0f - colorPick.y;
+
+ Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+ // NOTE: Vector3ToColor() only available on raylib 1.8.1
+ color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
+ (unsigned char)(255.0f*rgb.y),
+ (unsigned char)(255.0f*rgb.z),
+ (unsigned char)(255.0f*(float)color.a/255.0f) };
+
+ }
+ else state = STATE_FOCUSED;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (state != STATE_DISABLED)
+ {
+ DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha));
+ DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0));
+
+ // Draw color picker: selector
+ Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) };
+ GuiDrawRectangle(selector, 0, BLANK, Fade(colWhite, guiAlpha));
+ }
+ else
+ {
+ DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha));
+ }
+
+ GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK);
+ //--------------------------------------------------------------------
+
+ return color;
+}
+
+// Color Bar Alpha control
+// NOTE: Returns alpha value normalized [0..1]
+float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha)
+{
+ #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE)
+ #define RAYGUI_COLORBARALPHA_CHECKED_SIZE 10
+ #endif
+
+ GuiState state = guiState;
+ Rectangle selector = { (float)bounds.x + alpha*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (CheckCollisionPointRec(mousePoint, bounds) ||
+ CheckCollisionPointRec(mousePoint, selector))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+ {
+ state = STATE_PRESSED;
+
+ alpha = (mousePoint.x - bounds.x)/bounds.width;
+ if (alpha <= 0.0f) alpha = 0.0f;
+ if (alpha >= 1.0f) alpha = 1.0f;
+ //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2;
+ }
+ else state = STATE_FOCUSED;
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+
+ // Draw alpha bar: checked background
+ if (state != STATE_DISABLED)
+ {
+ int checksX = (int)bounds.width/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+ int checksY = (int)bounds.height/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+
+ for (int x = 0; x < checksX; x++)
+ {
+ for (int y = 0; y < checksY; y++)
+ {
+ Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE };
+ GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) : Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha));
+ }
+ }
+
+ DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha));
+ }
+ else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+ GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK);
+
+ // Draw alpha bar: selector
+ GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha));
+ //--------------------------------------------------------------------
+
+ return alpha;
+}
+
+// Color Bar Hue control
+// Returns hue value normalized [0..1]
+// NOTE: Other similar bars (for reference):
+// Color GuiColorBarSat() [WHITE->color]
+// Color GuiColorBarValue() [BLACK->color], HSV/HSL
+// float GuiColorBarLuminance() [BLACK->WHITE]
+float GuiColorBarHue(Rectangle bounds, const char *text, float hue)
+{
+ GuiState state = guiState;
+ Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + hue/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) };
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (CheckCollisionPointRec(mousePoint, bounds) ||
+ CheckCollisionPointRec(mousePoint, selector))
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+ {
+ state = STATE_PRESSED;
+
+ hue = (mousePoint.y - bounds.y)*360/bounds.height;
+ if (hue <= 0.0f) hue = 0.0f;
+ if (hue >= 359.0f) hue = 359.0f;
+
+ }
+ else state = STATE_FOCUSED;
+
+ /*if (IsKeyDown(KEY_UP))
+ {
+ hue -= 2.0f;
+ if (hue <= 0.0f) hue = 0.0f;
+ }
+ else if (IsKeyDown(KEY_DOWN))
+ {
+ hue += 2.0f;
+ if (hue >= 360.0f) hue = 360.0f;
+ }*/
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (state != STATE_DISABLED)
+ {
+ // Draw hue bar:color bars
+ DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha));
+ DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha));
+ DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha));
+ DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha));
+ DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha));
+ DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha));
+ }
+ else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+ GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK);
+
+ // Draw hue bar: selector
+ GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha));
+ //--------------------------------------------------------------------
+
+ return hue;
+}
+
+// Color Picker control
+// NOTE: It's divided in multiple controls:
+// Color GuiColorPanel(Rectangle bounds, Color color)
+// float GuiColorBarAlpha(Rectangle bounds, float alpha)
+// float GuiColorBarHue(Rectangle bounds, float value)
+// NOTE: bounds define GuiColorPanel() size
+Color GuiColorPicker(Rectangle bounds, const char *text, Color color)
+{
+ color = GuiColorPanel(bounds, NULL, color);
+
+ Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
+ //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
+
+ Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ color.r/255.0f, color.g/255.0f, color.b/255.0f });
+ hsv.x = GuiColorBarHue(boundsHue, NULL, hsv.x);
+ //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f);
+ Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+ color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), color.a };
+
+ return color;
+}
+
+// Message Box control
+int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons)
+{
+ #if !defined(RAYGUI_MESSAGEBOX_BUTTON_HEIGHT)
+ #define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT 24
+ #endif
+ #if !defined(RAYGUI_MESSAGEBOX_BUTTON_PADDING)
+ #define RAYGUI_MESSAGEBOX_BUTTON_PADDING 12
+ #endif
+
+ int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button
+
+ int buttonCount = 0;
+ const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+ Rectangle buttonBounds = { 0 };
+ buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+ buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+ buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+ buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+ int textWidth = GetTextWidth(message);
+
+ Rectangle textBounds = { 0 };
+ textBounds.x = bounds.x + bounds.width/2 - textWidth/2;
+ textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+ textBounds.width = (float)textWidth;
+ textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (GuiWindowBox(bounds, title)) clicked = 0;
+
+ int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+ GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+ GuiLabel(textBounds, message);
+ GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+
+ prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+ for (int i = 0; i < buttonCount; i++)
+ {
+ if (GuiButton(buttonBounds, buttonsText[i])) clicked = i + 1;
+ buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+ }
+
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment);
+ //--------------------------------------------------------------------
+
+ return clicked;
+}
+
+// Text Input Box control, ask for text
+int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, int *secretViewActive)
+{
+ #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT)
+ #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT 24
+ #endif
+ #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING)
+ #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING 12
+ #endif
+ #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT)
+ #define RAYGUI_TEXTINPUTBOX_HEIGHT 26
+ #endif
+
+ // Used to enable text edit mode
+ // WARNING: No more than one GuiTextInputBox() should be open at the same time
+ static bool textEditMode = false;
+
+ int btnIndex = -1;
+
+ int buttonCount = 0;
+ const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+ Rectangle buttonBounds = { 0 };
+ buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+ buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+ buttonBounds.width = (bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+ buttonBounds.height = RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT;
+
+ int messageInputHeight = (int)bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - 2*RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+
+ Rectangle textBounds = { 0 };
+ if (message != NULL)
+ {
+ int textSize = GetTextWidth(message);
+
+ textBounds.x = bounds.x + bounds.width/2 - textSize/2;
+ textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+ textBounds.width = (float)textSize;
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ }
+
+ Rectangle textBoxBounds = { 0 };
+ textBoxBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+ textBoxBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_TEXTINPUTBOX_HEIGHT/2;
+ if (message == NULL) textBoxBounds.y = bounds.y + 24 + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+ else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4);
+ textBoxBounds.width = bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*2;
+ textBoxBounds.height = RAYGUI_TEXTINPUTBOX_HEIGHT;
+
+ // Draw control
+ //--------------------------------------------------------------------
+ if (GuiWindowBox(bounds, title)) btnIndex = 0;
+
+ // Draw message if available
+ if (message != NULL)
+ {
+ int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+ GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+ GuiLabel(textBounds, message);
+ GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+ }
+
+ if (secretViewActive != NULL)
+ {
+ static char stars[] = "****************";
+ if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height },
+ ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+
+ *secretViewActive = GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", *secretViewActive);
+ }
+ else
+ {
+ if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+ }
+
+ int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+ for (int i = 0; i < buttonCount; i++)
+ {
+ if (GuiButton(buttonBounds, buttonsText[i])) btnIndex = i + 1;
+ buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+ }
+
+ GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment);
+ //--------------------------------------------------------------------
+
+ return btnIndex;
+}
+
+// Grid control
+// NOTE: Returns grid mouse-hover selected cell
+// About drawing lines at subpixel spacing, simple put, not easy solution:
+// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
+Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs)
+{
+ // Grid lines alpha amount
+ #if !defined(RAYGUI_GRID_ALPHA)
+ #define RAYGUI_GRID_ALPHA 0.15f
+ #endif
+
+ GuiState state = guiState;
+ Vector2 mousePoint = GetMousePosition();
+ Vector2 currentCell = { -1, -1 };
+
+ int linesV = ((int)(bounds.width/spacing))*subdivs + 1;
+ int linesH = ((int)(bounds.height/spacing))*subdivs + 1;
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ // NOTE: Cell values must be the upper left of the cell the mouse is in
+ currentCell.x = floorf((mousePoint.x - bounds.x)/spacing);
+ currentCell.y = floorf((mousePoint.y - bounds.y)/spacing);
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+
+ // TODO: Draw background panel?
+
+ switch (state)
+ {
+ case STATE_NORMAL:
+ {
+ if (subdivs > 0)
+ {
+ // Draw vertical grid lines
+ for (int i = 0; i < linesV; i++)
+ {
+ Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height };
+ GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA));
+ }
+
+ // Draw horizontal grid lines
+ for (int i = 0; i < linesH; i++)
+ {
+ Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width, 1 };
+ GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA));
+ }
+ }
+ } break;
+ default: break;
+ }
+
+ return currentCell;
+}
+
+//----------------------------------------------------------------------------------
+// Tooltip management functions
+// NOTE: Tooltips requires some global variables: tooltipPtr
+//----------------------------------------------------------------------------------
+// Enable gui tooltips (global state)
+void GuiEnableTooltip(void) { guiTooltip = true; }
+
+// Disable gui tooltips (global state)
+void GuiDisableTooltip(void) { guiTooltip = false; }
+
+// Set tooltip string
+void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; }
+
+
+//----------------------------------------------------------------------------------
+// Styles loading functions
+//----------------------------------------------------------------------------------
+
+// Load raygui style file (.rgs)
+// NOTE: By default a binary file is expected, that file could contain a custom font,
+// in that case, custom font image atlas is GRAY+ALPHA and pixel data can be compressed (DEFLATE)
+void GuiLoadStyle(const char *fileName)
+{
+ #define MAX_LINE_BUFFER_SIZE 256
+
+ bool tryBinary = false;
+
+ // Try reading the files as text file first
+ FILE *rgsFile = fopen(fileName, "rt");
+
+ if (rgsFile != NULL)
+ {
+ char buffer[MAX_LINE_BUFFER_SIZE] = { 0 };
+ fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+
+ if (buffer[0] == '#')
+ {
+ int controlId = 0;
+ int propertyId = 0;
+ unsigned int propertyValue = 0;
+
+ while (!feof(rgsFile))
+ {
+ switch (buffer[0])
+ {
+ case 'p':
+ {
+ // Style property: p <control_id> <property_id> <property_value> <property_name>
+
+ sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue);
+ GuiSetStyle(controlId, propertyId, (int)propertyValue);
+
+ } break;
+ case 'f':
+ {
+ // Style font: f <gen_font_size> <charmap_file> <font_file>
+
+ int fontSize = 0;
+ char charmapFileName[256] = { 0 };
+ char fontFileName[256] = { 0 };
+ sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName);
+
+ Font font = { 0 };
+
+ if (charmapFileName[0] != '0')
+ {
+ // Load characters from charmap file,
+ // expected '\n' separated list of integer values
+ char *charValues = LoadFileText(charmapFileName);
+ if (charValues != NULL)
+ {
+ int glyphCount = 0;
+ const char **chars = TextSplit(charValues, '\n', &glyphCount);
+
+ int *values = (int *)RAYGUI_MALLOC(glyphCount*sizeof(int));
+ for (int i = 0; i < glyphCount; i++) values[i] = TextToInteger(chars[i]);
+
+ if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+ font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, values, glyphCount);
+ if (font.texture.id == 0) font = GetFontDefault();
+
+ RAYGUI_FREE(values);
+ }
+ }
+ else
+ {
+ if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+ font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0);
+ if (font.texture.id == 0) font = GetFontDefault();
+ }
+
+ if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font);
+
+ } break;
+ default: break;
+ }
+
+ fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+ }
+ }
+ else tryBinary = true;
+
+ fclose(rgsFile);
+ }
+
+ if (tryBinary)
+ {
+ rgsFile = fopen(fileName, "rb");
+
+ if (rgsFile == NULL) return;
+
+ char signature[5] = { 0 };
+ short version = 0;
+ short reserved = 0;
+ int propertyCount = 0;
+
+ fread(signature, 1, 4, rgsFile);
+ fread(&version, sizeof(short), 1, rgsFile);
+ fread(&reserved, sizeof(short), 1, rgsFile);
+ fread(&propertyCount, sizeof(int), 1, rgsFile);
+
+ if ((signature[0] == 'r') &&
+ (signature[1] == 'G') &&
+ (signature[2] == 'S') &&
+ (signature[3] == ' '))
+ {
+ short controlId = 0;
+ short propertyId = 0;
+ unsigned int propertyValue = 0;
+
+ for (int i = 0; i < propertyCount; i++)
+ {
+ fread(&controlId, sizeof(short), 1, rgsFile);
+ fread(&propertyId, sizeof(short), 1, rgsFile);
+ fread(&propertyValue, sizeof(unsigned int), 1, rgsFile);
+
+ if (controlId == 0) // DEFAULT control
+ {
+ // If a DEFAULT property is loaded, it is propagated to all controls
+ // NOTE: All DEFAULT properties should be defined first in the file
+ GuiSetStyle(0, (int)propertyId, propertyValue);
+
+ if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue);
+ }
+ else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
+ }
+
+ // Font loading is highly dependant on raylib API to load font data and image
+#if !defined(RAYGUI_STANDALONE)
+ // Load custom font if available
+ int fontDataSize = 0;
+ fread(&fontDataSize, sizeof(int), 1, rgsFile);
+
+ if (fontDataSize > 0)
+ {
+ Font font = { 0 };
+ int fontType = 0; // 0-Normal, 1-SDF
+ Rectangle whiteRec = { 0 };
+
+ fread(&font.baseSize, sizeof(int), 1, rgsFile);
+ fread(&font.glyphCount, sizeof(int), 1, rgsFile);
+ fread(&fontType, sizeof(int), 1, rgsFile);
+
+ // Load font white rectangle
+ fread(&whiteRec, sizeof(Rectangle), 1, rgsFile);
+
+ // Load font image parameters
+ int fontImageUncompSize = 0;
+ int fontImageCompSize = 0;
+ fread(&fontImageUncompSize, sizeof(int), 1, rgsFile);
+ fread(&fontImageCompSize, sizeof(int), 1, rgsFile);
+
+ Image imFont = { 0 };
+ imFont.mipmaps = 1;
+ fread(&imFont.width, sizeof(int), 1, rgsFile);
+ fread(&imFont.height, sizeof(int), 1, rgsFile);
+ fread(&imFont.format, sizeof(int), 1, rgsFile);
+
+ if (fontImageCompSize < fontImageUncompSize)
+ {
+ // Compressed font atlas image data (DEFLATE), it requires DecompressData()
+ int dataUncompSize = 0;
+ unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
+ fread(compData, 1, fontImageCompSize, rgsFile);
+ imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize);
+
+ // Security check, dataUncompSize must match the provided fontImageUncompSize
+ if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted");
+
+ RAYGUI_FREE(compData);
+ }
+ else
+ {
+ // Font atlas image data is not compressed
+ imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
+ fread(imFont.data, 1, fontImageUncompSize, rgsFile);
+ }
+
+ if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+ font.texture = LoadTextureFromImage(imFont);
+ if (font.texture.id == 0) font = GetFontDefault();
+
+ RAYGUI_FREE(imFont.data);
+
+ // Load font recs data
+ font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle));
+ for (int i = 0; i < font.glyphCount; i++) fread(&font.recs[i], sizeof(Rectangle), 1, rgsFile);
+
+ // Load font chars info data
+ font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo));
+ for (int i = 0; i < font.glyphCount; i++)
+ {
+ fread(&font.glyphs[i].value, sizeof(int), 1, rgsFile);
+ fread(&font.glyphs[i].offsetX, sizeof(int), 1, rgsFile);
+ fread(&font.glyphs[i].offsetY, sizeof(int), 1, rgsFile);
+ fread(&font.glyphs[i].advanceX, sizeof(int), 1, rgsFile);
+ }
+
+ GuiSetFont(font);
+
+ // Set font texture source rectangle to be used as white texture to draw shapes
+ // NOTE: This way, all gui can be draw using a single draw call
+ if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec);
+ }
+#endif
+ }
+
+ fclose(rgsFile);
+ }
+}
+
+// Load style default over global style
+void GuiLoadStyleDefault(void)
+{
+ // We set this variable first to avoid cyclic function calls
+ // when calling GuiSetStyle() and GuiGetStyle()
+ guiStyleLoaded = true;
+
+ // Initialize default LIGHT style property values
+ GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff);
+ GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff);
+ GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff);
+ GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff);
+ GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff);
+ GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff);
+ GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff);
+ GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff);
+ GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff);
+ GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff);
+ GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff);
+ GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff);
+ GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); // WARNING: Some controls use other values
+ GuiSetStyle(DEFAULT, TEXT_PADDING, 0); // WARNING: Some controls use other values
+ GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); // WARNING: Some controls use other values
+
+ // Initialize control-specific property values
+ // NOTE: Those properties are in default list but require specific values by control type
+ GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+ GuiSetStyle(BUTTON, BORDER_WIDTH, 2);
+ GuiSetStyle(SLIDER, TEXT_PADDING, 4);
+ GuiSetStyle(CHECKBOX, TEXT_PADDING, 4);
+ GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT);
+ GuiSetStyle(TEXTBOX, TEXT_PADDING, 4);
+ GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+ GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
+ GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+ GuiSetStyle(SPINNER, TEXT_PADDING, 0);
+ GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+ GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
+ GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+
+ // Initialize extended property values
+ // NOTE: By default, extended property values are initialized to 0
+ GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls
+ GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls
+ GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property
+ GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property
+ GuiSetStyle(TOGGLE, GROUP_PADDING, 2);
+ GuiSetStyle(SLIDER, SLIDER_WIDTH, 16);
+ GuiSetStyle(SLIDER, SLIDER_PADDING, 1);
+ GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1);
+ GuiSetStyle(CHECKBOX, CHECK_PADDING, 1);
+ GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32);
+ GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
+ GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
+ GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
+ GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, 4);
+ GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4);
+ GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24);
+ GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2);
+ GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
+ GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
+ GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
+ GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0);
+ GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16);
+ GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0);
+ GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
+ GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
+ GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+ GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
+ GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
+ GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
+ GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16);
+ GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8);
+ GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8);
+ GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2);
+
+ if (guiFont.texture.id != GetFontDefault().texture.id)
+ {
+ // Unload previous font texture
+ UnloadTexture(guiFont.texture);
+
+ // Setup default raylib font
+ guiFont = GetFontDefault();
+
+ // Setup default raylib font rectangle
+ Rectangle whiteChar = { 41, 46, 2, 8 };
+ SetShapesTexture(guiFont.texture, whiteChar);
+ }
+}
+
+// Get text with icon id prepended
+// NOTE: Useful to add icons by name id (enum) instead of
+// a number that can change between ricon versions
+const char *GuiIconText(int iconId, const char *text)
+{
+#if defined(RAYGUI_NO_ICONS)
+ return NULL;
+#else
+ static char buffer[1024] = { 0 };
+ static char iconBuffer[6] = { 0 };
+
+ if (text != NULL)
+ {
+ memset(buffer, 0, 1024);
+ sprintf(buffer, "#%03i#", iconId);
+
+ for (int i = 5; i < 1024; i++)
+ {
+ buffer[i] = text[i - 5];
+ if (text[i - 5] == '\0') break;
+ }
+
+ return buffer;
+ }
+ else
+ {
+ sprintf(iconBuffer, "#%03i#", iconId & 0x1ff);
+
+ return iconBuffer;
+ }
+#endif
+}
+
+#if !defined(RAYGUI_NO_ICONS)
+// Get full icons data pointer
+unsigned int *GuiGetIcons(void) { return guiIconsPtr; }
+
+// Load raygui icons file (.rgi)
+// NOTE: In case nameIds are required, they can be requested with loadIconsName,
+// they are returned as a guiIconsName[iconCount][RAYGUI_ICON_MAX_NAME_LENGTH],
+// WARNING: guiIconsName[]][] memory should be manually freed!
+char **GuiLoadIcons(const char *fileName, bool loadIconsName)
+{
+ // Style File Structure (.rgi)
+ // ------------------------------------------------------
+ // Offset | Size | Type | Description
+ // ------------------------------------------------------
+ // 0 | 4 | char | Signature: "rGI "
+ // 4 | 2 | short | Version: 100
+ // 6 | 2 | short | reserved
+
+ // 8 | 2 | short | Num icons (N)
+ // 10 | 2 | short | Icons size (Options: 16, 32, 64) (S)
+
+ // Icons name id (32 bytes per name id)
+ // foreach (icon)
+ // {
+ // 12+32*i | 32 | char | Icon NameId
+ // }
+
+ // Icons data: One bit per pixel, stored as unsigned int array (depends on icon size)
+ // S*S pixels/32bit per unsigned int = K unsigned int per icon
+ // foreach (icon)
+ // {
+ // ... | K | unsigned int | Icon Data
+ // }
+
+ FILE *rgiFile = fopen(fileName, "rb");
+
+ char **guiIconsName = NULL;
+
+ if (rgiFile != NULL)
+ {
+ char signature[5] = { 0 };
+ short version = 0;
+ short reserved = 0;
+ short iconCount = 0;
+ short iconSize = 0;
+
+ fread(signature, 1, 4, rgiFile);
+ fread(&version, sizeof(short), 1, rgiFile);
+ fread(&reserved, sizeof(short), 1, rgiFile);
+ fread(&iconCount, sizeof(short), 1, rgiFile);
+ fread(&iconSize, sizeof(short), 1, rgiFile);
+
+ if ((signature[0] == 'r') &&
+ (signature[1] == 'G') &&
+ (signature[2] == 'I') &&
+ (signature[3] == ' '))
+ {
+ if (loadIconsName)
+ {
+ guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char **));
+ for (int i = 0; i < iconCount; i++)
+ {
+ guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH);
+ fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
+ }
+ }
+ else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
+
+ // Read icons data directly over internal icons array
+ fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile);
+ }
+
+ fclose(rgiFile);
+ }
+
+ return guiIconsName;
+}
+
+// Draw selected icon using rectangles pixel-by-pixel
+void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
+{
+ #define BIT_CHECK(a,b) ((a) & (1u<<(b)))
+
+ for (int i = 0, y = 0; i < RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32; i++)
+ {
+ for (int k = 0; k < 32; k++)
+ {
+ if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k))
+ {
+ #if !defined(RAYGUI_STANDALONE)
+ DrawRectangle(posX + (k%RAYGUI_ICON_SIZE)*pixelSize, posY + y*pixelSize, pixelSize, pixelSize, color);
+ #endif
+ }
+
+ if ((k == 15) || (k == 31)) y++;
+ }
+ }
+}
+
+// Set icon drawing size
+void GuiSetIconScale(int scale)
+{
+ if (scale >= 1) guiIconScale = scale;
+}
+
+#endif // !RAYGUI_NO_ICONS
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+// Gui get text width considering icon
+static int GetTextWidth(const char *text)
+{
+ #if !defined(ICON_TEXT_PADDING)
+ #define ICON_TEXT_PADDING 4
+ #endif
+
+ Vector2 textSize = { 0 };
+ int textIconOffset = 0;
+
+ if ((text != NULL) && (text[0] != '\0'))
+ {
+ if (text[0] == '#')
+ {
+ for (int i = 1; (text[i] != '\0') && (i < 5); i++)
+ {
+ if (text[i] == '#')
+ {
+ textIconOffset = i;
+ break;
+ }
+ }
+ }
+
+ text += textIconOffset;
+
+ // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+ float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+ // Custom MeasureText() implementation
+ if ((guiFont.texture.id > 0) && (text != NULL))
+ {
+ // Get size in bytes of text, considering end of line and line break
+ int size = 0;
+ for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+ {
+ if ((text[i] != '\0') && (text[i] != '\n')) size++;
+ else break;
+ }
+
+ float scaleFactor = fontSize/(float)guiFont.baseSize;
+ textSize.y = (float)guiFont.baseSize*scaleFactor;
+ float glyphWidth = 0.0f;
+
+ for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+ {
+ int codepoint = GetCodepointNext(&text[i], &codepointSize);
+ int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+ if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING));
+
+ textSize.x += glyphWidth;
+ }
+ }
+
+ if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING);
+ }
+
+ return (int)textSize.x;
+}
+
+// Get text bounds considering control bounds
+static Rectangle GetTextBounds(int control, Rectangle bounds)
+{
+ Rectangle textBounds = bounds;
+
+ textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH);
+ textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH);
+ textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);
+ textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH);
+
+ // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT
+ switch (control)
+ {
+ case COMBOBOX: textBounds.width -= (GuiGetStyle(control, COMBO_BUTTON_WIDTH) + GuiGetStyle(control, COMBO_BUTTON_SPACING)); break;
+ //case VALUEBOX: break; // NOTE: ValueBox text value always centered, text padding applies to label
+ default:
+ {
+ if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING);
+ else textBounds.x += GuiGetStyle(control, TEXT_PADDING);
+ textBounds.width -= 2 * GuiGetStyle(control, TEXT_PADDING);
+ }
+ break;
+ }
+
+ // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?)
+ // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER
+
+ return textBounds;
+}
+
+// Get text icon if provided and move text cursor
+// NOTE: We support up to 999 values for iconId
+static const char *GetTextIcon(const char *text, int *iconId)
+{
+#if !defined(RAYGUI_NO_ICONS)
+ *iconId = -1;
+ if (text[0] == '#') // Maybe we have an icon!
+ {
+ char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0'
+
+ int pos = 1;
+ while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9'))
+ {
+ iconValue[pos - 1] = text[pos];
+ pos++;
+ }
+
+ if (text[pos] == '#')
+ {
+ *iconId = TextToInteger(iconValue);
+
+ // Move text pointer after icon
+ // WARNING: If only icon provided, it could point to EOL character: '\0'
+ if (*iconId >= 0) text += (pos + 1);
+ }
+ }
+#endif
+
+ return text;
+}
+
+// Get text divided into lines (by line-breaks '\n')
+const char **GetTextLines(const char *text, int *count)
+{
+ #define RAYGUI_MAX_TEXT_LINES 128
+
+ static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+ for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings
+
+ int textSize = (int)strlen(text);
+
+ lines[0] = text;
+ int len = 0;
+ *count = 1;
+ int lineSize = 0; // Stores current line size, not returned
+
+ for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
+ {
+ if (text[i] == '\n')
+ {
+ lineSize = len;
+ k++;
+ lines[k] = &text[i + 1]; // WARNING: next value is valid?
+ len = 0;
+ *count += 1;
+ }
+ else len++;
+ }
+
+ //lines[*count - 1].size = len;
+
+ return lines;
+}
+
+// Gui draw text using default font
+static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint)
+{
+ #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect
+
+ #if !defined(ICON_TEXT_PADDING)
+ #define ICON_TEXT_PADDING 4
+ #endif
+
+ // We process the text lines one by one
+ if ((text != NULL) && (text[0] != '\0'))
+ {
+ // Get text lines ('\n' delimiter) to process lines individually
+ // NOTE: We can't use GuiTextSplit() because it can be already use before calling
+ // GuiDrawText() and static buffer would be overriden :(
+ int lineCount = 0;
+ const char **lines = GetTextLines(text, &lineCount);
+
+ //Rectangle textBounds = GetTextBounds(LABEL, bounds);
+ float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2);
+ float posOffsetY = 0;
+
+ for (int i = 0; i < lineCount; i++)
+ {
+ int iconId = 0;
+ lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor
+
+ // Get text position depending on alignment and iconId
+ //---------------------------------------------------------------------------------
+ Vector2 position = { bounds.x, bounds.y };
+
+ // TODO: We get text size after icon has been processed
+ // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
+ int textSizeX = GetTextWidth(lines[i]);
+
+ // If text requires an icon, add size to measure
+ if (iconId >= 0)
+ {
+ textSizeX += RAYGUI_ICON_SIZE*guiIconScale;
+
+ // WARNING: If only icon provided, text could be pointing to EOF character: '\0'
+ if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING;
+ }
+
+ // Check guiTextAlign global variables
+ switch (alignment)
+ {
+ case TEXT_ALIGN_LEFT:
+ {
+ position.x = bounds.x;
+ position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height);
+ } break;
+ case TEXT_ALIGN_CENTER:
+ {
+ position.x = bounds.x + bounds.width/2 - textSizeX/2;
+ position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height);
+ } break;
+ case TEXT_ALIGN_RIGHT:
+ {
+ position.x = bounds.x + bounds.width - textSizeX;
+ position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height);
+ } break;
+ default: break;
+ }
+
+ // NOTE: Make sure we get pixel-perfect coordinates,
+ // In case of decimals we got weird text positioning
+ position.x = (float)((int)position.x);
+ position.y = (float)((int)position.y);
+ //---------------------------------------------------------------------------------
+
+ // Draw text (with icon if available)
+ //---------------------------------------------------------------------------------
+#if !defined(RAYGUI_NO_ICONS)
+ if (iconId >= 0)
+ {
+ // NOTE: We consider icon height, probably different than text size
+ GuiDrawIcon(iconId, (int)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint);
+ position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+ }
+#endif
+ //DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint);
+
+ // Get size in bytes of text,
+ // considering end of line and line break
+ int size = 0;
+ for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n'); c++, size++){ }
+ float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+
+ int textOffsetY = 0;
+ float textOffsetX = 0.0f;
+ for (int c = 0, codepointSize = 0; c < size; c += codepointSize)
+ {
+ int codepoint = GetCodepointNext(&lines[i][c], &codepointSize);
+ int index = GetGlyphIndex(guiFont, codepoint);
+
+ // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+ // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+ if (codepoint == 0x3f) codepointSize = 1;
+
+ if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint
+ else
+ {
+ if ((codepoint != ' ') && (codepoint != '\t'))
+ {
+ // TODO: Draw only required text glyphs fitting the bounds.width, '...' can be appended at the end of the text
+ if (textOffsetX < bounds.width)
+ {
+ DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ position.x + textOffsetX, position.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), tint);
+ }
+ }
+
+ if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ }
+ }
+
+ posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; // TODO: GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)?
+ //---------------------------------------------------------------------------------
+ }
+ }
+}
+
+// Gui draw rectangle using default raygui plain style with borders
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color)
+{
+ if (color.a > 0)
+ {
+ // Draw rectangle filled with color
+ DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, color);
+ }
+
+ if (borderWidth > 0)
+ {
+ // Draw rectangle border lines with color
+ DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, borderColor);
+ DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor);
+ DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor);
+ DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, borderColor);
+ }
+}
+
+// Draw tooltip using control bounds
+static void GuiTooltip(Rectangle controlRec)
+{
+ if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL))
+ {
+ Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING));
+
+ if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
+
+ GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, NULL);
+
+ int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
+ int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+ GuiSetStyle(LABEL, TEXT_PADDING, 0);
+ GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+ GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, guiTooltipPtr);
+ GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
+ GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
+ }
+}
+
+// Split controls text into multiple strings
+// Also check for multiple columns (required by GuiToggleGroup())
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow)
+{
+ // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+ // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+ // all used memory is static... it has some limitations:
+ // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+ // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+ // NOTE: Those definitions could be externally provided if required
+
+ // WARNING: HACK: TODO: Review!
+ // textRow is an externally provided array of integers that stores row number for every splitted string
+
+ #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+ #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128
+ #endif
+ #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+ #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024
+ #endif
+
+ static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data)
+ static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added)
+ memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+ result[0] = buffer;
+ int counter = 1;
+
+ if (textRow != NULL) textRow[0] = 0;
+
+ // Count how many substrings we have on text and point to every one
+ for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+ {
+ buffer[i] = text[i];
+ if (buffer[i] == '\0') break;
+ else if ((buffer[i] == delimiter) || (buffer[i] == '\n'))
+ {
+ result[counter] = buffer + i + 1;
+
+ if (textRow != NULL)
+ {
+ if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1;
+ else textRow[counter] = textRow[counter - 1];
+ }
+
+ buffer[i] = '\0'; // Set an end of string at this point
+
+ counter++;
+ if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+ }
+ }
+
+ *count = counter;
+
+ return result;
+}
+
+// Convert color data from RGB to HSV
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertRGBtoHSV(Vector3 rgb)
+{
+ Vector3 hsv = { 0 };
+ float min = 0.0f;
+ float max = 0.0f;
+ float delta = 0.0f;
+
+ min = (rgb.x < rgb.y)? rgb.x : rgb.y;
+ min = (min < rgb.z)? min : rgb.z;
+
+ max = (rgb.x > rgb.y)? rgb.x : rgb.y;
+ max = (max > rgb.z)? max : rgb.z;
+
+ hsv.z = max; // Value
+ delta = max - min;
+
+ if (delta < 0.00001f)
+ {
+ hsv.y = 0.0f;
+ hsv.x = 0.0f; // Undefined, maybe NAN?
+ return hsv;
+ }
+
+ if (max > 0.0f)
+ {
+ // NOTE: If max is 0, this divide would cause a crash
+ hsv.y = (delta/max); // Saturation
+ }
+ else
+ {
+ // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
+ hsv.y = 0.0f;
+ hsv.x = 0.0f; // Undefined, maybe NAN?
+ return hsv;
+ }
+
+ // NOTE: Comparing float values could not work properly
+ if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta
+ else
+ {
+ if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow
+ else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan
+ }
+
+ hsv.x *= 60.0f; // Convert to degrees
+
+ if (hsv.x < 0.0f) hsv.x += 360.0f;
+
+ return hsv;
+}
+
+// Convert color data from HSV to RGB
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertHSVtoRGB(Vector3 hsv)
+{
+ Vector3 rgb = { 0 };
+ float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f;
+ long i = 0;
+
+ // NOTE: Comparing float values could not work properly
+ if (hsv.y <= 0.0f)
+ {
+ rgb.x = hsv.z;
+ rgb.y = hsv.z;
+ rgb.z = hsv.z;
+ return rgb;
+ }
+
+ hh = hsv.x;
+ if (hh >= 360.0f) hh = 0.0f;
+ hh /= 60.0f;
+
+ i = (long)hh;
+ ff = hh - i;
+ p = hsv.z*(1.0f - hsv.y);
+ q = hsv.z*(1.0f - (hsv.y*ff));
+ t = hsv.z*(1.0f - (hsv.y*(1.0f - ff)));
+
+ switch (i)
+ {
+ case 0:
+ {
+ rgb.x = hsv.z;
+ rgb.y = t;
+ rgb.z = p;
+ } break;
+ case 1:
+ {
+ rgb.x = q;
+ rgb.y = hsv.z;
+ rgb.z = p;
+ } break;
+ case 2:
+ {
+ rgb.x = p;
+ rgb.y = hsv.z;
+ rgb.z = t;
+ } break;
+ case 3:
+ {
+ rgb.x = p;
+ rgb.y = q;
+ rgb.z = hsv.z;
+ } break;
+ case 4:
+ {
+ rgb.x = t;
+ rgb.y = p;
+ rgb.z = hsv.z;
+ } break;
+ case 5:
+ default:
+ {
+ rgb.x = hsv.z;
+ rgb.y = p;
+ rgb.z = q;
+ } break;
+ }
+
+ return rgb;
+}
+
+// Scroll bar control (used by GuiScrollPanel())
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
+{
+ GuiState state = guiState;
+
+ // Is the scrollbar horizontal or vertical?
+ bool isVertical = (bounds.width > bounds.height)? false : true;
+
+ // The size (width or height depending on scrollbar type) of the spinner buttons
+ const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)?
+ (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) :
+ (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0;
+
+ // Arrow buttons [<] [>] [∧] [∨]
+ Rectangle arrowUpLeft = { 0 };
+ Rectangle arrowDownRight = { 0 };
+
+ // Actual area of the scrollbar excluding the arrow buttons
+ Rectangle scrollbar = { 0 };
+
+ // Slider bar that moves --[///]-----
+ Rectangle slider = { 0 };
+
+ // Normalize value
+ if (value > maxValue) value = maxValue;
+ if (value < minValue) value = minValue;
+
+ const int range = maxValue - minValue;
+ int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+
+ // Calculate rectangles for all of the components
+ arrowUpLeft = RAYGUI_CLITERAL(Rectangle){
+ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+ (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+ (float)spinnerSize, (float)spinnerSize };
+
+ if (isVertical)
+ {
+ arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+ scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) };
+ sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar
+ slider = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)), (float)bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), (float)sliderSize };
+ }
+ else // horizontal
+ {
+ arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+ scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) };
+ sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar
+ slider = RAYGUI_CLITERAL(Rectangle){ (float)scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)sliderSize, (float)bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) };
+ }
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ state = STATE_FOCUSED;
+
+ // Handle mouse wheel
+ int wheel = (int)GetMouseWheelMove();
+ if (wheel != 0) value += wheel;
+
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+ {
+ if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+ else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+
+ state = STATE_PRESSED;
+ }
+ else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+ {
+ if (!isVertical)
+ {
+ Rectangle scrollArea = { arrowUpLeft.x + arrowUpLeft.width, arrowUpLeft.y, scrollbar.width, bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) };
+ if (CheckCollisionPointRec(mousePoint, scrollArea)) value = (int)(((float)(mousePoint.x - scrollArea.x - slider.width/2)*range)/(scrollArea.width - slider.width) + minValue);
+ }
+ else
+ {
+ Rectangle scrollArea = { arrowUpLeft.x, arrowUpLeft.y+arrowUpLeft.height, bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), scrollbar.height };
+ if (CheckCollisionPointRec(mousePoint, scrollArea)) value = (int)(((float)(mousePoint.y - scrollArea.y - slider.height/2)*range)/(scrollArea.height - slider.height) + minValue);
+ }
+ }
+ }
+
+ // Normalize value
+ if (value > maxValue) value = maxValue;
+ if (value < minValue) value = minValue;
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background
+
+ GuiDrawRectangle(scrollbar, 0, BLANK, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background
+ GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar
+
+ // Draw arrows (using icon if available)
+ if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE))
+ {
+#if defined(RAYGUI_NO_ICONS)
+ GuiDrawText(isVertical? "^" : "<",
+ RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+ TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha));
+ GuiDrawText(isVertical? "v" : ">",
+ RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+ TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha));
+#else
+ GuiDrawText(isVertical? "#121#" : "#118#",
+ RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+ TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL
+ GuiDrawText(isVertical? "#120#" : "#119#",
+ RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+ TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL
+#endif
+ }
+ //--------------------------------------------------------------------
+
+ return value;
+}
+
+#if defined(RAYGUI_STANDALONE)
+// Returns a Color struct from hexadecimal value
+static Color GetColor(int hexValue)
+{
+ Color color;
+
+ color.r = (unsigned char)(hexValue >> 24) & 0xFF;
+ color.g = (unsigned char)(hexValue >> 16) & 0xFF;
+ color.b = (unsigned char)(hexValue >> 8) & 0xFF;
+ color.a = (unsigned char)hexValue & 0xFF;
+
+ return color;
+}
+
+// Returns hexadecimal value for a Color
+static int ColorToInt(Color color)
+{
+ return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
+}
+
+// Check if point is inside rectangle
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
+{
+ bool collision = false;
+
+ if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) &&
+ (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true;
+
+ return collision;
+}
+
+// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
+static Color Fade(Color color, float alpha)
+{
+ if (alpha < 0.0f) alpha = 0.0f;
+ else if (alpha > 1.0f) alpha = 1.0f;
+
+ Color result = { color.r, color.g, color.b, (unsigned char)(255.0f*alpha) };
+
+ return result;
+}
+
+// Formatting of text with variables to 'embed'
+static const char *TextFormat(const char *text, ...)
+{
+ #if !defined(RAYGUI_TEXTFORMAT_MAX_SIZE)
+ #define RAYGUI_TEXTFORMAT_MAX_SIZE 256
+ #endif
+
+ static char buffer[RAYGUI_TEXTFORMAT_MAX_SIZE];
+
+ va_list args;
+ va_start(args, text);
+ vsprintf(buffer, text, args);
+ va_end(args);
+
+ return buffer;
+}
+
+// Draw rectangle with vertical gradient fill color
+// NOTE: This function is only used by GuiColorPicker()
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2)
+{
+ Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height };
+ DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
+}
+
+// Split string into multiple strings
+const char **TextSplit(const char *text, char delimiter, int *count)
+{
+ // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+ // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+ // all used memory is static... it has some limitations:
+ // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+ // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+
+ #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+ #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128
+ #endif
+ #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+ #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024
+ #endif
+
+ static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL };
+ static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 };
+ memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+ result[0] = buffer;
+ int counter = 0;
+
+ if (text != NULL)
+ {
+ counter = 1;
+
+ // Count how many substrings we have on text and point to every one
+ for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+ {
+ buffer[i] = text[i];
+ if (buffer[i] == '\0') break;
+ else if (buffer[i] == delimiter)
+ {
+ buffer[i] = '\0'; // Set an end of string at this point
+ result[counter] = buffer + i + 1;
+ counter++;
+
+ if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+ }
+ }
+ }
+
+ *count = counter;
+ return result;
+}
+
+// Get integer value from text
+// NOTE: This function replaces atoi() [stdlib.h]
+static int TextToInteger(const char *text)
+{
+ int value = 0;
+ int sign = 1;
+
+ if ((text[0] == '+') || (text[0] == '-'))
+ {
+ if (text[0] == '-') sign = -1;
+ text++;
+ }
+
+ for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
+
+ return value*sign;
+}
+
+// Encode codepoint into UTF-8 text (char array size returned as parameter)
+static const char *CodepointToUTF8(int codepoint, int *byteSize)
+{
+ static char utf8[6] = { 0 };
+ int size = 0;
+
+ if (codepoint <= 0x7f)
+ {
+ utf8[0] = (char)codepoint;
+ size = 1;
+ }
+ else if (codepoint <= 0x7ff)
+ {
+ utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
+ utf8[1] = (char)((codepoint & 0x3f) | 0x80);
+ size = 2;
+ }
+ else if (codepoint <= 0xffff)
+ {
+ utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
+ utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
+ utf8[2] = (char)((codepoint & 0x3f) | 0x80);
+ size = 3;
+ }
+ else if (codepoint <= 0x10ffff)
+ {
+ utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
+ utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
+ utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
+ utf8[3] = (char)((codepoint & 0x3f) | 0x80);
+ size = 4;
+ }
+
+ *byteSize = size;
+
+ return utf8;
+}
+
+// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found
+// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
+// Total number of bytes processed are returned as a parameter
+// NOTE: the standard says U+FFFD should be returned in case of errors
+// but that character is not supported by the default font in raylib
+static int GetCodepointNext(const char *text, int *codepointSize)
+{
+ const char *ptr = text;
+ int codepoint = 0x3f; // Codepoint (defaults to '?')
+ *codepointSize = 0;
+
+ // Get current codepoint and bytes processed
+ if (0xf0 == (0xf8 & ptr[0]))
+ {
+ // 4 byte UTF-8 codepoint
+ codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
+ *codepointSize = 4;
+ }
+ else if (0xe0 == (0xf0 & ptr[0]))
+ {
+ // 3 byte UTF-8 codepoint */
+ codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
+ *codepointSize = 3;
+ }
+ else if (0xc0 == (0xe0 & ptr[0]))
+ {
+ // 2 byte UTF-8 codepoint
+ codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
+ *codepointSize = 2;
+ }
+ else
+ {
+ // 1 byte UTF-8 codepoint
+ codepoint = ptr[0];
+ *codepointSize = 1;
+ }
+
+ return codepoint;
+}
+#endif // RAYGUI_STANDALONE
+
+#endif // RAYGUI_IMPLEMENTATION
diff --git a/src/synth.c b/src/synth.c
new file mode 100644
index 0000000..5a08e4f
--- /dev/null
+++ b/src/synth.c
@@ -0,0 +1,114 @@
+/*
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* audio */
+#include <string.h>
+#include <portaudio.h>
+
+/* graphics */
+
+/* synth */
+#include "synth_engine.h"
+#include "synth_gui.h"
+
+#define NUM_SECONDS (1)
+
+#define WAVE_SIZE (44100)
+
+static void
+StreamFinished( void* synthData )
+{
+ synth_t *synth = (synth_t *) synthData;
+ printf( "Stream Completed\n");
+}
+
+int
+main(void) {
+ PaStreamParameters outputParameters;
+ PaStream *stream;
+ PaError err;
+ int i;
+ synth_t synth;
+ init_synth(&synth);
+
+
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+ const PaDeviceInfo *deviceInfo;
+ for( i=0; i< Pa_GetDeviceCount(); i++ ) {
+ deviceInfo = Pa_GetDeviceInfo( i );
+ //if (!strcmp("HyperX Cloud II Wireless: USB Audio (hw:0,0)", deviceInfo->name)) break;
+ if (!strcmp("HDA Intel PCH: ALC1220 Analog (hw:1,0)", deviceInfo->name)) break;
+ printf("dev: %s || %f\n", deviceInfo->name, deviceInfo->defaultSampleRate);
+ }
+
+
+ outputParameters.device = i; Pa_GetDefaultOutputDevice(); /* default output device */
+ printf("-------\nSelected device: %s\n-------\n", Pa_GetDeviceInfo(outputParameters.device)->name);
+ if (outputParameters.device == paNoDevice) {
+ fprintf(stderr,"Error: No default output device.\n");
+ goto error;
+ }
+ outputParameters.channelCount = 2; /* stereo output */
+ outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+ outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = NULL;
+
+ err = Pa_OpenStream(&stream,
+ NULL, /* no input */
+ &outputParameters,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ sound_gen,
+ &synth );
+ if( err != paNoError ) goto error;
+
+ err = Pa_SetStreamFinishedCallback( stream, &StreamFinished );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+
+ rayrun(&synth, stream);
+
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+
+ Pa_Terminate();
+ printf("Test finished.\n");
+
+ return err;
+ error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occurred while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
diff --git a/src/synth_engine.c b/src/synth_engine.c
new file mode 100644
index 0000000..57ec29b
--- /dev/null
+++ b/src/synth_engine.c
@@ -0,0 +1,332 @@
+#include "synth_engine.h"
+#include "lowpass.h"
+#include "filter.h"
+
+float
+adsr_amplitude(void *synthData, unsigned long long elapsed)
+{
+ synth_t *synth = (synth_t*)synthData;
+
+ float dAmplitude = 0.0;
+ float dReleaseAmplitude = 0.0;
+ float dStartAmplitude = 1.0;
+
+ float dLifeTime = (elapsed * (1.0 / (float)SAMPLE_RATE));
+
+ if (synth->n.noteOn != 0 && synth->n.noteOff == 0) {
+ if (dLifeTime <= synth->adsr.a)
+ dAmplitude = (dLifeTime / synth->adsr.a)*(dLifeTime / synth->adsr.a) * dStartAmplitude;
+
+ if (dLifeTime > synth->adsr.a && dLifeTime <= ( synth->adsr.a + synth->adsr.d))
+ dAmplitude = ((dLifeTime - synth->adsr.a) / synth->adsr.d) * (synth->adsr.s - dStartAmplitude) + dStartAmplitude;
+
+ if (dLifeTime > (synth->adsr.a + synth->adsr.d))
+ dAmplitude = synth->adsr.s;
+ }
+ else { // Note is off
+ if (dLifeTime <= synth->adsr.a)
+ dReleaseAmplitude = (dLifeTime / synth->adsr.a)*(dLifeTime / synth->adsr.a) * dStartAmplitude;
+
+ if (dLifeTime > synth->adsr.a && dLifeTime <= (synth->adsr.a + synth->adsr.d))
+ dReleaseAmplitude = ((dLifeTime - synth->adsr.a) / synth->adsr.d) * (synth->adsr.s - dStartAmplitude) + dStartAmplitude;
+
+ if (dLifeTime > (synth->adsr.a + synth->adsr.d))
+ dReleaseAmplitude = synth->adsr.s;
+
+ dAmplitude = (((synth->n.noteOn + dLifeTime) - synth->n.noteOff) / synth->adsr.r) * (0.0 - dReleaseAmplitude) + dReleaseAmplitude;
+
+ if (synth->adsr.r < 0) {
+ dAmplitude = synth->adsr.s;
+ }
+}
+ // Amplitude should not be negative
+ if (dAmplitude <= 0.000)
+ dAmplitude = 0.0;
+
+ return dAmplitude;
+}
+
+
+float
+sin_sample(float amp, float freq, unsigned long long phase, unsigned int sample_rate)
+{
+ return amp * sinf(2.0 * M_PI * freq * ((float)phase / (float)sample_rate));
+}
+
+float
+saw_sample(float amp, float freq, unsigned long long phase, unsigned int sample_rate)
+{
+ return amp * (0.17 * (1.0 - (2.0 * M_PI * freq * fmod((float)phase, (float)(sample_rate / (freq)))) / (float)sample_rate));
+}
+
+float
+sawX_sample(float amp, float freq, float sm, unsigned long long phase, unsigned int sample_rate)
+{
+ float dOutput = 0.0;
+ for (float n = 1.0; n < sm; n++)
+ dOutput += (sinf(n * 2.0 * M_PI * freq * ((float)phase / (float)sample_rate))) / n;
+ return 0.5 * amp * dOutput;
+}
+
+float
+sqr_sample(float amp, float freq, float duty_cycle, unsigned long long phase, unsigned int sample_rate)
+{
+ if (duty_cycle < 0.0001 || duty_cycle > 0.9999) {
+ duty_cycle = 0.5;
+ }
+
+ return (fmod((float)phase / (float)sample_rate, 1.0 / freq) < duty_cycle * (1.0 / freq)) ? amp : -amp;
+}
+
+float
+gen0(float f, unsigned long long phase, float x, unsigned int sample_rate)
+{
+ return sqr_sample(0.1, f, 0.3, phase, sample_rate)
+ + sqr_sample(0.1, f * 3.0 / 2.0 , 0.5, phase, sample_rate)
+ + saw_sample(0.3, f, phase, sample_rate)
+ + sin_sample(0.1, f, phase, sample_rate)
+ + sin_sample(0.1, f * 5, phase, sample_rate)
+ /* + sin_sample(0.1, freq * 50 * 1021, phase, sample_rate) */
+ /* + sin_sample(0.1, freq * 50 * 3531021, phase, sample_rate) */
+ + sin_sample(0.1, f * 7, phase, sample_rate);
+}
+
+float
+gen1(float f, unsigned long long phase, float x, unsigned int sample_rate)
+{
+ return sawX_sample(0.5, f, 5, phase, sample_rate)
+ + saw_sample(0.3, 2 * f / 5, phase, sample_rate)
+ + sin_sample(0.2, f * 5.0 / 7.0 , phase, sample_rate);
+}
+
+float
+gen2(float f, unsigned long long phase, float x, unsigned int sample_rate)
+{
+ return sin_sample(0.5, f * sqrt(2) , phase, sample_rate)
+ + sin_sample(0.5, f, phase, sample_rate);
+
+ /* sawX_sample(1, synth->freq, 5, phase, sample_rate); */
+}
+
+float
+gen3(float f, unsigned long long phase, float x, unsigned int sample_rate)
+{
+ return sawX_sample(0.7, f, 5, phase, sample_rate)
+ + sin_sample(0.3, 4.0/17.0*f, phase, sample_rate);
+ /* return sawX_sample(0.5, f * (1 + sqrt(5)) / 2, 5, phase, sample_rate) */
+ /* + sin_sample(0.3, f * x, phase, sample_rate) */
+ /* + sqr_sample(0.2, f * x, 0.2 * x * x, phase, sample_rate); */
+}
+
+
+
+/* 1d convolution */
+void
+convole(float *signal, float *filter, size_t signal_size, size_t filter_size, float *out) {
+ for (size_t i = 0; i < filter_size + signal_size; i++) {
+ size_t kmin, kmax, k;
+ out[i] = 0;
+ /* find overlap */
+ kmin = (i >= filter_size - 1) ? i - (filter_size - 1) : 0;
+ kmax = (i < signal_size - 1) ? i : signal_size - 1;
+
+ /* Add the overlaping values */
+ for (k = kmin; k <= kmax; k++) {
+ out[i] += signal[k] * filter[i - k];
+ }
+ }
+}
+
+void
+low_pass_filter(float* signal, int length, float cutoff, float resonance, float* out) {
+ float c = 1.0f / tanf(M_PI * cutoff); // calculate filter constant
+ float a1 = 1.0f / (1.0f + resonance * c + c * c); // calculate filter coefficients
+ float a2 = 2.0f * a1;
+ float a3 = a1;
+ float b1 = 2.0f * (1.0f - c * c) * a1;
+ float b2 = (1.0f - resonance * c + c * c) * a1;
+ float prev_input = 0.0f, prev_output = 0.0f; // initialize previous input and output to zero
+ for (int i = 0; i < length; i++) {
+ float input = signal[i];
+ float output = a1 * input + a2 * prev_input + a3 * prev_output - (i >= 1 ? b1 * out[i-1] : 0.0f) - (i >= 2 ? b2 * out[i-2] : 0.0f);
+ out[i] = output;
+ prev_input = input;
+ prev_output = output;
+ }
+}
+
+
+float
+make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, int viz)
+{
+ synth_t *synth = (synth_t*)synthData;
+ float sample = 0;
+
+
+ int n = 1;
+ if (1 /* !synth->filter */) {
+ for (int i = 0; i < n; i++) {
+ sample += (1.0 / n) * synth->gen[synth->geni](synth->n.freq + synth->freq_offset, phase, synth->x, sample_rate);
+ }
+
+ if (!viz && synth->filter) {
+ // ALLL THE FILTERS
+ LowPass_Update(synth->resonance, (adsr_amplitude(synth, synth->adsr.elapsed) + 0.1) * synth->cutoff + 1, sample_rate);
+ sample = LowPass_Filter(sample);
+
+ update_bw_low_pass_filter(synth->fff, SAMPLE_RATE, (adsr_amplitude(synth, synth->adsr.elapsed) + 0.1) * synth->cutoff, synth->resonance);
+ sample = bw_low_pass(synth->fff, sample);
+ }
+
+ sample = synth->gain * adsr_amplitude(synth, synth->adsr.elapsed) *
+ sample; //synth->gen[synth->geni](synth->n.freq + synth->freq_offset, phase, synth->x);
+
+ if (synth->clamp && sample >= 1) sample = 0.99;
+ if (synth->clamp && sample <= -1) sample = -0.99;
+ } else {
+ // get sample array s[]
+ int samples = 30;
+ float s[samples];
+
+ if (synth->adsr.elapsed < samples) {
+ for (int i = 0; i < samples; i++) {
+ s[i] = synth->gain * adsr_amplitude(synth, i) * synth->gen[synth->geni](synth->n.freq + synth->freq_offset, i, synth->x, sample_rate);
+ }
+ } else {
+ for (int i = 0; i < samples; i++) {
+ s[i] = synth->gain * adsr_amplitude(synth, synth->adsr.elapsed - 50 + i) * synth->gen[synth->geni](synth->n.freq + synth->freq_offset, phase - 50 + i, synth->x, sample_rate);
+ }
+ }
+
+ // process s[]
+
+
+ // return s[50]
+ }
+
+ return sample;
+}
+
+int
+sound_gen(const void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *synthData)
+{
+ synth_t *synth = (synth_t*)synthData;
+ float *out = (float*)outputBuffer;
+
+ (void) timeInfo; /* Prevent unused variable warnings. */
+ (void) statusFlags;
+ (void) inputBuffer;
+
+ if (1) {
+ float s;
+ for( unsigned long i=0; i<framesPerBuffer; i++ ) {
+ //get_portaudio_frame(outputBuffer, synth);
+ s = make_sample(synth->n.elapsed, synth, SAMPLE_RATE, 0);
+ *out++ = s;
+ *out++ = s;
+ synth->adsr.elapsed++;
+ synth->n.elapsed++;
+ if (!synth->multi) {
+ if (synth->n.elapsed >= (1.0 / synth->n.freq) * SAMPLE_RATE) synth->n.elapsed = 0;
+ } else {
+
+ }
+ }
+ } else {
+ float s[FRAMES_PER_BUFFER * 5];
+
+ if (synth->adsr.elapsed < framesPerBuffer * 5) {
+ for (unsigned long long i = 0; i < framesPerBuffer * 5; i++) {
+ s[i] = make_sample(i, synth, SAMPLE_RATE, 0);
+ synth->adsr.elapsed++;
+ synth->n.elapsed++;
+ if (!synth->multi) {
+ if (synth->n.elapsed >= (1.0 / synth->n.freq) * SAMPLE_RATE) synth->n.elapsed = 0;
+ } else {
+ }
+ }
+ } else {
+ for (unsigned long long i = 0; i < framesPerBuffer * 5; i++) {
+ s[i] = make_sample(synth->n.elapsed - framesPerBuffer * 2 + i, synth, SAMPLE_RATE, 0);
+ synth->adsr.elapsed++;
+ synth->n.elapsed++;
+ if (!synth->multi) {
+ if (synth->n.elapsed >= (1.0 / synth->n.freq) * SAMPLE_RATE) synth->n.elapsed = 0;
+ } else {
+ }
+ }
+ }
+
+ // filter
+
+ // output
+
+ if (synth->adsr.elapsed < framesPerBuffer * 5) {
+ for( unsigned long i=0; i<framesPerBuffer; i++ ) {
+ *out++ = s[i];
+ *out++ = s[i];
+ }
+ } else {
+ for( unsigned long i=0; i<framesPerBuffer; i++ ) {
+ *out++ = s[i + (2 * framesPerBuffer)];
+ *out++ = s[i + (2 * framesPerBuffer)];
+ }
+ }
+ synth->adsr.elapsed-= 4*framesPerBuffer;
+
+ if (!synth->multi) {
+ for (int i = 0; i < framesPerBuffer * 4; i++) {
+ synth->n.elapsed--;
+ if (synth->n.elapsed == 0) synth->n.elapsed = (int)(SAMPLE_RATE / synth->n.freq);
+ }
+ } else {
+ synth->n.elapsed-= 4*framesPerBuffer;
+ }
+ }
+
+ return paContinue;
+}
+
+void
+init_synth(synth_t * synth)
+{
+ synth->freq_offset = 0;
+ synth->gain = 1;
+ synth->x = 1;
+
+ synth->n.freq = 0;
+ synth->n.noteOn = 0;
+ synth->n.noteOff = 1;
+ synth->n.key = 0;
+ synth->n.elapsed = 0;
+
+
+ synth->adsr.a = 0.001;
+ synth->adsr.d = 0.3;
+ synth->adsr.s = 0.7;
+ synth->adsr.r = 0.4;
+ synth->adsr.elapsed = 0;
+
+ synth->octave = 3;
+
+ synth->multi = 0;
+ synth->filter = 0;
+ synth->cutoff = 22000.0f;
+ synth->resonance = 1.0f;
+ synth->clamp = 0;
+
+ synth->gen[0] = gen0;
+ synth->gen[1] = gen1;
+ synth->gen[2] = gen2;
+ synth->gen[3] = gen3;
+ synth->geni = 0;
+
+ synth->viz.sample_rate_divider = 1;
+
+ LowPass_Init();
+ synth->fff = create_bw_low_pass_filter(2, SAMPLE_RATE, 400);
+}
diff --git a/src/synth_engine.h b/src/synth_engine.h
new file mode 100644
index 0000000..e35e8fb
--- /dev/null
+++ b/src/synth_engine.h
@@ -0,0 +1,77 @@
+#ifndef SYNTH_ENGINE_H
+#define SYNTH_ENGINE_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <portaudio.h>
+
+#include "notes.h"
+#include "filter.h"
+
+#define SAMPLE_RATE (44100)
+//#define SAMPLE_RATE (48000)
+#define FRAMES_PER_BUFFER (210)
+
+#define WIDTH 1024
+#define HEIGHT 600
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+
+typedef struct {
+ float a;
+ float d;
+ float s;
+ float r;
+ unsigned long long elapsed;
+} adsr_t;
+
+typedef struct {
+ float freq;
+ PaTime noteOn;
+ PaTime noteOff;
+ int key;
+ unsigned long long elapsed;
+} note_t;
+
+typedef struct {
+ int sample_rate_divider;
+} viz_t;
+
+typedef struct {
+ float freq_offset;
+ float gain;
+ float x;
+
+ note_t n;
+ adsr_t adsr;
+
+ int octave;
+
+ int multi;
+ int filter;
+ float cutoff;
+ float resonance;
+ int clamp;
+
+ float (*gen[4]) (float freq, unsigned long long phase, float x, unsigned int sample_rate);
+ int geni;
+
+ BWLowPass* fff;
+
+ viz_t viz;
+} synth_t;
+
+float adsr_amplitude(void *synthData, unsigned long long elapsed);
+float make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, int viz);
+void init_synth(synth_t * synth);
+int sound_gen(const void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *synthData);
+
+#endif /* SYNTH_ENGINE_H */
diff --git a/src/synth_gui.c b/src/synth_gui.c
new file mode 100644
index 0000000..3cfde6f
--- /dev/null
+++ b/src/synth_gui.c
@@ -0,0 +1,173 @@
+#include "synth_gui.h"
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+//#include "raylib.h"
+
+
+void
+set_note(void *synthData, float note, PaTime time, int key)
+{
+ synth_t * synth = (synth_t *)synthData;
+
+ synth->n.key = key;
+ synth->n.freq = note;
+ synth->n.noteOn = time;
+ synth->n.noteOff = 0;
+ synth->n.elapsed = 0;
+ synth->adsr.elapsed = 0;
+}
+
+
+void
+keyboard(void *synthData, PaStream *stream)
+{
+ synth_t * synth = (synth_t *)synthData;
+
+ int keys[] = {KEY_Q, KEY_TWO, KEY_W, KEY_THREE, KEY_E, KEY_R, KEY_FIVE, KEY_T, KEY_SIX, KEY_Y, KEY_SEVEN, KEY_U, KEY_I, KEY_NINE, KEY_O, KEY_ZERO, KEY_P, KEY_LEFT_BRACKET, KEY_EQUAL, KEY_RIGHT_BRACKET, 0};
+
+ float note;
+
+ for (int i = 0; keys[i]; i++) {
+ if (IsKeyPressed(keys[i])) {
+ note = notes[i % 12][(synth->octave + (i / 12)) % 8];
+ if (note) {
+ set_note(synth, note, Pa_GetStreamTime(stream), keys[i]);
+ printf("Note On : %s[%d]\n", int_to_note(i % 12), (synth->octave + (i / 12)) % 8);
+ }
+ }
+ }
+ for (int i = 0; keys[i]; i++) {
+ if (IsKeyReleased(keys[i])) {
+ if (synth->n.key == keys[i]) {
+ synth->n.noteOff = Pa_GetStreamTime(stream);
+ printf("Note Off: %s[%d]\n", int_to_note(i % 12), (synth->octave + (i / 12)) % 8);
+ }
+ }
+ }
+}
+
+void
+draw_adsr_sliders(synth_t * synth, int x, int y, int width, int height, int offset)
+{
+ char buf[64];
+ int count = 0;
+
+ snprintf(buf, sizeof buf, "%.3f", synth->adsr.a);
+ synth->adsr.a = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "A: ", buf, synth->adsr.a , 0.001f, 2.0f);
+ snprintf(buf, sizeof buf, "%.3f", synth->adsr.d);
+ synth->adsr.d = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "D: ", buf, synth->adsr.d , 0.001f, 2.0f);
+ snprintf(buf, sizeof buf, "%.3f", synth->adsr.s);
+ synth->adsr.s = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "S: ", buf, synth->adsr.s , 0.0f, 1.0f);
+ snprintf(buf, sizeof buf, "%.3f", synth->adsr.r);
+ synth->adsr.r = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "R: ", buf, synth->adsr.r , -0.001f, 3.0f);
+ snprintf(buf, sizeof buf, "%.3f", synth->cutoff);
+ synth->cutoff = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "fC: ", buf, synth->cutoff , 0.0f, 11000.0f);
+ snprintf(buf, sizeof buf, "%.3f", synth->resonance);
+ synth->resonance = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "fR: ", buf, synth->resonance , 0.001f, 5.0f);
+}
+
+void
+draw_signals(synth_t * synth, int x, int y, int width, int height)
+{
+ DrawRectangleLines(x, y, width, height, BLACK);
+
+ int point_radius = 1.5;
+
+ GuiSpinner((Rectangle){ x+ 100, y - 24 - 6, 100, 24 }, "rate divider: ", &(synth->viz.sample_rate_divider), 1, 10, 0);
+ int period = (1 / (synth->n.freq + 1)) * SAMPLE_RATE / synth->viz.sample_rate_divider;
+
+ if (synth->multi) {
+ for (int i = x; i < WIDTH - x; i++) {
+ DrawCircle(i , y + height / 2 + floor(50 * make_sample(i - x, synth, SAMPLE_RATE / synth->viz.sample_rate_divider, 1)), point_radius, RED);
+ }
+ } else {
+ for (int i = x; i < WIDTH - x; i++) {
+ //DrawCircle((WIDTH - (1 / (synth->n.freq + 1)) * SAMPLE_RATE) / 2 + i , (HEIGHT / 2) + floor(50 * make_sample(i % period, synth, SAMPLE_RATE)), point_radius, RED);
+ DrawCircle(i , y + height / 2 + floor(50 * make_sample((i - x) % period, synth, SAMPLE_RATE / synth->viz.sample_rate_divider, 1)), point_radius, RED);
+ }
+ }
+
+ for (int i = x; i < WIDTH - x; i++) {
+ DrawCircle(i , y + height - 1+ floor((WIDTH / 24) * - adsr_amplitude(synth, synth->adsr.elapsed)), point_radius, GREEN);
+ }
+ float adsr_duration = synth->adsr.a + synth->adsr.d + (synth->adsr.a + synth->adsr.d + synth->adsr.r) / 3.0 + synth->adsr.r;
+ for (int i = x; i < WIDTH - x; i++) {
+ DrawCircle(i , y + height - 1 + floor((WIDTH / 24) * - adsr_amplitude(synth, (i - x) * (adsr_duration * SAMPLE_RATE) / WIDTH)), point_radius, BLUE);
+ }
+}
+
+void
+rayrun(void *synthData, PaStream *stream)
+{
+ synth_t * synth = (synth_t *)synthData;
+ PaTime current_time = 0;
+ PaTime prev_time = 0;
+
+ InitWindow(WIDTH, HEIGHT, "Raylib synth");
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ while (!WindowShouldClose()) {
+
+ keyboard(synth, stream);
+ int prec = 100000;
+
+ if (IsKeyPressed(KEY_ENTER)) {
+ synth->multi = !synth->multi;
+ }
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+ ClearBackground(RAYWHITE);
+
+ // GUI
+ char buf[64];
+
+ draw_adsr_sliders(synth, 30, 20, 256, 20, 4);
+
+
+ if ( GuiButton((Rectangle){ WIDTH / 2 - 108, 150 - 42 - 6 - 6, 216, 6 }, "")) {
+ synth->freq_offset = 0;
+ }
+ snprintf(buf, sizeof buf, "%.1f", synth->freq_offset);
+ synth->freq_offset = GuiSliderBar((Rectangle){ WIDTH / 2 - 108, 150 - 42, 216, 24 }, "fine", buf, synth->freq_offset , -20.0f, 20.0f);
+
+ if ( GuiButton((Rectangle){ WIDTH / 2 - 108, 150 - 6 - 6, 216, 6 }, "")) {
+ synth->gain = 1;
+ }
+ snprintf(buf, sizeof buf, "%.1f", synth->gain);
+ synth->gain = GuiSliderBar((Rectangle){ WIDTH / 2 - 108, 150, 216, 24 }, "gain", buf, synth->gain , 0.0f, 2.0f);
+
+ if ( GuiButton((Rectangle){ WIDTH / 2 - 108, 150 - 6 - 6 + 42, 216, 6 }, "")) {
+ synth->x = 1;
+ }
+ snprintf(buf, sizeof buf, "%.1f", synth->x);
+ synth->x = GuiSliderBar((Rectangle){ WIDTH / 2 - 108, 150 + 42, 216, 24 }, "x", buf, synth->x , 0.0f, 2.0f);
+
+ synth->multi = GuiToggle((Rectangle){ WIDTH - 100 - 50 - 100 - 50 , 50, 100, 24 }, "!MULTI!", synth->multi);
+ synth->filter = GuiToggle((Rectangle){ WIDTH - 100 - 50 - 100 - 50 , 50 + 24 + 6, 100, 24 }, "FILTER", synth->filter);
+
+ GuiSpinner((Rectangle){ WIDTH - 100 - 50 , 50, 100, 24 }, "oct: ", &(synth->octave), 0, 7, 0);
+ snprintf(buf, sizeof buf, "generator %d --> ", synth->geni);
+ GuiSpinner((Rectangle){ WIDTH - 100 - 50 , 50 + 24 + 6, 100, 24 }, buf, &(synth->geni), 0, 3, 0);
+
+ synth->clamp = GuiToggle((Rectangle){ WIDTH - 100 - 50, 50 + 24 + 6 + 24 + 6, 100, 24 }, "clamp", synth->clamp);
+
+
+ // signals
+ draw_signals(synth, 20, 390, WIDTH - 2*20, 200);
+
+ DrawText("THE SYNTH!!!!!!!!!!!!!!!!!!1", WIDTH / 2 - 100, 50, 20, LIGHTGRAY);
+ DrawText("KEYBOARD: Q .. ] TOGGLE MULTI: ENTER", WIDTH / 2 -300, HEIGHT - 20 - 50, 20, LIGHTGRAY);
+ snprintf(buf, sizeof buf, "time?: %lld adsr: %lld note: %lld", synth->adsr.elapsed / SAMPLE_RATE, synth->adsr.elapsed, synth->n.elapsed);
+ DrawText(buf, WIDTH / 2 -300, HEIGHT - 40 - 50, 20, LIGHTGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+
+ current_time = Pa_GetStreamTime(stream);
+ //printf("%f :: %ld\n", current_time - prev_time, phase);
+
+ prev_time = current_time;
+ }
+ CloseWindow();
+}
diff --git a/src/synth_gui.h b/src/synth_gui.h
new file mode 100644
index 0000000..73bdbdf
--- /dev/null
+++ b/src/synth_gui.h
@@ -0,0 +1,10 @@
+#ifndef SYNTH_GUI_H
+#define SYNTH_GUI_H
+
+#include <portaudio.h>
+
+#include "synth_engine.h"
+
+void rayrun(void *synthData, PaStream *stream);
+
+#endif /* SYNTH_GUI_H */
diff --git a/src/synth_math.h b/src/synth_math.h
new file mode 100644
index 0000000..de6fb26
--- /dev/null
+++ b/src/synth_math.h
@@ -0,0 +1,22 @@
+#ifndef SYNTH_MATH_H
+#define SYNTH_MATH_H
+
+extern long long
+gcd(long long int a, long long int b)
+{
+ long long rem;
+ rem=a-(a/b*b);
+ if(rem==0)
+ return b;
+ else
+ gcd(b,rem);
+}
+
+// Function to return LCM of two numbers
+extern long long
+lcm(long long a, long long b)
+{
+ return (a / gcd(a, b)) * b;
+}
+
+#endif /* SYNTH_MATH_H */