#include #include #include #include #include #include #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 #define LOGO_HEIGHT 64 #define LOGO_SPEED 300.0f #define GIF_FPS 20 static unsigned int hash_str(const char *s) { unsigned int h = 5381; while (*s) h = h * 33 ^ (unsigned char)*s++; return h; } static void generate_gif(const char *output_gif, const char **logo_paths, int logo_count, float switch_secs, int duration_secs) { if (FileExists(output_gif)) { TraceLog(LOG_INFO, "Skipping %s (already exists)", output_gif); return; } TraceLog(LOG_INFO, "Generating %s...", output_gif); char tmpdir[] = "/tmp/demo_gen_XXXXXX"; if (!mkdtemp(tmpdir)) { perror("mkdtemp"); return; } Texture2D *textures = (Texture2D *)malloc(logo_count * sizeof(Texture2D)); for (int i = 0; i < logo_count; i++) { Image img = LoadImage(logo_paths[i]); ImageResize(&img, img.width * LOGO_HEIGHT / img.height, LOGO_HEIGHT); textures[i] = LoadTextureFromImage(img); UnloadImage(img); } RenderTexture2D target = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT); SetRandomSeed(hash_str(output_gif)); int cur_logo = 0; float x = GetRandomValue(0, WINDOW_WIDTH - textures[cur_logo].width); float y = GetRandomValue(0, WINDOW_HEIGHT - textures[cur_logo].height); float vx = LOGO_SPEED * (GetRandomValue(0, 1) ? 1.0f : -1.0f); float vy = LOGO_SPEED * (GetRandomValue(0, 1) ? 1.0f : -1.0f); int total_frames = duration_secs * GIF_FPS; float frame_dt = 1.0f / GIF_FPS; float logo_timer = 0.0f; for (int f = 0; f < total_frames; f++) { Texture2D tex = textures[cur_logo]; x += vx * frame_dt; y += vy * frame_dt; if (x < 0 || x + tex.width >= WINDOW_WIDTH) { vx *= -1.0f; x += vx * frame_dt; } if (y < 0 || y + tex.height >= WINDOW_HEIGHT) { vy *= -1.0f; y += vy * frame_dt; } logo_timer += frame_dt; if (logo_timer >= switch_secs) { logo_timer = 0.0f; cur_logo = (cur_logo + 1) % logo_count; } BeginTextureMode(target); ClearBackground(BLACK); DrawTextureV(tex, (Vector2){x, y}, WHITE); EndTextureMode(); Image frame_img = LoadImageFromTexture(target.texture); ImageFlipVertical(&frame_img); ExportImage(frame_img, TextFormat("%s/frame_%04d.png", tmpdir, f)); UnloadImage(frame_img); TraceLog(LOG_INFO, " frame %d/%d", f + 1, total_frames); } UnloadRenderTexture(target); for (int i = 0; i < logo_count; i++) UnloadTexture(textures[i]); free(textures); if (system(TextFormat( "ffmpeg -y -framerate %d -i '%s/frame_%%04d.png' " "-vf 'fps=%d,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse' " "'%s' 2>/dev/null", GIF_FPS, tmpdir, GIF_FPS, output_gif)) != 0) TraceLog(LOG_ERROR, "ffmpeg failed for %s", output_gif); system(TextFormat("rm -rf '%s'", tmpdir)); } int main(void) { SetConfigFlags(FLAG_WINDOW_HIDDEN); InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "demo_gen"); glob_t gl; if (glob("languages/*/*_logo.png", 0, NULL, &gl) != 0) { TraceLog(LOG_ERROR, "No logos found. Run from project root."); CloseWindow(); return 1; } mkdir("docs", 0755); // generate docs/demo.gif (cycles all logos) generate_gif("docs/demo.gif", (const char **)gl.gl_pathv, (int)gl.gl_pathc, 1.0f, (int)gl.gl_pathc); // generate languages/**/demo.gif for (size_t i = 0; i < gl.gl_pathc; i++) { const char *path = gl.gl_pathv[i]; char *output_path = strdup(TextFormat("%s/demo.gif", GetDirectoryPath(path))); generate_gif(output_path, &path, 1, 9999.0f, 4); free(output_path); } CloseWindow(); globfree(&gl); return 0; }