From 8bf80674ae5150d6c718f6d6ef4907b85dd8b083 Mon Sep 17 00:00:00 2001 From: Alexander Kavon Date: Mon, 16 Mar 2026 02:45:43 -0400 Subject: working webcam demo --- main.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 main.c (limited to 'main.c') diff --git a/main.c b/main.c new file mode 100644 index 0000000..2a37839 --- /dev/null +++ b/main.c @@ -0,0 +1,395 @@ +/* nuklear - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* This demo uses "main callbacks" which are new in SDL3 + * Those provide highly portable entry point and event loop for the app + * see: https://wiki.libsdl.org/SDL3/README-main-functions + * */ +#define SDL_MAIN_USE_CALLBACKS +#include + +#include "./camera.c" + +/* =============================================================== + * + * CONFIG + * + * ===============================================================*/ + +/* optional: sdl3_renderer does not need any of these defines + * (but some examples might need them, so be careful) */ +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_STANDARD_IO + +/* note that sdl3_renderer comes with nk_sdl_style_set_debug_font() + * so you may wish to use that instead of font baking */ +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT + +/* note that sdl3_renderer comes with nk_sdl_allocator() + * and you probably want to use that allocator instead of the default ones */ +/*#define NK_INCLUDE_DEFAULT_ALLOCATOR*/ + +/* mandatory: sdl3_renderer depends on those defines */ +#define NK_INCLUDE_COMMAND_USERDATA +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT + + +/* We can re-use the types provided by SDL which are extremely portable, + * so there is no need for Nuklear to detect those on its own */ +/*#define NK_INCLUDE_FIXED_TYPES*/ +#ifndef NK_INCLUDE_FIXED_TYPES + #define NK_INT8 Sint8 + #define NK_UINT8 Uint8 + #define NK_INT16 Sint16 + #define NK_UINT16 Uint16 + #define NK_INT32 Sint32 + #define NK_UINT32 Uint32 + /* SDL guarantees 'uintptr_t' typedef */ + #define NK_SIZE_TYPE uintptr_t + #define NK_POINTER_TYPE uintptr_t +#endif + +/* We can reuse the `bool` symbol because SDL3 guarantees its existence */ +/*#define NK_INCLUDE_STANDARD_BOOL*/ +#ifndef NK_INCLUDE_STANDARD_BOOL + #define NK_BOOL bool +#endif + +/* We can re-use various portable libc functions provided by SDL */ +#define NK_ASSERT(condition) SDL_assert(condition) +#define NK_STATIC_ASSERT(exp) SDL_COMPILE_TIME_ASSERT(, exp) +#define NK_MEMSET(dst, c, len) SDL_memset(dst, c, len) +#define NK_MEMCPY(dst, src, len) SDL_memcpy(dst, src, len) +#define NK_VSNPRINTF(s, n, f, a) SDL_vsnprintf(s, n, f, a) +#define NK_STRTOD(str, endptr) SDL_strtod(str, endptr) + +/* SDL3 does not provide "dtoa" (only integer versions) + * but we can emulate it with SDL_snprintf */ +static char* nk_sdl_dtoa(char *str, double d); +#define NK_DTOA(str, d) nk_sdl_dtoa(str, d) + +/* SDL can also provide us with math functions, but beware that Nuklear's own + * implementation can be slightly faster at the cost of some precision */ +#define NK_INV_SQRT(f) (1.0f / SDL_sqrtf(f)) +#define NK_SIN(f) SDL_sinf(f) +#define NK_COS(f) SDL_cosf(f) + +/* HACK: Nuklear pulls two stb libraries in order to use font baking + * those libraries pull in some libc headers internally, creating a linkage dependency, + * so you’ll most likely want to use SDL symbols instead */ +#define STBTT_ifloor(x) ((int)SDL_floor(x)) +#define STBTT_iceil(x) ((int)SDL_ceil(x)) +#define STBTT_sqrt(x) SDL_sqrt(x) +#define STBTT_pow(x,y) SDL_pow(x,y) +#define STBTT_fmod(x,y) SDL_fmod(x,y) +#define STBTT_cos(x) SDL_cosf(x) +#define STBTT_acos(x) SDL_acos(x) +#define STBTT_fabs(x) SDL_fabs(x) +#define STBTT_assert(x) SDL_assert(x) +#define STBTT_strlen(x) SDL_strlen(x) +#define STBTT_memcpy SDL_memcpy +#define STBTT_memset SDL_memset +#define stbtt_uint8 Uint8 +#define stbtt_int8 Sint8 +#define stbtt_uint16 Uint16 +#define stbtt_int16 Sint16 +#define stbtt_uint32 Uint32 +#define stbtt_int32 Sint32 +#define STBRP_SORT SDL_qsort +#define STBRP_ASSERT SDL_assert +/* There is no need to define STBTT_malloc/STBTT_free macros + * Nuklear will define those to user-provided nk_allocator */ + + +#define NK_IMPLEMENTATION +#include "../nuklear/nuklear.h" +#define NK_SDL3_RENDERER_IMPLEMENTATION +#include "nuklear_sdl3_renderer.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ + +struct nk_sdl_app { + SDL_Window* window; + SDL_Renderer* renderer; + struct nk_context * ctx; + struct nk_colorf bg; + enum nk_anti_aliasing AA; + camera_t* camera; +}; + +static SDL_AppResult +nk_sdl_fail() +{ + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error: %s", SDL_GetError()); + return SDL_APP_FAILURE; +} + +SDL_AppResult +SDL_AppInit(void** appstate, int argc, char* argv[]) +{ + struct nk_sdl_app* app; + struct nk_context* ctx; + camera_t * camera; + float font_scale; + NK_UNUSED(argc); + NK_UNUSED(argv); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { + return nk_sdl_fail(); + } + + app = SDL_malloc(sizeof(*app)); + if (app == NULL) { + return nk_sdl_fail(); + } + + app->camera = malloc(sizeof(*camera)); + app->camera->latest_frame = NULL; + get_camera_devices(app->camera); + + if (!SDL_CreateWindowAndRenderer("Nuklear: SDL3 Renderer", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &app->window, &app->renderer)) { + SDL_free(app); + return nk_sdl_fail(); + } + *appstate = app; + + if (!SDL_SetRenderVSync(app->renderer, 1)) { + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "SDL_SetRenderVSync failed: %s", SDL_GetError()); + } + + app->bg.r = 0.10f; + app->bg.g = 0.18f; + app->bg.b = 0.24f; + app->bg.a = 1.0f; + + font_scale = 1; + { + /* This scaling logic was kept simple for the demo purpose. + * On some platforms, this might not be the exact scale + * that you want to use. For more information, see: + * https://wiki.libsdl.org/SDL3/README-highdpi */ + const float scale = SDL_GetWindowDisplayScale(app->window); + SDL_SetRenderScale(app->renderer, scale, scale); + font_scale = scale; + } + + ctx = nk_sdl_init(app->window, app->renderer, nk_sdl_allocator()); + app->ctx = ctx; + +#if 0 + { + /* If you don't want to use advanced Nuklear font baking API + * you can use simple ASCII debug font provided by SDL + * just change the `#if 0` above to `#if 1` */ + nk_sdl_style_set_debug_font(ctx); + + /* Note that since debug font is extremely small (only 8x8 pixels), + * scaling it does not make much sense. The font would appear blurry. */ + NK_UNUSED(font_scale); + + /* You may wish to change a few style options, here are few recommendations: */ + ctx->style.button.rounding = 0.0f; + ctx->style.menu_button.rounding = 0.0f; + ctx->style.property.rounding = 0.0f; + ctx->style.property.border = 0.0f; + ctx->style.option.border = -1.0f; + ctx->style.checkbox.border = -1.0f; + ctx->style.property.dec_button.border = -2.0f; + ctx->style.property.inc_button.border = -2.0f; + ctx->style.tab.tab_minimize_button.border = -2.0f; + ctx->style.tab.tab_maximize_button.border = -2.0f; + ctx->style.tab.node_minimize_button.border = -2.0f; + ctx->style.tab.node_maximize_button.border = -2.0f; + ctx->style.checkbox.spacing = 5.0f; + + /* It's better to disable anti-aliasing when using small fonts */ + app->AA = NK_ANTI_ALIASING_OFF; + } +#else + { + struct nk_font_atlas *atlas; + struct nk_font_config config = nk_font_config(0); + struct nk_font *font; + + /* set up the font atlas and add desired font; note that font sizes are + * multiplied by font_scale to produce better results at higher DPIs */ + atlas = nk_sdl_font_stash_begin(ctx); + font = nk_font_atlas_add_default(atlas, 16 * font_scale, &config); + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 16 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13 * font_scale, &config);*/ + nk_sdl_font_stash_end(ctx); + + /* this hack makes the font appear to be scaled down to the desired + * size and is only necessary when font_scale > 1 */ + font->handle.height /= font_scale; + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + nk_style_set_font(ctx, &font->handle); + + app->AA = NK_ANTI_ALIASING_ON; + } +#endif + + nk_input_begin(ctx); + + return SDL_APP_CONTINUE; +} + +SDL_AppResult +SDL_AppEvent(void *appstate, SDL_Event* event) +{ + struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; + + switch (event->type) { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: + /* You may wish to rescale the renderer and Nuklear during this event. + * Without this the UI and Font could appear too small or too big. + * This is not handled by the demo in order to keep it simple, + * but you may wish to re-bake the Font whenever this happens. */ + SDL_Log("Unhandled scale event! Nuklear may appear blurry"); + return SDL_APP_CONTINUE; + } + + /* Remember to always rescale the event coordinates, + * if your renderer uses custom scale. */ + SDL_ConvertEventToRenderCoordinates(app->renderer, event); + + nk_sdl_handle_event(app->ctx, event); + + return SDL_APP_CONTINUE; +} + +SDL_AppResult +SDL_AppIterate(void *appstate) +{ + struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; + struct nk_context* ctx = app->ctx; + int wwidth = 0; + int wheight = 0; + static char text[9][64]; + static int text_len[9]; + SDL_GetWindowSizeInPixels(app->window, &wwidth, &wheight); + + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Recent", nk_rect(0, 0, 300, wheight), + NK_WINDOW_TITLE|NK_TEXT_CENTERED)) + { + + } + nk_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Main", nk_rect(300, 0, wwidth-200-300, wheight), + NK_WINDOW_BORDER)) + { + nk_layout_row_dynamic(ctx, 30, 2); + nk_label(ctx, "Loaf Key", 0); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[0], &text_len[0], 64, nk_filter_default); + if(nk_button_label(ctx, "Action!")) { + start_camera_stream(app->camera); + } + if(nk_button_label(ctx, "Cut!")) { + stop_camera_stream(app->camera); + } + nk_spacer(ctx); + nk_spacer(ctx); + } + + if (nk_begin(ctx, "Main", nk_rect(300, 0, wwidth-200-300, wheight), + NK_WINDOW_BORDER)) + { + if (app->camera->latest_frame != NULL && app->camera->latest_frame->data_bytes > 0) { + const uvc_frame_t * frame = app->camera->latest_frame; + printf("Texture WxH: %dx%d, Bytes: %zd\n", frame->width, frame->height, frame->data_bytes); + SDL_Texture * texture = SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_MJPG, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); + if (texture == NULL) { + puts("ERROR Creating Texture!"); + const char * err = SDL_GetError(); + puts(err); + puts("---"); + } + bool upres = SDL_UpdateTexture(texture, NULL, frame->data, frame->data_bytes); + if (!upres) { + puts("Failed to add frame to Texture"); + const char * err = SDL_GetError(); + puts(err); + puts("---"); + } + + struct nk_image camera_image; + camera_image = nk_image_ptr(texture); + struct nk_command_buffer * canvas = nk_window_get_canvas(ctx); + struct nk_rect total_space = nk_window_get_content_region(ctx); + const struct nk_color grid_color = nk_rgba(255, 255, 255, 255); + nk_draw_image(canvas, total_space, &camera_image, grid_color); + } + } + nk_end(ctx); + + SDL_SetRenderDrawColorFloat(app->renderer, app->bg.r, app->bg.g, app->bg.b, app->bg.a); + SDL_RenderClear(app->renderer); + + nk_sdl_render(ctx, app->AA); + nk_sdl_update_TextInput(ctx); + + /* show if TextInput is active for debug purpose. Feel free to remove this. */ + SDL_SetRenderDrawColor(app->renderer, 0xFF, 0xFF, 0xFF, 0xFF); + // SDL_RenderDebugTextFormat(app->renderer, 10, 10, "APP WINDOW W:%d, H:%d", wwidth, wheight); + SDL_RenderPresent(app->renderer); + + nk_input_begin(ctx); + return SDL_APP_CONTINUE; +} + +void +SDL_AppQuit(void* appstate, SDL_AppResult result) +{ + struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; + NK_UNUSED(result); + + if (app) { + close_camera(app->camera); + nk_input_end(app->ctx); + nk_sdl_shutdown(app->ctx); + SDL_DestroyRenderer(app->renderer); + SDL_DestroyWindow(app->window); + SDL_free(app); + } +} + +static char* +nk_sdl_dtoa(char *str, double d) +{ + NK_ASSERT(str); + if (!str) return NULL; + (void)SDL_snprintf(str, 99999, "%.17g", d); + return str; +} + -- cgit v1.2.3