From 24835cdc25de80e1cbc0e8cca719424a7b69e19a Mon Sep 17 00:00:00 2001 From: Benjamin Schaaf Date: Mon, 4 Jan 2021 23:39:17 +1100 Subject: [PATCH] Implement flash Fixes #5 --- config/pine64,pinephone-1.0.ini | 2 + config/pine64,pinephone-1.1.ini | 2 + config/pine64,pinephone-1.2.ini | 2 + data/camera.css | 4 + data/camera.ui | 9 ++ data/flash-disabled-symbolic.svg | 32 ++++ data/flash-enabled-symbolic.svg | 31 ++++ .../org.postmarketos.Megapixels.gresource.xml | 6 +- meson.build | 15 +- src/camera_config.c | 4 + src/camera_config.h | 3 + src/flash.c | 141 ++++++++++++++++++ src/flash.h | 8 + src/io_pipeline.c | 44 +++++- src/io_pipeline.h | 1 + src/main.c | 15 ++ 16 files changed, 302 insertions(+), 17 deletions(-) create mode 100644 data/flash-disabled-symbolic.svg create mode 100644 data/flash-enabled-symbolic.svg create mode 100644 src/flash.c create mode 100644 src/flash.h diff --git a/config/pine64,pinephone-1.0.ini b/config/pine64,pinephone-1.0.ini index 901ccc7..3b87aa7 100644 --- a/config/pine64,pinephone-1.0.ini +++ b/config/pine64,pinephone-1.0.ini @@ -23,6 +23,7 @@ cropfactor=10.81 fnumber=3.0 iso-min=100 iso-max=64000 +flash-path=/sys/class/leds/white:flash [front] driver=gc2145 @@ -40,3 +41,4 @@ mirrored=true focallength=2.6 cropfactor=12.7 fnumber=2.8 +flash-display=true diff --git a/config/pine64,pinephone-1.1.ini b/config/pine64,pinephone-1.1.ini index 901ccc7..3b87aa7 100644 --- a/config/pine64,pinephone-1.1.ini +++ b/config/pine64,pinephone-1.1.ini @@ -23,6 +23,7 @@ cropfactor=10.81 fnumber=3.0 iso-min=100 iso-max=64000 +flash-path=/sys/class/leds/white:flash [front] driver=gc2145 @@ -40,3 +41,4 @@ mirrored=true focallength=2.6 cropfactor=12.7 fnumber=2.8 +flash-display=true diff --git a/config/pine64,pinephone-1.2.ini b/config/pine64,pinephone-1.2.ini index e295f87..d3c1d68 100644 --- a/config/pine64,pinephone-1.2.ini +++ b/config/pine64,pinephone-1.2.ini @@ -24,6 +24,7 @@ cropfactor=10.81 fnumber=3.0 iso-min=100 iso-max=64000 +flash-path=/sys/class/leds/white:flash [front] driver=gc2145 @@ -41,3 +42,4 @@ mirrored=true focallength=2.6 cropfactor=12.7 fnumber=2.8 +flash-display=true diff --git a/data/camera.css b/data/camera.css index 85c65e3..68dd90d 100644 --- a/data/camera.css +++ b/data/camera.css @@ -11,3 +11,7 @@ opacity: 0.9; background-color: rgba(0, 0, 0, 0.2); } + +.flash { + background-color: #ffffff; +} diff --git a/data/camera.ui b/data/camera.ui index 83adf8e..13140cf 100644 --- a/data/camera.ui +++ b/data/camera.ui @@ -59,6 +59,15 @@ + + + start + flash-enabled-symbolic + + + diff --git a/data/flash-disabled-symbolic.svg b/data/flash-disabled-symbolic.svg new file mode 100644 index 0000000..e32ee04 --- /dev/null +++ b/data/flash-disabled-symbolic.svg @@ -0,0 +1,32 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/data/flash-enabled-symbolic.svg b/data/flash-enabled-symbolic.svg new file mode 100644 index 0000000..39b53ab --- /dev/null +++ b/data/flash-enabled-symbolic.svg @@ -0,0 +1,31 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/data/org.postmarketos.Megapixels.gresource.xml b/data/org.postmarketos.Megapixels.gresource.xml index 0243024..f4a3c8f 100644 --- a/data/org.postmarketos.Megapixels.gresource.xml +++ b/data/org.postmarketos.Megapixels.gresource.xml @@ -4,11 +4,13 @@ camera.ui controls-popover.ui camera.css - switch-camera-symbolic.svg - shutter-button-symbolic.svg + flash-disabled-symbolic.svg + flash-enabled-symbolic.svg folder-symbolic.svg settings-symbolic.svg + shutter-button-symbolic.svg shutter-symbolic.svg + switch-camera-symbolic.svg blit.vert blit.frag solid.vert diff --git a/meson.build b/meson.build index 0e8ff55..727b2fa 100644 --- a/meson.build +++ b/meson.build @@ -34,18 +34,19 @@ if get_option('tiffcfapattern') endif executable('megapixels', - 'src/main.c', - 'src/ini.c', - 'src/gles2_debayer.c', - 'src/gl_util.c', 'src/camera.c', - 'src/device.c', - 'src/pipeline.c', 'src/camera_config.c', + 'src/device.c', + 'src/flash.c', + 'src/gl_util.c', + 'src/gles2_debayer.c', + 'src/ini.c', 'src/io_pipeline.c', + 'src/main.c', + 'src/matrix.c', + 'src/pipeline.c', 'src/process_pipeline.c', 'src/zbar_pipeline.c', - 'src/matrix.c', resources, include_directories: 'src/', dependencies: [gtkdep, libm, tiff, zbar, threads, epoxy], diff --git a/src/camera_config.c b/src/camera_config.c index f8ecf05..84d9e05 100644 --- a/src/camera_config.c +++ b/src/camera_config.c @@ -206,6 +206,10 @@ config_ini_handler(void *user, const char *section, const char *name, cc->iso_min = strtod(value, NULL); } else if (strcmp(name, "iso-max") == 0) { cc->iso_max = strtod(value, NULL); + } else if (strcmp(name, "flash-path") == 0) { + strcpy(cc->flash_path, value); + } else if (strcmp(name, "flash-display") == 0) { + cc->flash_display = strcmp(value, "true") == 0; } else { g_printerr("Unknown key '%s' in [%s]\n", name, section); exit(1); diff --git a/src/camera_config.h b/src/camera_config.h index f506af8..55a0fcb 100644 --- a/src/camera_config.h +++ b/src/camera_config.h @@ -41,6 +41,9 @@ struct mp_camera_config { double fnumber; int iso_min; int iso_max; + + char flash_path[260]; + bool flash_display; }; bool mp_load_config(); diff --git a/src/flash.c b/src/flash.c new file mode 100644 index 0000000..930bcd8 --- /dev/null +++ b/src/flash.c @@ -0,0 +1,141 @@ +#include "flash.h" + +#include "gtk/gtk.h" +#include +#include +#include +#include + +typedef enum { + FLASH_TYPE_LED, + FLASH_TYPE_DISPLAY, +} FlashType; + +typedef struct { + char path[260]; + int fd; +} MPLEDFlash; + +typedef struct { + GtkWidget *window; +} MPDisplayFlash; + +struct _MPFlash { + FlashType type; + + union { + MPLEDFlash led; + MPDisplayFlash display; + }; +}; + +MPFlash * +mp_led_flash_from_path(const char * path) +{ + MPFlash *flash = malloc(sizeof(MPFlash)); + flash->type = FLASH_TYPE_LED; + + strncpy(flash->led.path, path, 259); + + char mpath[275]; + snprintf(mpath, 275, "%s/flash_strobe", path); + flash->led.fd = open(mpath, O_WRONLY); + if (flash->led.fd == -1) { + g_printerr("Failed to open %s\n", mpath); + free(flash); + return NULL; + } + + return flash; +} + +static bool create_display(MPFlash *flash) +{ + // Create a full screen full white window as a flash + GtkWidget *window = gtk_window_new(); + // gtk_window_set_accept_focus(GTK_WINDOW(flash->display.window), FALSE); + gtk_window_set_decorated(GTK_WINDOW(window), FALSE); + gtk_window_fullscreen(GTK_WINDOW(window)); + + GtkStyleContext *context; + context = gtk_widget_get_style_context(window); + gtk_style_context_add_class(context, "flash"); + + flash->display.window = window; + + return false; +} + +MPFlash * +mp_create_display_flash() +{ + MPFlash *flash = malloc(sizeof(MPFlash)); + flash->type = FLASH_TYPE_DISPLAY; + flash->display.window = NULL; + + // All GTK functions must be called on the main thread + g_main_context_invoke(NULL, (GSourceFunc)create_display, flash); + + return flash; +} + +static bool flash_free(MPFlash *flash) +{ + gtk_window_destroy(GTK_WINDOW(flash->display.window)); + free(flash); + return false; +} + +void +mp_flash_free(MPFlash *flash) +{ + switch (flash->type) { + case FLASH_TYPE_LED: + close(flash->led.fd); + free(flash); + break; + case FLASH_TYPE_DISPLAY: + g_main_context_invoke(NULL, (GSourceFunc)flash_free, flash); + break; + } +} + +static bool show_flash_window(MPFlash *flash) +{ + gtk_widget_show(flash->display.window); + return false; +} + +void +mp_flash_enable(MPFlash *flash) +{ + switch (flash->type) { + case FLASH_TYPE_LED: + lseek(flash->led.fd, 0, SEEK_SET); + dprintf(flash->led.fd, "1\n"); + break; + case FLASH_TYPE_DISPLAY: + g_main_context_invoke(NULL, (GSourceFunc)show_flash_window, flash); + break; + } +} + +static bool hide_flash_window(MPFlash *flash) +{ + gtk_widget_hide(flash->display.window); + return false; +} + +void +mp_flash_disable(MPFlash *flash) +{ + switch (flash->type) { + case FLASH_TYPE_LED: + // Flash gets reset automatically + break; + case FLASH_TYPE_DISPLAY: + g_main_context_invoke(NULL, (GSourceFunc)hide_flash_window, flash); + break; + } +} + diff --git a/src/flash.h b/src/flash.h new file mode 100644 index 0000000..9ff8813 --- /dev/null +++ b/src/flash.h @@ -0,0 +1,8 @@ +typedef struct _MPFlash MPFlash; + +MPFlash *mp_led_flash_from_path(const char *path); +MPFlash *mp_create_display_flash(); +void mp_flash_free(MPFlash *flash); + +void mp_flash_enable(MPFlash *flash); +void mp_flash_disable(MPFlash *flash); diff --git a/src/io_pipeline.c b/src/io_pipeline.c index 2e0331a..68bdefb 100644 --- a/src/io_pipeline.c +++ b/src/io_pipeline.c @@ -1,16 +1,17 @@ #include "io_pipeline.h" -#include "device.h" #include "camera.h" +#include "device.h" +#include "flash.h" #include "pipeline.h" #include "process_pipeline.h" -#include -#include -#include -#include -#include #include +#include +#include +#include #include +#include +#include struct media_link_info { unsigned int source_entity_id; @@ -29,6 +30,8 @@ struct camera_info { MPCamera *camera; + MPFlash *flash; + int gain_ctrl; int gain_max; @@ -90,6 +93,8 @@ struct control_state { static struct control_state desired_controls = {}; static struct control_state current_controls = {}; +static bool flash_enabled = false; + static bool want_focus = false; static MPPipeline *pipeline; @@ -216,6 +221,15 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) info->gain_ctrl = V4L2_CID_ANALOGUE_GAIN; info->gain_max = control.max; } + + // Setup flash + if (config->flash_path[0]) { + info->flash = mp_led_flash_from_path(config->flash_path); + } else if (config->flash_display) { + info->flash = mp_create_display_flash(); + } else { + info->flash = NULL; + } } } @@ -340,6 +354,11 @@ capture(MPPipeline *pipeline, const void *data) mp_camera_start_capture(info->camera); + // Enable flash + if (info->flash && flash_enabled) { + mp_flash_enable(info->flash); + } + update_process_pipeline(); mp_process_pipeline_capture(); @@ -490,6 +509,11 @@ on_frame(MPBuffer buffer, void * _data) mp_camera_start_capture(info->camera); + // Disable flash + if (info->flash) { + mp_flash_disable(info->flash); + } + update_process_pipeline(); } } @@ -571,8 +595,12 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) desired_controls.exposure = state->exposure; has_changed = - has_changed || memcmp(&previous_desired, &desired_controls, - sizeof(struct control_state)) != 0; + has_changed + || memcmp(&previous_desired, &desired_controls, + sizeof(struct control_state)) != 0 + || flash_enabled != state->flash_enabled; + + flash_enabled = state->flash_enabled; } assert(has_changed); diff --git a/src/io_pipeline.h b/src/io_pipeline.h index a8a0f2c..6e5ff28 100644 --- a/src/io_pipeline.h +++ b/src/io_pipeline.h @@ -19,6 +19,7 @@ struct mp_io_pipeline_state { int exposure; bool save_dng; + bool flash_enabled; }; void mp_io_pipeline_start(); diff --git a/src/main.c b/src/main.c index d0c66fb..cf653a2 100644 --- a/src/main.c +++ b/src/main.c @@ -52,6 +52,8 @@ static int exposure; static bool has_auto_focus_continuous; static bool has_auto_focus_start; +static bool flash_enabled = true; + static bool setting_save_dng; static MPProcessPipelineBuffer *current_preview_buffer = NULL; @@ -107,6 +109,7 @@ update_io_pipeline() .exposure_is_manual = exposure_is_manual, .exposure = exposure, .save_dng = setting_save_dng, + .flash_enabled = flash_enabled, }; mp_io_pipeline_update_state(&io_state); } @@ -758,6 +761,16 @@ open_shutter_controls(GtkWidget *button, gpointer user_data) open_controls(button, "Shutter", 1.0, 360.0, exposure, !exposure_is_manual, set_shutter, set_shutter_auto); } +static void +flash_button_clicked(GtkWidget *button, gpointer user_data) +{ + flash_enabled = !flash_enabled; + update_io_pipeline(); + + const char * icon_name = flash_enabled ? "flash-enabled-symbolic" : "flash-disabled-symbolic"; + gtk_button_set_icon_name(GTK_BUTTON(button), icon_name); +} + static void on_realize(GtkWidget *window, gpointer *data) { @@ -895,6 +908,7 @@ activate(GtkApplication *app, gpointer data) GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window")); GtkWidget *iso_button = GTK_WIDGET(gtk_builder_get_object(builder, "iso-controls-button")); GtkWidget *shutter_button = GTK_WIDGET(gtk_builder_get_object(builder, "shutter-controls-button")); + GtkWidget *flash_button = GTK_WIDGET(gtk_builder_get_object(builder, "flash-controls-button")); GtkWidget *setting_dng_button = GTK_WIDGET(gtk_builder_get_object(builder, "setting-raw")); preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview")); main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack")); @@ -916,6 +930,7 @@ activate(GtkApplication *app, gpointer data) g_signal_connect(iso_button, "clicked", G_CALLBACK(open_iso_controls), NULL); g_signal_connect(shutter_button, "clicked", G_CALLBACK(open_shutter_controls), NULL); + g_signal_connect(flash_button, "clicked", G_CALLBACK(flash_button_clicked), NULL); // Setup actions create_simple_action(app, "capture", G_CALLBACK(run_capture_action));