// https://dataswamp.org/~incal/bad-el/src/bad-sdl3/bad-sdl3.c int plugin_is_GPL_compatible; #include #include "bad-sdl3.h" #include #include #include #include #include #include #include #include #include #include #define nas __attribute__((unused)) #define lines 32 #define tri 3 #define unset_int -1 #define unset32 UINT32_MAX typedef struct { SDL_Mutex *mtx; char *str; SDL_Surface *sur; size_t new; size_t old; } LineMeta; static SDL_FRect gsr; static SDL_Mutex *cube_mtx = NULL; static SDL_Renderer *rnd = NULL; static SDL_Texture *ptex = NULL; static SDL_Texture *tex = NULL; static SDL_Window *win = NULL; static bool always_draw_mode = true; static bool draw_grid_mode = true; static bool draw_light_mode = true; static int res_w = unset_int; static int res_h = unset_int; static int rnd_w = unset_int; static int rnd_h = unset_int; // --- // log // --- static bool log_enable = true; void logi (const char *s) { if (log_enable) { const SDL_ThreadID id = SDL_GetThreadID (NULL); printf ("[ thread %ld ] %s\n", id, s); } } // ---- // font // ---- static SDL_Color fg = { 112, 196, 255, 255 }; static TTF_Font *fnt = NULL; static int fnt_sze = 16; static int fnt_w = unset_int; static int fnt_h = unset_int; void font_init () { // SDL3 module if (!TTF_Init()) logi ("DNC TTF_Init"); // font fnt = TTF_OpenFont ("/usr/share/fonts/truetype/ocr-a/OCRABold.ttf", fnt_sze); if (!fnt) logi ("DNC TTF_OpenFont"); // size bool manual = true; if (fnt_w == unset_int) fnt_w = 11; if (fnt_h == unset_int) fnt_h = 21; if (!manual) printf ("[char] %d/%d = %.2f\n", fnt_w, fnt_h, (float)fnt_w / (float)fnt_h); } void font_quit () { if (fnt) { TTF_CloseFont (fnt); fnt = NULL; } TTF_Quit(); } // ----- // cache // ----- #define cube_side 256 #define cube_size 16777216 static SDL_Texture *cube[cube_size]; static int collisions = 0; static int surfaces_created = 0; static int surfaces_destroyed = 0; static int textures_created = 0; static int textures_destroyed = 0; static int drawn = 0; static int not_drawn = 0; static int timeouts = 0; static int cube_call = 0; static int cube_insert = 0; void cube_init () { cube_mtx = SDL_CreateMutex(); SDL_UnlockMutex (cube_mtx); } void cube_erase () { SDL_LockMutex (cube_mtx); for (uint32_t i = 0; i < cube_size; i++) { if (cube[i]) { SDL_DestroyTexture (cube[i]); textures_destroyed++; cube[i] = NULL; } } SDL_UnlockMutex (cube_mtx); } void cube_quit () { cube_erase(); SDL_DestroyMutex (cube_mtx); cube_mtx = NULL; } // ------ // thread // ------ static LineMeta lines_pipe [lines]; static bool thr_run = false; static SDL_Thread *thr_sur = NULL; static SDL_Thread *thr_rus = NULL; static SDL_Thread *thr_cac = NULL; int thr_surface (void *arg nas) { const SDL_ThreadID id = SDL_GetThreadID (NULL); static __thread int drawn_local = 0; uint32_t idx; uint8_t row; while (thr_run) { for (row = 0; row < lines; row++) { if (SDL_TryLockMutex (lines_pipe[row].mtx)) { if (lines_pipe[row].str) { idx = hash_idx (lines_pipe[row].str) % cube_size; lines_pipe[row].new = idx; if (cube_mtx) { SDL_LockMutex (cube_mtx); if (!cube[idx]) { if (!lines_pipe[row].sur) { lines_pipe[row].sur = TTF_RenderText_Blended (fnt, lines_pipe[row].str, strlen (lines_pipe[row].str), fg); surfaces_created++; drawn_local++; } } } SDL_UnlockMutex (cube_mtx); free (lines_pipe[row].str); lines_pipe[row].str = NULL; } SDL_UnlockMutex (lines_pipe[row].mtx); } } SDL_Delay (1); } if (0 < drawn_local) printf ("[ thread %ld ] I made %d surfaces\n", id, drawn_local); return 0; } int thr_cache (void *arg nas) { const SDL_ThreadID id = SDL_GetThreadID (NULL); const int max_all = 2048; int max_nxt = max_all; int clears = 0; while (thr_run) { if ((max_nxt < cube_insert)) { cube_erase(); clears++; max_nxt += max_all; } SDL_Delay (1024); } if (0 < clears) printf ("[ thread %ld ] I cleared the cache %d times\n", id, clears); return 0; } void thr_init () { thr_run = true; if (!thr_sur) thr_sur = SDL_CreateThread (thr_surface, "sdl3-surface", NULL); if (!thr_rus) thr_rus = SDL_CreateThread (thr_surface, "sdl3-rusface", NULL); if (!thr_cac) thr_cac = SDL_CreateThread (thr_cache, "sdl3-cache", NULL); } void thr_quit () { thr_run = false; if (thr_sur) { SDL_WaitThread (thr_sur, NULL); thr_sur = NULL; } if (thr_rus) { SDL_WaitThread (thr_rus, NULL); thr_rus = NULL; } if (thr_cac) { SDL_WaitThread (thr_cac, NULL); thr_cac = NULL; } } // ----------- // render text // ----------- uint32_t hash_idx (const char *str) { assert (str); uint8_t dig[SHA256_DIGEST_LENGTH]; SHA256 ((const unsigned char*)str, strlen (str), dig); return (uint32_t) dig[0] << 24 | (uint32_t) dig[1] << 16 | (uint32_t) dig[2] << 8 | (uint32_t) dig[3]; } void store_texture (int i) { assert (0 <= i); assert (i < lines); // lock SDL_LockMutex (cube_mtx); SDL_LockMutex (lines_pipe[i].mtx); if ((lines_pipe[i].new != unset32) && lines_pipe[i].sur) { // collision if (cube[lines_pipe[i].new]) { SDL_DestroyTexture (cube[lines_pipe[i].new]); textures_destroyed++; cube[lines_pipe[i].new] = NULL; collisions++; } // create texture cube[lines_pipe[i].new] = SDL_CreateTextureFromSurface (rnd, lines_pipe[i].sur); SDL_SetTextureBlendMode (cube[lines_pipe[i].new], SDL_BLENDMODE_BLEND); textures_created++; cube_insert++; // destroy surface SDL_DestroySurface (lines_pipe[i].sur); lines_pipe[i].sur = NULL; surfaces_destroyed++; } // unlock SDL_UnlockMutex (lines_pipe[i].mtx); SDL_UnlockMutex (cube_mtx); } void draw_texture (int i) { assert (0 <= i); assert (i < lines); float tw, th; float nw, nh; int y = fnt_h *i; SDL_GetTextureSize (tex, &tw, &th); SDL_LockMutex (cube_mtx); SDL_LockMutex (lines_pipe[i].mtx); SDL_GetTextureSize (cube[lines_pipe[i].new], &nw, &nh); SDL_UnlockMutex (lines_pipe[i].mtx); SDL_UnlockMutex (cube_mtx); const SDL_FRect sr = {0, y, round (tw), round (nh) }; const SDL_FRect nr = {0, y, round (nw), round (nh) }; SDL_SetRenderTarget (rnd, tex); SDL_RenderFillRect (rnd, &sr); SDL_LockMutex (cube_mtx); SDL_RenderTexture (rnd, cube[lines_pipe[i].new], NULL, &nr); SDL_UnlockMutex (cube_mtx); } void draw_string (const char *str) { int enqd = 0; bool draw = false; char *buff = my_strdup (str); char *save = NULL; char *lne = strtok_r (buff, "\n", &save); for (int i = 0; (i < lines) && lne; i++) { SDL_LockMutex (lines_pipe[i].mtx); if (lines_pipe[i].str) { free (lines_pipe[i].str); lines_pipe[i].str = NULL; } else if (lne) lines_pipe[i].str = my_strdup (lne); SDL_UnlockMutex (lines_pipe[i].mtx); enqd++; cube_call++; lne = strtok_r (NULL, "\n", &save); } // wait for surfaces or refs to cube textures int total = 0; int total_max = 64 * lines; for (int i = 0; ((0 < enqd) && (i < lines) && (total < total_max)); i = (total++) % lines) { if (SDL_TryLockMutex (lines_pipe[i].mtx)) { if (lines_pipe[i].new != unset32) { if (lines_pipe[i].sur) store_texture (i); if (always_draw_mode || (lines_pipe[i].old == unset32) || (lines_pipe[i].old != lines_pipe[i].new)) { draw = true; draw_texture (i); } lines_pipe[i].old = lines_pipe[i].new; lines_pipe[i].new = unset32; enqd--; } SDL_UnlockMutex (lines_pipe[i].mtx); } if (i * 2 == 0) SDL_Delay (1); } if (total_max <= total) timeouts++; if (buff) { free (buff); buff = NULL; } if (draw) drawn++; else not_drawn++; } void draw_screen () { assert (rnd); assert (tex); SDL_SetRenderTarget (rnd, NULL); SDL_RenderTexture (rnd, tex, NULL, &gsr); SDL_RenderPresent (rnd); } // -------------------- // Emacs dynamic module // -------------------- int emacs_module_init (struct emacs_runtime *rnt) { // check for incompatible Emacs binary if (rnt->size < (long int) sizeof (*rnt)) { return 1; } // check for incompatible module API emacs_env *env = rnt->get_environment (rnt); if (env->size < (long int) sizeof (*env)) { return 2; } // check for too old Emacs int nas emacs_ver; if ((long int) sizeof (struct emacs_env_31) <= env->size) { emacs_ver = 31; } else if ((long int) sizeof (struct emacs_env_30) <= env->size) { emacs_ver = 30; } else if ((long int) sizeof (struct emacs_env_29) <= env->size) { emacs_ver = 29; } else { return 3; } // draw_init int nargs = 0; emacs_value draw_init_func = env->make_function (env, nargs, nargs, draw_init, "Init draw.", NULL); emacs_value draw_init_symb = env->intern (env, "draw_init"); emacs_value draw_init_args[] = { draw_init_symb, draw_init_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_init_args); // draw_quit nargs = 0; emacs_value draw_quit_func = env->make_function (env, nargs, nargs, draw_quit, "Quit draw.", NULL); emacs_value draw_quit_symb = env->intern (env, "draw_quit"); emacs_value draw_quit_args[] = { draw_quit_symb, draw_quit_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_quit_args); // draw_frame nargs = 0; emacs_value draw_frame_func = env->make_function (env, nargs, nargs, draw_frame, "Draw frame.", NULL); emacs_value draw_frame_symb = env->intern (env, "draw_frame"); emacs_value draw_frame_args[] = { draw_frame_symb, draw_frame_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_frame_args); // clear_frame nargs = 0; emacs_value clear_frame_func = env->make_function (env, nargs, nargs, clear_frame, "Clear frame.", NULL); emacs_value clear_frame_symb = env->intern (env, "clear_frame"); emacs_value clear_frame_args[] = { clear_frame_symb, clear_frame_func }; env->funcall (env, env->intern (env, "defalias"), 2, clear_frame_args); // sdl_draw_circle nargs = 6; emacs_value sdl_draw_circle_func = env->make_function (env, nargs, nargs, sdl_draw_circle, "Draw planet.", NULL); emacs_value sdl_draw_circle_symb = env->intern (env, "sdl_draw_circle"); emacs_value sdl_draw_circle_args[] = { sdl_draw_circle_symb, sdl_draw_circle_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_circle_args); // sdl_draw_triangle nargs = 18; emacs_value sdl_draw_triangle_func = env->make_function (env, nargs, nargs, sdl_draw_triangle, "SDL draw triangle.", NULL); emacs_value sdl_draw_triangle_symb = env->intern (env, "sdl_draw_triangle"); emacs_value sdl_draw_triangle_args[] = { sdl_draw_triangle_symb, sdl_draw_triangle_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_triangle_args); // draw_all_toggle (a.k.a. dirty rects, this works bad for 60 FPS) nargs = 0; emacs_value draw_all_toggle_func = env->make_function (env, nargs, nargs, draw_all_toggle, "Toggle draw-all mode (disable 'dirty rects' optimization).", NULL); emacs_value draw_all_toggle_symb = env->intern (env, "draw_all_toggle"); emacs_value draw_all_toggle_args[] = { draw_all_toggle_symb, draw_all_toggle_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_all_toggle_args); // draw_grid_toggle nargs = 0; emacs_value draw_grid_toggle_func = env->make_function (env, nargs, nargs, draw_grid_toggle, "Toggle draw-grid mode.", NULL); emacs_value draw_grid_toggle_symb = env->intern (env, "draw_grid_toggle"); emacs_value draw_grid_toggle_args[] = { draw_grid_toggle_symb, draw_grid_toggle_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_grid_toggle_args); // sdl_webp nargs = 0; emacs_value sdl_webp_func = env->make_function (env, nargs, nargs, sdl_webp, "Load a webp.", NULL); emacs_value sdl_webp_symb = env->intern (env, "sdl_webp"); emacs_value sdl_webp_args[] = { sdl_webp_symb, sdl_webp_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_webp_args); // done return 0; } emacs_value draw_init (emacs_env* env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { video_init (); circle_init (); webp_init (); font_init (); cube_init (); thr_init (); // mutexes for (int i = 0; i < lines; i++) { lines_pipe[i].mtx = SDL_CreateMutex(); lines_pipe[i].str = NULL; lines_pipe[i].sur = NULL; lines_pipe[i].new = unset32; lines_pipe[i].old = unset32; } // random unsigned ut = (unsigned) time (NULL); srand (ut); // done return env->intern (env, "nil"); } void mutex_quit () { for (int i = 0; i < lines; i++) { if (lines_pipe[i].mtx) { SDL_DestroyMutex (lines_pipe[i].mtx); lines_pipe[i].mtx = NULL; } } } emacs_value draw_quit (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { assert (env); thr_quit (); cube_quit (); font_quit (); circle_quit (); webp_quit (); video_quit (); mutex_quit (); SDL_Quit (); // stats int tot = drawn + not_drawn; if (0 < textures_created) { printf ("%8d textures created\n", textures_created); printf ("%8d -------- destroyed\n", textures_destroyed); } if (0 < surfaces_created) { printf ("%8d surfaces created (%.1f%% of worst-case %d)\n", surfaces_created, (100.0f * (surfaces_created / (1.0f + cube_call))), cube_call); printf ("%8d -------- destroyed\n", surfaces_destroyed); printf ("%8d -------- collisions\n", collisions); } if (0 < tot) { printf ("%8d frames\n", tot); printf ("%8d -------- skipped as identical (%.1f%%)\n", not_drawn, 100.0f * (float) not_drawn / (float) tot); printf ("%8d -------- not drawn as timeout (%.1f%%)\n", timeouts, 100.0f * (float) timeouts / (float) tot); } // done return env->intern (env, "nil"); } // ----- // video // ----- static SDL_DisplayID *display = NULL; void video_init () { SDL_Init (SDL_INIT_VIDEO); // window int num_displays = 1; display = SDL_GetDisplays (&num_displays); const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode (*display); if (dm) { res_w = dm->w; res_h = dm->h; } else { logi ("DNC SDL_GetCurrentDisplayMode, using fallback values"); res_w = 1280; res_h = 720; } SDL_WindowFlags win_flags = SDL_WINDOW_FULLSCREEN; win = SDL_CreateWindow ("SDL3 draw", res_w, res_h, win_flags); // renderer rnd = SDL_CreateRenderer (win, NULL); SDL_SetRenderDrawBlendMode (rnd, SDL_BLENDMODE_BLEND); SDL_GetCurrentRenderOutputSize (rnd, &rnd_w, &rnd_h); SDL_SetRenderLogicalPresentation (rnd, rnd_w, rnd_h, SDL_LOGICAL_PRESENTATION_LETTERBOX); // tex tex = SDL_CreateTexture (rnd, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, rnd_w, rnd_h); SDL_SetTextureBlendMode (tex, SDL_BLENDMODE_BLEND); textures_created++; // rect gsr.x = 0; gsr.y = 0; gsr.w = rnd_w; gsr.h = rnd_h; // done clear_screen(); } void video_quit () { if (ptex) { SDL_DestroyTexture (ptex); ptex = NULL; textures_destroyed++; } if (tex) { SDL_DestroyTexture (tex); tex = NULL; textures_destroyed++; } if (rnd) { SDL_DestroyRenderer (rnd); rnd = NULL; } if (win) { SDL_DestroyWindow (win); win = NULL; } if (display) { SDL_free (display); display = NULL; } } // ---- // draw // ---- void clear_screen () { SDL_SetRenderDrawColor (rnd, 0, 0, 0, 255); // bg color SDL_SetRenderTarget (rnd, tex); SDL_RenderClear (rnd); } emacs_value clear_frame (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { assert (env); clear_screen(); return env->intern (env, "nil"); } emacs_value draw_frame (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { assert (env); emacs_value ef = env->intern (env, "bad-grid-string"); // Elisp emacs_value es = env->funcall (env, ef, 0, NULL); ptrdiff_t l = 0; env->copy_string_contents (env, es, NULL, &l); if (0 < l) { char *txt_str = NULL; txt_str = malloc (l); env->copy_string_contents (env, es, txt_str, &l); if (txt_str) { draw_string (txt_str); free (txt_str); txt_str = NULL; } } if (draw_light_mode) draw_light (0, 0, 16); draw_screen (); return env->intern (env, "nil"); } emacs_value sdl_draw_triangle (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data nas) { assert (env); assert (nargs == 18); // coords float x0 = env->extract_float (env, args [0]); float y0 = env->extract_float (env, args [1]); float z0 = env->extract_float (env, args [2]); float x1 = env->extract_float (env, args [3]); float y1 = env->extract_float (env, args [4]); float z1 = env->extract_float (env, args [5]); float x2 = env->extract_float (env, args [6]); float y2 = env->extract_float (env, args [7]); float z2 = env->extract_float (env, args [8]); // color float r0 = env->extract_float (env, args [ 9]); float g0 = env->extract_float (env, args [10]); float b0 = env->extract_float (env, args [11]); float a0 = 0.93f; float r1 = env->extract_float (env, args [12]); float g1 = env->extract_float (env, args [14]); float b1 = env->extract_float (env, args [14]); float a1 = 0.86f; float r2 = env->extract_float (env, args [15]); float g2 = env->extract_float (env, args [12]); float b2 = env->extract_float (env, args [17]); float a2 = 0.79f; // re-create matrix as vectors and colors vector v0 = { .x = x0, .y = y0, .z = z0 }; vector v1 = { .x = x1, .y = y1, .z = z1 }; vector v2 = { .x = x2, .y = y2, .z = z2 }; SDL_FColor c0 = { .r = r0, .g = g0, .b = b0, .a = a0 }; SDL_FColor c1 = { .r = r1, .g = g1, .b = b1, .a = a1 }; SDL_FColor c2 = { .r = r2, .g = g2, .b = b2, .a = a2 }; // verify coords assert (0 <= fabs (v0.x)); assert (0 <= fabs (v0.y)); assert (0 <= fabs (v0.z)); assert (0 <= fabs (v1.x)); assert (0 <= fabs (v1.y)); assert (0 <= fabs (v1.z)); assert (0 <= fabs (v2.x)); assert (0 <= fabs (v2.y)); assert (0 <= fabs (v2.z)); // verify color assert (0.0f <= c0.r); assert (0.0f <= c0.g); assert (0.0f <= c0.b); assert (0.0f <= c0.a); assert (0.0f <= c1.r); assert (0.0f <= c1.g); assert (0.0f <= c1.b); assert (0.0f <= c1.a); assert (0.0f <= c2.r); assert (0.0f <= c2.g); assert (0.0f <= c2.b); assert (0.0f <= c2.a); // draw draw_triangle (v0, v1, v2, c0, c1, c2); return env->intern (env, "nil"); } emacs_value sdl_draw_circle (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data nas) { assert (env); assert (nargs == 6); int x = env->extract_integer (env, args[0]); int y = env->extract_integer (env, args[1]); int radius = env->extract_integer (env, args[2]); float red = env->extract_float (env, args[3]); float green = env->extract_float (env, args[4]); float blue = env->extract_float (env, args[5]); SDL_FColor col = { red, green, blue, 1.0f }; draw_circle (x, y, radius, col); return env->intern (env, "nil"); } // ----- // modes // ----- emacs_value draw_all_toggle (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { assert (env); clear_screen(); always_draw_mode = !always_draw_mode; return env->intern (env, "nil"); } emacs_value draw_grid_toggle (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { assert (env); clear_screen(); draw_grid_mode = !draw_grid_mode; return env->intern (env, "nil"); } // --------- // draw rect // --------- void draw_rect (int x, int y, int w, int h) { const SDL_FRect rect = { .x = x, .y = y, .w = w, .h = h }; SDL_SetRenderDrawColor (rnd, 128, 128, 255, 200); SDL_SetRenderTarget (rnd, tex); SDL_RenderFillRect (rnd, &rect); } void draw_rect_test () { // max size int max_w = unset_int; int max_h = unset_int; SDL_GetCurrentRenderOutputSize (rnd, &max_w, &max_h); if ((max_w != unset_int) && (max_h != unset_int)) { // margin const float part = 1.0f; const float full = 32.0f; int mar_x = (round (max_w * part / full)); int mar_y = (round (max_h * 4.0f * part / full)); // rec int w = max_w - (2 * mar_x); int h = fnt_h * 3; int x = mar_x; int y = max_h - mar_y - h; // done draw_rect (x, y, w, h); } } // ----------------------------------------------------------------------------- // draw light // ----------------------------------------------------------------------------- void draw_light (int cx, int cy, int radius) { int d = 2 * radius; int w = d; int h = d; if (!ptex) { ptex = SDL_CreateTexture (rnd, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, w, h); textures_created++; int pitch = 0; void *pixels = NULL; SDL_LockTexture (ptex, NULL, &pixels, &pitch); int stride = pitch / sizeof (uint32_t); uint32_t *pixel_array = (uint32_t*)pixels; for (int y = cy; y < h; y++) { for (int x = cx; x < w; x++) { float dx = x - radius; float dy = y - radius; float dist = sqrt (dx*dx + dy*dy); float bright = fmax (0.0f, 1.0f - dist/radius); float curve = powf (bright, 1.5f); uint8_t r = (uint8_t)(128 * curve); uint8_t g = (uint8_t)( 96 * curve); uint8_t b = (uint8_t)(255 * curve); uint8_t a = (uint8_t)(212 * curve); pixel_array[y * stride + x] = (a << 24) | (b << 16) | (g << 8) | r; } } SDL_UnlockTexture (ptex); } // draw SDL_SetRenderTarget (rnd, tex); SDL_RenderTexture (rnd, ptex, NULL, NULL); } // --------- // draw webp // --------- static SDL_Surface *webp_sur = NULL; static SDL_Texture *webp_tex = NULL; static int webp_beg = unset_int; static int webp_end = unset_int; static const int webp_dur = 448000; void webp_init () { assert (rnd); if (!webp_sur) { webp_beg = SDL_GetTicks(); webp_sur = IMG_Load ("webp.webp"); if (webp_sur) { surfaces_created++; } } if (!webp_tex && webp_sur) { webp_tex = SDL_CreateTextureFromSurface(rnd, webp_sur); if (webp_tex) { textures_created++; } SDL_SetTextureBlendMode (webp_tex, SDL_BLENDMODE_BLEND); } } void webp_quit () { if (webp_sur) { SDL_DestroySurface (webp_sur); webp_sur = NULL; surfaces_destroyed++; } if (webp_tex) { SDL_DestroyTexture (webp_tex); webp_tex = NULL; textures_destroyed++; } } emacs_value sdl_webp (emacs_env* env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { assert (rnd); if (!webp_sur) { webp_init (); } if (webp_sur) { SDL_LockSurface (webp_sur); uint8_t *pixels = webp_sur -> pixels; int pitch = webp_sur -> pitch; int h = webp_sur -> h; size_t size = pitch * h; SDL_UnlockSurface (webp_sur); return (env->make_unibyte_string (env, (char*)pixels, size)); } return NULL; } void webp_draw () { assert (rnd); if (!webp_sur || !webp_tex) { webp_init (); } if (webp_tex) { if (webp_beg == unset_int) { webp_beg = SDL_GetTicks(); webp_end = webp_beg + webp_dur; } float cur_tme = SDL_GetTicks(); float cur_prt = (cur_tme - webp_beg) / webp_dur; float dampen = (1 - cur_prt) * sinf(cur_tme); Uint8 mod = SDL_clamp(dampen * 255, 0, 255); if (cur_prt < 1) { SDL_SetRenderTarget (rnd, webp_tex); SDL_SetTextureColorMod (webp_tex, mod, mod, mod); SDL_SetTextureAlphaMod (webp_tex, 240); SDL_SetTextureBlendMode (webp_tex, SDL_BLENDMODE_BLEND); SDL_RenderTexture (rnd, webp_tex, NULL, NULL); } } } // ----------- // draw circle // ----------- static SDL_Texture *planet_tex = NULL; void circle_init () { if (rnd && !planet_tex) { planet_tex = IMG_LoadTexture (rnd, "planet.png"); if (planet_tex) { textures_created++; } } } void circle_quit () { if (planet_tex) { SDL_DestroyTexture (planet_tex); planet_tex = NULL; textures_destroyed++; } } float randf () { const float RAND_MAXF = (float) RAND_MAX; const float rand_rndf = (float) rand(); return (rand_rndf / RAND_MAXF); } void draw_triangle (vector p0, vector p1, vector p2, SDL_FColor c0, SDL_FColor c1, SDL_FColor c2) { const int indices [tri] = { 0, 1, 2 }; SDL_Vertex verts [tri] = { { .position = { .x = p0.x, .y = p0.y }, .color = c0 }, { .position = { .x = p1.x, .y = p1.y }, .color = c1 }, { .position = { .x = p2.x, .y = p2.y }, .color = c2 } }; SDL_SetRenderTarget (rnd, tex); SDL_RenderGeometry (rnd, NULL, verts, tri, indices, tri); SDL_RenderTexture (rnd, tex, NULL, &gsr); } void draw_circle (int cx, int cy, int radius, SDL_FColor col) { const int segs = 64; const int segs_start_stop = segs + 2; SDL_Vertex verts [segs_start_stop]; // int to float const float radiusf = (float) radius; const float cxf = (float) cx; const float cyf = (float) cy; const float segsf = (float) segs; // center verts[0].position = (SDL_FPoint) { .x = cxf, .y = cyf }; verts[0].color = col; if (planet_tex) verts[0].tex_coord = (SDL_FPoint) { .x = 0.5f, .y = 0.5f }; else verts[0].tex_coord = (SDL_FPoint) { .x = 0.0f, .y = 0.0f }; // radial falloff const float bse = 1.0f; const float falloff = (5.0f / 8.0f); // less is more // random jitter const float mod = 0.04f; const float min = bse - mod; const float max = bse + mod; const float dif = max - min; // horizontal modulation const float mod_h = 0.3f; const float min_h = bse - mod_h; const float max_h = bse + mod_h; const float dif_h = max_h - min_h; float i; int j; for (i = 0.0f, j = 1; i <= segs; i++, j++) { // theta and coords float t = (2.0f * i * pi) / segsf; float x = cxf + radiusf * cosf (t); float y = cyf + radiusf * sinf (t); verts[j].position = (SDL_FPoint) { .x = x, .y = y }; // jitter const float j_red = min + dif * randf(); const float j_grn = min + dif * randf(); const float j_blu = min + dif * randf(); // horzontal const float hori = min_h + dif_h * (1.0f - fabs (sin (t))); // combined verts[j].color = (SDL_FColor) { .r = col.r * falloff * j_red * hori, .g = col.g * falloff * j_grn * hori, .b = col.b * falloff * j_blu * hori, .a = 1.0f }; // texture if (planet_tex) verts[j].tex_coord = (SDL_FPoint) { .x = 0.5f * (1 + cosf (t)), .y = 0.5f * (1 + sinf (t)) }; else verts[j].tex_coord = (SDL_FPoint) { .x = 0.0f, .y = 0.0f }; } // triangles const int tris = segs * 3; int indices [tris]; for (int i = 0, j = 0; i < segs; i++, j = 3 * i) { indices [j ] = 0; indices [j + 1] = 1 + i; indices [j + 2] = 2 + i; } // done SDL_SetRenderTarget (rnd, tex); SDL_RenderGeometry (rnd, planet_tex, verts, segs_start_stop, indices, tris); SDL_RenderTexture (rnd, tex, NULL, &gsr); } // ------ // strdup // ------ char *my_strdup (const char *s) { assert (s); if (s) { size_t len = strlen (s) + 1; char *cpy = malloc (len); memcpy (cpy, s, len); return cpy; } return NULL; }