// https://dataswamp.org/~incal/bad-el/src/bad-sdl.c int plugin_is_GPL_compatible; #include #include "bad-sdl.h" #include #include #include #include #include #include #define nas __attribute__((unused)) #define lines 32 #define tri 3 #define unset_int -1 #define unset32 UINT32_MAX #define unset_double -133.7 static SDL_Surface *webp_sur = NULL; static SDL_Texture *webp_tex = NULL; static Uint32 webp_beg = unset32; static Uint32 webp_end = unset32; static const Uint32 webp_dur = 448000; #define cache_side 256 #define cache_size 16777216 static SDL_Texture *cache [cache_size]; static int mutexes_created = 0; static int mutexes_destroyed = 0; static int surfaces_created = 0; static int surfaces_destroyed = 0; static int textures_created = 0; static int textures_destroyed = 0; static int frames_attempted = 0; static int frames_completed = 0; static int frames_timeout = 0; static int cache_attempted = 0; static int cache_collision = 0; static int cache_insert = 0; static SDL_DisplayID *display = NULL; typedef struct { SDL_Mutex *mtx; char *str; SDL_Surface *sur; size_t new; size_t old; } LineMeta; static SDL_FRect gsr; static SDL_Mutex *cache_mtx = NULL; static SDL_Renderer *rnd = NULL; static SDL_Window *win = NULL; static SDL_Texture *ptex = NULL; static SDL_Texture *tex = NULL; static SDL_Texture *planet_tex = NULL; static bool draw_light_mode = true; static bool draw_grid_mode = false; static int res_w = unset_int; static int res_h = unset_int; static int rnd_w = unset_int; static int rnd_h = unset_int; static SDL_Color fg = { 230, 240, 255, 250 }; static TTF_Font *fnt = NULL; static int fnt_sze = 16; static int fnt_w = unset_int; static int fnt_h = unset_int; static bool thr_run = false; static LineMeta lines_pipe [lines]; static SDL_Thread *thr_cac = NULL; static SDL_Thread *thr_lop = NULL; static SDL_Thread *thr_rus = NULL; static SDL_Thread *thr_sur = NULL; // ---- // font // ---- void font_init () { TTF_Init (); fnt = TTF_OpenFont ("/usr/share/fonts/truetype/ocr-a/OCRABold.ttf", fnt_sze); if (!fnt) { SDL_Log ("Cannot find hardcoded font.\n"); } int minx nas, maxx nas, miny nas, maxy nas, advance; TTF_GetGlyphMetrics (fnt, '.', &minx, &maxx, &miny, &maxy, &advance); if (fnt_w == unset_int) { fnt_w = advance; } if (fnt_h == unset_int) { fnt_h = TTF_GetFontLineSkip (fnt); } SDL_Log ("font w x h: %d x %d\n", fnt_w, fnt_h); } void font_quit () { if (fnt) { TTF_CloseFont (fnt); fnt = NULL; } TTF_Quit(); } // ----- // cache // ----- void cache_init () { cache_mtx = SDL_CreateMutex(); mutexes_created++; SDL_UnlockMutex (cache_mtx); } void cache_clear () { SDL_LockMutex (cache_mtx); for (uint32_t i = 0; i < cache_size; i++) { if (cache [i]) { SDL_DestroyTexture (cache [i]); textures_destroyed++; cache [i] = NULL; } } SDL_UnlockMutex (cache_mtx); } void cache_quit () { cache_clear (); SDL_DestroyMutex (cache_mtx); mutexes_destroyed++; cache_mtx = NULL; } // ------ // thread // ------ int thr_loop (void *arg nas) { const SDL_ThreadID id = SDL_GetThreadID (NULL); const Uint64 hz = SDL_GetPerformanceFrequency (); const Uint64 fps = 60; const Uint64 delta = hz / fps; const Uint64 div = 1e9 / hz; Uint64 end = unset_int; int frames = unset_int; double next = unset_double; SDL_Event e; const Uint64 beg = SDL_GetPerformanceCounter (); while (thr_run) { SDL_PollEvent (&e); if (e.type == SDL_EVENT_KEY_DOWN) { switch (e.key.key) { case SDLK_Q: thr_run = false; break; case SDLK_T: SDL_Log ("We create a mental atmosphere.\n"); break; default: SDL_Log ("You can't do that, at least not now.\n"); break; } } next = beg + (frames + 1) * delta; end = SDL_GetPerformanceCounter (); if (end < next) { SDL_DelayNS ((next - end) / div); } } if (0 < frames) { SDL_Log ("[%" SDL_PRIu64 "] looped %" SDL_PRIu32 " frames\n", id, frames); } return 0; } int thr_surface (void *arg nas) { const SDL_ThreadID id = SDL_GetThreadID (NULL); static __thread int drawn_local = 0; Uint32 idx; Uint8 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) % cache_size; lines_pipe[row].new = idx; if (cache_mtx) { SDL_LockMutex (cache_mtx); if (!cache[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 (cache_mtx); SDL_free (lines_pipe[row].str); lines_pipe[row].str = NULL; } SDL_UnlockMutex (lines_pipe[row].mtx); } } SDL_Delay (1); } if (0 < drawn_local) { SDL_Log ("[thread %" PRIu64 "] %" PRIu32 " 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 < cache_insert) { cache_clear (); clears++; max_nxt += max_all; } SDL_Delay (1024); } if (0 < clears) { SDL_Log ("[thread %" PRIu64 "] cleared cache %" PRIu8 " times\n", id, clears); } return 0; } void thr_init () { SDL_assert (!thr_lop); thr_run = true; if (!thr_sur) { thr_sur = SDL_CreateThread (thr_surface, "sdl-surface", NULL); } if (!thr_rus) { thr_rus = SDL_CreateThread (thr_surface, "sdl-rusface", NULL); } if (!thr_cac) { thr_cac = SDL_CreateThread (thr_cache, "sdl-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 *s) { SDL_assert (s); uint8_t dig [SHA256_DIGEST_LENGTH]; SHA256 ((const unsigned char*)s, strlen (s), dig); return (Uint32) dig [0] << 24 | (Uint32) dig [1] << 16 | (Uint32) dig [2] << 8 | (Uint32) dig [3]; } void store_texture (int i) { SDL_assert (0 <= i); SDL_assert (i < lines); // lock SDL_LockMutex (cache_mtx); SDL_LockMutex (lines_pipe[i].mtx); if ((lines_pipe[i].new != unset32) && lines_pipe[i].sur) { // collision if (cache[lines_pipe[i].new]) { SDL_DestroyTexture (cache[lines_pipe[i].new]); textures_destroyed++; cache [lines_pipe[i].new] = NULL; cache_collision++; } // create texture cache [lines_pipe[i].new] = SDL_CreateTextureFromSurface (rnd, lines_pipe[i].sur); SDL_SetTextureBlendMode (cache[lines_pipe[i].new], SDL_BLENDMODE_BLEND); textures_created++; cache_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 (cache_mtx); } void draw_texture (int i) { SDL_assert (0 <= i); SDL_assert (i < lines); float tw, th, nw, nh; int y = fnt_h * i; SDL_GetTextureSize (tex, &tw, &th); SDL_LockMutex (cache_mtx); SDL_LockMutex (lines_pipe[i].mtx); SDL_GetTextureSize (cache[lines_pipe[i].new], &nw, &nh); SDL_UnlockMutex (lines_pipe[i].mtx); SDL_UnlockMutex (cache_mtx); const SDL_FRect sr = { 0, y, SDL_ceil (tw), SDL_ceil (nh) }; const SDL_FRect nr = { 0, y, SDL_ceil (nw), SDL_ceil (nh) }; SDL_SetRenderTarget (rnd, tex); SDL_RenderFillRect (rnd, &sr); SDL_LockMutex (cache_mtx); SDL_RenderTexture (rnd, cache [lines_pipe[i].new], NULL, &nr); SDL_UnlockMutex (cache_mtx); } void draw_string (const char *s) { SDL_assert (s); int enqd = 0; bool draw = false; char *buf = SDL_strdup (s); char *save = NULL; char *lne = strtok_r (buf, "\n", &save); for (int i = 0; (i < lines) && lne; i++) { SDL_LockMutex (lines_pipe[i].mtx); if (lines_pipe[i].str) { SDL_free (lines_pipe[i].str); lines_pipe[i].str = NULL; } else if (lne) { lines_pipe[i].str = SDL_strdup (lne); } SDL_UnlockMutex (lines_pipe[i].mtx); enqd++; cache_attempted++; lne = strtok_r (NULL, "\n", &save); } // wait for surfaces or refs to cache 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); } 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); } } frames_attempted++; if (total_max <= total) { frames_timeout++; } if (buf) { SDL_free (buf); buf = NULL; } if (draw) { frames_completed++; } } void draw_screen () { 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; } // sdl_draw_init int nargs = 0; emacs_value sdl_draw_init_func = env->make_function (env, nargs, nargs, sdl_draw_init, "Init draw.", NULL); emacs_value sdl_draw_init_symb = env->intern (env, "sdl_draw_init"); emacs_value sdl_draw_init_args[] = { sdl_draw_init_symb, sdl_draw_init_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_init_args); // sdl_draw_quit nargs = 0; emacs_value sdl_draw_quit_func = env->make_function (env, nargs, nargs, sdl_draw_quit, "Quit draw.", NULL); emacs_value sdl_draw_quit_symb = env->intern (env, "sdl_draw_quit"); emacs_value sdl_draw_quit_args[] = { sdl_draw_quit_symb, sdl_draw_quit_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_quit_args); // sdl_draw_clear nargs = 0; emacs_value sdl_draw_clear_func = env->make_function (env, nargs, nargs, sdl_draw_clear, "Clear.", NULL); emacs_value sdl_draw_clear_symb = env->intern (env, "sdl_draw_clear"); emacs_value sdl_draw_clear_args[] = { sdl_draw_clear_symb, sdl_draw_clear_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_clear_args); // sdl_draw_text nargs = 0; emacs_value sdl_draw_text_func = env->make_function (env, nargs, nargs, sdl_draw_text, "Text.", NULL); emacs_value sdl_draw_text_symb = env->intern (env, "sdl_draw_text"); emacs_value sdl_draw_text_args[] = { sdl_draw_text_symb, sdl_draw_text_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_text_args); // sdl_draw_frame nargs = 0; emacs_value sdl_draw_frame_func = env->make_function (env, nargs, nargs, sdl_draw_frame, "Draw frame.", NULL); emacs_value sdl_draw_frame_symb = env->intern (env, "sdl_draw_frame"); emacs_value sdl_draw_frame_args[] = { sdl_draw_frame_symb, sdl_draw_frame_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_frame_args); // sdl_draw_circle nargs = 6; emacs_value sdl_draw_circle_func = env->make_function (env, nargs, nargs, sdl_draw_circle, "Draw circle.", 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, "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); // sdl_draw_grid_toggle nargs = 0; emacs_value sdl_draw_grid_toggle_func = env->make_function (env, nargs, nargs, sdl_draw_grid_toggle, "Toggle draw grid.", NULL); emacs_value sdl_draw_grid_toggle_symb = env->intern (env, "sdl_draw_grid_toggle"); emacs_value sdl_draw_grid_toggle_args[] = { sdl_draw_grid_toggle_symb, sdl_draw_grid_toggle_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_grid_toggle_args); // sdl_draw_webp nargs = 0; emacs_value sdl_draw_webp_func = env->make_function (env, nargs, nargs, sdl_draw_webp, "Load a webp image.", NULL); emacs_value sdl_draw_webp_symb = env->intern (env, "sdl_draw_webp"); emacs_value sdl_draw_webp_args[] = { sdl_draw_webp_symb, sdl_draw_webp_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_webp_args); // done return 0; } void draw_init () { video_init (); circle_init (); webp_init (); font_init (); cache_init (); thr_init (); for (int i = 0; i < lines; i++) { lines_pipe[i].mtx = SDL_CreateMutex(); mutexes_created++; lines_pipe[i].str = NULL; lines_pipe[i].sur = NULL; lines_pipe[i].new = unset32; lines_pipe[i].old = unset32; } SDL_srand (0); } emacs_value sdl_draw_init (emacs_env* env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { SDL_assert (env); draw_init (); 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); mutexes_destroyed++; lines_pipe[i].mtx = NULL; } } } void draw_quit () { thr_quit (); cache_quit (); font_quit (); circle_quit (); webp_quit (); video_quit (); mutex_quit (); SDL_Quit (); // stats SDL_Log ("%" PRIu32 " mutexes created\n", mutexes_created ); SDL_Log ("%" PRIu32 " -------- destroyed\n", mutexes_destroyed ); SDL_Log ("%" PRIu32 " textures created\n", textures_created ); SDL_Log ("%" PRIu32 " -------- destroyed\n", textures_destroyed ); SDL_Log ("%" PRIu32 " surfaces created\n", surfaces_created ); // worst-case: (100.0f * (surfaces_created / (1.0f + cache_attempted))), cache_attempted) SDL_Log ("%" PRIu32 " -------- destroyed\n", surfaces_destroyed ); SDL_Log ("%" PRIu32 " -------- collisions\n", cache_collision ); SDL_Log ("%" PRIu32 " frames attempted\n", frames_attempted ); SDL_Log ("%" PRIu32 " -------- completed\n", frames_completed ); } emacs_value sdl_draw_quit (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { SDL_assert (env); draw_quit (); return env->intern (env, "nil"); } // ----- // video // ----- void video_init () { SDL_Init (SDL_INIT_VIDEO); // window int count; SDL_DisplayID *displays = SDL_GetDisplays (&count); SDL_DisplayID display = (count > 0) ? displays[0] : 0; const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode (display); if (dm) { res_w = dm->w; res_h = dm->h; } else { SDL_Log ("SDL_GetCurrentDisplayMode DNC, using fallback values\n"); res_w = 1280; res_h = 720; } SDL_WindowFlags win_flags = SDL_WINDOW_FULLSCREEN; win = SDL_CreateWindow ("bad-el SDL3", res_w, res_h, win_flags); // renderer rnd = SDL_CreateRenderer (win, "opengl"); SDL_SetRenderDrawBlendMode (rnd, SDL_BLENDMODE_BLEND); SDL_GetCurrentRenderOutputSize (rnd, &rnd_w, &rnd_h); SDL_SetRenderLogicalPresentation (rnd, rnd_w, rnd_h, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); // 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 SDL_free (displays); } 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 () { SDL_SetRenderDrawColor (rnd, 4, 8, 16, 255); // bg color SDL_SetRenderTarget (rnd, tex); SDL_RenderClear (rnd); } emacs_value sdl_draw_clear (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { SDL_assert (env); clear (); return env->intern (env, "nil"); } emacs_value sdl_draw_frame (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { SDL_assert (env); if (draw_grid_mode) { 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 = SDL_malloc (l); env->copy_string_contents (env, es, txt_str, &l); if (txt_str) { draw_string (txt_str); } SDL_free (txt_str); txt_str = NULL; } } if (draw_light_mode) { draw_light (2, 4, 16); } draw_screen (); return env->intern (env, "nil"); } emacs_value sdl_draw_text (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { SDL_assert (env); SDL_SetRenderDrawColor (rnd, 230, 240, 250, 255); SDL_SetRenderScale (rnd, 2, 2); SDL_RenderDebugText (rnd, 64, 64, "THANKS FOR WATCHING!"); SDL_SetRenderScale (rnd, 1, 1); return env->intern (env, "nil"); } emacs_value sdl_draw_triangle (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data nas) { SDL_assert (env); SDL_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 = 1.0f; 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 = 1.0f; 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 = 1.0f; // 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 color SDL_assert (0.0f <= c0.r); SDL_assert (0.0f <= c0.g); SDL_assert (0.0f <= c0.b); SDL_assert (0.0f <= c0.a); SDL_assert (0.0f <= c1.r); SDL_assert (0.0f <= c1.g); SDL_assert (0.0f <= c1.b); SDL_assert (0.0f <= c1.a); SDL_assert (0.0f <= c2.r); SDL_assert (0.0f <= c2.g); SDL_assert (0.0f <= c2.b); SDL_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) { SDL_assert (env); SDL_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 = { .r = red, .g = green, .b = blue, .a = 1.0f }; draw_circle (x, y, radius, col); return env->intern (env, "nil"); } // ----- // modes // ----- emacs_value sdl_draw_grid_toggle (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { SDL_assert (env); 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 = (SDL_round (max_w * part / full)); int mar_y = (SDL_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 = SDL_sqrt (SDL_pow (dx, 2) + SDL_pow (dy, 2)); float bright = SDL_max (0.0f, 1.0f - dist / radius); float curve = SDL_pow (bright, 1.5f); uint8_t r = (uint8_t) (128 * curve); uint8_t g = (uint8_t) (104 * 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); } SDL_SetRenderTarget (rnd, tex); SDL_RenderTexture (rnd, ptex, NULL, NULL); } // ---- // webp // ---- void webp_init () { if (!webp_sur) { webp_beg = SDL_GetTicks(); webp_sur = IMG_Load ("data/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_draw_webp (emacs_env *env, ptrdiff_t nargs nas, emacs_value *args nas, void *data nas) { SDL_assert (env); 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 env->intern (env, "nil"); } void webp_draw () { if (!webp_sur || !webp_tex) { webp_init (); } if (webp_tex) { if (webp_beg == unset32) { 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) * SDL_sin (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); } } } // ------ // circle // ------ void circle_init () { if (!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++; } } void draw_triangle (vector p0, vector p1, vector p2, SDL_FColor c0, SDL_FColor c1, SDL_FColor c2) { const int indices [3] = { 0, 1, 2 }; SDL_Vertex verts [3] = { { { p0.x, p0.y }, c0, {0, 0} }, { { p1.x, p1.y }, c1, {0, 0} }, { { p2.x, p2.y }, c2, {0, 0} } }; SDL_SetRenderTarget (rnd, tex); SDL_RenderGeometry (rnd, NULL, verts, 3, indices, 3); } 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 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 + radius * SDL_cos (t); float y = cyf + radius * SDL_sin (t); verts[j].position = (SDL_FPoint) { .x = x, .y = y }; // jitter const float j_red = min + dif * (float) SDL_rand (0.94f); const float j_grn = min + dif * (float) SDL_rand (1.04f); const float j_blu = min + dif * (float) SDL_rand (1.02f); // horzontal const float hori = min_h + dif_h * (1.0f - SDL_abs (SDL_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 + SDL_cos (t)), .y = 0.5f * (1 + SDL_sin (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) { SDL_assert (s); if (s) { size_t len = strlen (s) + 1; char *cpy = SDL_malloc (len); memcpy (cpy, s, len); return cpy; } return NULL; }