From d4ff1e3a5087b58d36b62ae0b4ff0321ed95562c Mon Sep 17 00:00:00 2001 From: Cody Tolene Date: Thu, 27 Jul 2023 22:25:37 -0500 Subject: [PATCH 1/2] Update '[ESP32] Camera Suite' to v1.1.0 --- .../external/camera_suite/camera_suite.c | 25 +- .../external/camera_suite/camera_suite.h | 10 +- .../helpers/camera_suite_custom_event.h | 21 +- ..._style_1.c => camera_suite_scene_camera.c} | 28 +- .../scenes/camera_suite_scene_config.h | 3 +- .../scenes/camera_suite_scene_menu.c | 26 +- .../scenes/camera_suite_scene_start.c | 2 +- .../scenes/camera_suite_scene_style_2.c | 54 ---- ...w_style_1.c => camera_suite_view_camera.c} | 155 +++++------ ...w_style_1.h => camera_suite_view_camera.h} | 27 +- .../views/camera_suite_view_style_2.c | 249 ------------------ .../views/camera_suite_view_style_2.h | 19 -- 12 files changed, 119 insertions(+), 500 deletions(-) rename applications/external/camera_suite/scenes/{camera_suite_scene_style_1.c => camera_suite_scene_camera.c} (59%) delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene_style_2.c rename applications/external/camera_suite/views/{camera_suite_view_style_1.c => camera_suite_view_camera.c} (72%) rename applications/external/camera_suite/views/{camera_suite_view_style_1.h => camera_suite_view_camera.h} (56%) delete mode 100644 applications/external/camera_suite/views/camera_suite_view_style_2.c delete mode 100644 applications/external/camera_suite/views/camera_suite_view_style_2.h diff --git a/applications/external/camera_suite/camera_suite.c b/applications/external/camera_suite/camera_suite.c index 13ee09c22..cbe7e3d62 100644 --- a/applications/external/camera_suite/camera_suite.c +++ b/applications/external/camera_suite/camera_suite.c @@ -13,7 +13,7 @@ void camera_suite_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -//leave app if back button pressed +// Leave app if back button pressed. bool camera_suite_navigation_event_callback(void* context) { furi_assert(context); CameraSuite* app = context; @@ -25,10 +25,10 @@ CameraSuite* camera_suite_app_alloc() { app->gui = furi_record_open(RECORD_GUI); app->notification = furi_record_open(RECORD_NOTIFICATION); - //Turn backlight on, believe me this makes testing your app easier + // Turn backlight on. notification_message(app->notification, &sequence_display_backlight_on); - //Scene additions + // Scene additions app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -60,17 +60,11 @@ CameraSuite* camera_suite_app_alloc() { CameraSuiteViewIdStartscreen, camera_suite_view_start_get_view(app->camera_suite_view_start)); - app->camera_suite_view_style_1 = camera_suite_view_style_1_alloc(); + app->camera_suite_view_camera = camera_suite_view_camera_alloc(); view_dispatcher_add_view( app->view_dispatcher, - CameraSuiteViewIdScene1, - camera_suite_view_style_1_get_view(app->camera_suite_view_style_1)); - - app->camera_suite_view_style_2 = camera_suite_view_style_2_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - CameraSuiteViewIdScene2, - camera_suite_view_style_2_get_view(app->camera_suite_view_style_2)); + CameraSuiteViewIdCamera, + camera_suite_view_camera_get_view(app->camera_suite_view_camera)); app->camera_suite_view_guide = camera_suite_view_guide_alloc(); view_dispatcher_add_view( @@ -98,9 +92,9 @@ void camera_suite_app_free(CameraSuite* app) { scene_manager_free(app->scene_manager); // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdStartscreen); view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdMenu); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdScene1); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdScene2); + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdCamera); view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdGuide); view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdSettings); submenu_free(app->submenu); @@ -110,8 +104,7 @@ void camera_suite_app_free(CameraSuite* app) { // Free remaining resources camera_suite_view_start_free(app->camera_suite_view_start); - camera_suite_view_style_1_free(app->camera_suite_view_style_1); - camera_suite_view_style_2_free(app->camera_suite_view_style_2); + camera_suite_view_camera_free(app->camera_suite_view_camera); camera_suite_view_guide_free(app->camera_suite_view_guide); button_menu_free(app->button_menu); variable_item_list_free(app->variable_item_list); diff --git a/applications/external/camera_suite/camera_suite.h b/applications/external/camera_suite/camera_suite.h index 2b6a7748b..a8b9825be 100644 --- a/applications/external/camera_suite/camera_suite.h +++ b/applications/external/camera_suite/camera_suite.h @@ -4,9 +4,7 @@ #include "scenes/camera_suite_scene.h" #include "views/camera_suite_view_guide.h" #include "views/camera_suite_view_start.h" -#include "views/camera_suite_view_style_1.h" -#include "views/camera_suite_view_style_2.h" -#include +#include "views/camera_suite_view_camera.h" #include #include #include @@ -29,8 +27,7 @@ typedef struct { SceneManager* scene_manager; VariableItemList* variable_item_list; CameraSuiteViewStart* camera_suite_view_start; - CameraSuiteViewStyle1* camera_suite_view_style_1; - CameraSuiteViewStyle2* camera_suite_view_style_2; + CameraSuiteViewCamera* camera_suite_view_camera; CameraSuiteViewGuide* camera_suite_view_guide; uint32_t orientation; uint32_t haptic; @@ -42,8 +39,7 @@ typedef struct { typedef enum { CameraSuiteViewIdStartscreen, CameraSuiteViewIdMenu, - CameraSuiteViewIdScene1, - CameraSuiteViewIdScene2, + CameraSuiteViewIdCamera, CameraSuiteViewIdGuide, CameraSuiteViewIdSettings, } CameraSuiteViewId; diff --git a/applications/external/camera_suite/helpers/camera_suite_custom_event.h b/applications/external/camera_suite/helpers/camera_suite_custom_event.h index 7727a0de3..4d472d577 100644 --- a/applications/external/camera_suite/helpers/camera_suite_custom_event.h +++ b/applications/external/camera_suite/helpers/camera_suite_custom_event.h @@ -8,20 +8,13 @@ typedef enum { CameraSuiteCustomEventStartRight, CameraSuiteCustomEventStartOk, CameraSuiteCustomEventStartBack, - // Scene events: Camera style 1 - CameraSuiteCustomEventSceneStyle1Up, - CameraSuiteCustomEventSceneStyle1Down, - CameraSuiteCustomEventSceneStyle1Left, - CameraSuiteCustomEventSceneStyle1Right, - CameraSuiteCustomEventSceneStyle1Ok, - CameraSuiteCustomEventSceneStyle1Back, - // Scene events: Camera style 2 - CameraSuiteCustomEventSceneStyle2Up, - CameraSuiteCustomEventSceneStyle2Down, - CameraSuiteCustomEventSceneStyle2Left, - CameraSuiteCustomEventSceneStyle2Right, - CameraSuiteCustomEventSceneStyle2Ok, - CameraSuiteCustomEventSceneStyle2Back, + // Scene events: Camera + CameraSuiteCustomEventSceneCameraUp, + CameraSuiteCustomEventSceneCameraDown, + CameraSuiteCustomEventSceneCameraLeft, + CameraSuiteCustomEventSceneCameraRight, + CameraSuiteCustomEventSceneCameraOk, + CameraSuiteCustomEventSceneCameraBack, // Scene events: Guide CameraSuiteCustomEventSceneGuideUp, CameraSuiteCustomEventSceneGuideDown, diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_style_1.c b/applications/external/camera_suite/scenes/camera_suite_scene_camera.c similarity index 59% rename from applications/external/camera_suite/scenes/camera_suite_scene_style_1.c rename to applications/external/camera_suite/scenes/camera_suite_scene_camera.c index 1aeecec7a..809d9a5c1 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_style_1.c +++ b/applications/external/camera_suite/scenes/camera_suite_scene_camera.c @@ -1,35 +1,35 @@ #include "../camera_suite.h" #include "../helpers/camera_suite_custom_event.h" -#include "../views/camera_suite_view_style_1.h" +#include "../views/camera_suite_view_camera.h" -static void camera_suite_view_style_1_callback(CameraSuiteCustomEvent event, void* context) { +void camera_suite_view_camera_callback(CameraSuiteCustomEvent event, void* context) { furi_assert(context); CameraSuite* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, event); } -void camera_suite_scene_style_1_on_enter(void* context) { +void camera_suite_scene_camera_on_enter(void* context) { furi_assert(context); CameraSuite* app = context; - camera_suite_view_style_1_set_callback( - app->camera_suite_view_style_1, camera_suite_view_style_1_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdScene1); + camera_suite_view_camera_set_callback( + app->camera_suite_view_camera, camera_suite_view_camera_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdCamera); } -bool camera_suite_scene_style_1_on_event(void* context, SceneManagerEvent event) { +bool camera_suite_scene_camera_on_event(void* context, SceneManagerEvent event) { CameraSuite* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case CameraSuiteCustomEventSceneStyle1Left: - case CameraSuiteCustomEventSceneStyle1Right: - case CameraSuiteCustomEventSceneStyle1Up: - case CameraSuiteCustomEventSceneStyle1Down: - case CameraSuiteCustomEventSceneStyle1Ok: + case CameraSuiteCustomEventSceneCameraLeft: + case CameraSuiteCustomEventSceneCameraRight: + case CameraSuiteCustomEventSceneCameraUp: + case CameraSuiteCustomEventSceneCameraDown: + case CameraSuiteCustomEventSceneCameraOk: // Do nothing. break; - case CameraSuiteCustomEventSceneStyle1Back: + case CameraSuiteCustomEventSceneCameraBack: notification_message(app->notification, &sequence_reset_red); notification_message(app->notification, &sequence_reset_green); notification_message(app->notification, &sequence_reset_blue); @@ -46,7 +46,7 @@ bool camera_suite_scene_style_1_on_event(void* context, SceneManagerEvent event) return consumed; } -void camera_suite_scene_style_1_on_exit(void* context) { +void camera_suite_scene_camera_on_exit(void* context) { CameraSuite* app = context; UNUSED(app); } diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_config.h b/applications/external/camera_suite/scenes/camera_suite_scene_config.h index fca73ae16..2cb9245ef 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_config.h +++ b/applications/external/camera_suite/scenes/camera_suite_scene_config.h @@ -1,6 +1,5 @@ ADD_SCENE(camera_suite, start, Start) ADD_SCENE(camera_suite, menu, Menu) -ADD_SCENE(camera_suite, style_1, Style_1) -ADD_SCENE(camera_suite, style_2, Style_2) +ADD_SCENE(camera_suite, camera, Camera) ADD_SCENE(camera_suite, guide, Guide) ADD_SCENE(camera_suite, settings, Settings) \ No newline at end of file diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_menu.c b/applications/external/camera_suite/scenes/camera_suite_scene_menu.c index 09c4dade7..ae37e11b6 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_menu.c +++ b/applications/external/camera_suite/scenes/camera_suite_scene_menu.c @@ -1,10 +1,8 @@ #include "../camera_suite.h" enum SubmenuIndex { - /** Atkinson Dithering Algorithm. */ - SubmenuIndexSceneStyle1 = 10, - /** Floyd-Steinberg Dithering Algorithm. */ - SubmenuIndexSceneStyle2, + /** Camera. */ + SubmenuIndexSceneCamera = 10, /** Guide/how-to. */ SubmenuIndexGuide, /** Settings menu. */ @@ -22,16 +20,9 @@ void camera_suite_scene_menu_on_enter(void* context) { submenu_add_item( app->submenu, "Open Camera", - SubmenuIndexSceneStyle1, + SubmenuIndexSceneCamera, camera_suite_scene_menu_submenu_callback, app); - // Staged view for the future. - // submenu_add_item( - // app->submenu, - // "Test", - // SubmenuIndexSceneStyle2, - // camera_suite_scene_menu_submenu_callback, - // app); submenu_add_item( app->submenu, "Guide", SubmenuIndexGuide, camera_suite_scene_menu_submenu_callback, app); submenu_add_item( @@ -56,15 +47,10 @@ bool camera_suite_scene_menu_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(app->view_dispatcher); return true; } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSceneStyle1) { + if(event.event == SubmenuIndexSceneCamera) { scene_manager_set_scene_state( - app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneStyle1); - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStyle_1); - return true; - } else if(event.event == SubmenuIndexSceneStyle2) { - scene_manager_set_scene_state( - app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneStyle2); - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStyle_2); + app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneCamera); + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneCamera); return true; } else if(event.event == SubmenuIndexGuide) { scene_manager_set_scene_state( diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_start.c b/applications/external/camera_suite/scenes/camera_suite_scene_start.c index 12591760c..0dda05ede 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_start.c +++ b/applications/external/camera_suite/scenes/camera_suite_scene_start.c @@ -24,9 +24,9 @@ bool camera_suite_scene_start_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case CameraSuiteCustomEventStartLeft: case CameraSuiteCustomEventStartRight: - break; case CameraSuiteCustomEventStartUp: case CameraSuiteCustomEventStartDown: + // Do nothing. break; case CameraSuiteCustomEventStartOk: scene_manager_next_scene(app->scene_manager, CameraSuiteSceneMenu); diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_style_2.c b/applications/external/camera_suite/scenes/camera_suite_scene_style_2.c deleted file mode 100644 index b74d33784..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_style_2.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../camera_suite.h" -#include "../helpers/camera_suite_custom_event.h" -#include "../helpers/camera_suite_haptic.h" -#include "../helpers/camera_suite_led.h" -#include "../views/camera_suite_view_style_2.h" - -void camera_suite_view_style_2_callback(CameraSuiteCustomEvent event, void* context) { - furi_assert(context); - CameraSuite* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void camera_suite_scene_style_2_on_enter(void* context) { - furi_assert(context); - CameraSuite* app = context; - camera_suite_view_style_2_set_callback( - app->camera_suite_view_style_2, camera_suite_view_style_2_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdScene2); -} - -bool camera_suite_scene_style_2_on_event(void* context, SceneManagerEvent event) { - CameraSuite* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case CameraSuiteCustomEventSceneStyle2Left: - case CameraSuiteCustomEventSceneStyle2Right: - case CameraSuiteCustomEventSceneStyle2Up: - case CameraSuiteCustomEventSceneStyle2Down: - case CameraSuiteCustomEventSceneStyle2Ok: - // Do nothing. - break; - case CameraSuiteCustomEventSceneStyle2Back: - notification_message(app->notification, &sequence_reset_red); - notification_message(app->notification, &sequence_reset_green); - notification_message(app->notification, &sequence_reset_blue); - if(!scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, CameraSuiteSceneMenu)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); - } - consumed = true; - break; - } - } - - return consumed; -} - -void camera_suite_scene_style_2_on_exit(void* context) { - CameraSuite* app = context; - UNUSED(app); -} diff --git a/applications/external/camera_suite/views/camera_suite_view_style_1.c b/applications/external/camera_suite/views/camera_suite_view_camera.c similarity index 72% rename from applications/external/camera_suite/views/camera_suite_view_style_1.c rename to applications/external/camera_suite/views/camera_suite_view_camera.c index 6d16d0231..5d710044b 100644 --- a/applications/external/camera_suite/views/camera_suite_view_style_1.c +++ b/applications/external/camera_suite/views/camera_suite_view_camera.c @@ -8,23 +8,19 @@ #include "../helpers/camera_suite_speaker.h" #include "../helpers/camera_suite_led.h" -static CameraSuiteViewStyle1* current_instance = NULL; -// Dithering type: -// 0 = Floyd Steinberg (default) -// 1 = Atkinson -static int current_dithering = 0; +static CameraSuiteViewCamera* current_instance = NULL; -struct CameraSuiteViewStyle1 { - CameraSuiteViewStyle1Callback callback; +struct CameraSuiteViewCamera { + CameraSuiteViewCameraCallback callback; FuriStreamBuffer* rx_stream; FuriThread* worker_thread; View* view; void* context; }; -void camera_suite_view_style_1_set_callback( - CameraSuiteViewStyle1* instance, - CameraSuiteViewStyle1Callback callback, +void camera_suite_view_camera_set_callback( + CameraSuiteViewCamera* instance, + CameraSuiteViewCameraCallback callback, void* context) { furi_assert(instance); furi_assert(callback); @@ -32,7 +28,29 @@ void camera_suite_view_style_1_set_callback( instance->context = context; } -static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) { +// Function to draw pixels on the canvas based on camera orientation +static void draw_pixel_by_orientation(Canvas* canvas, uint8_t x, uint8_t y, uint8_t orientation) { + switch(orientation) { + case 0: // Camera rotated 0 degrees (right side up, default) + canvas_draw_dot(canvas, x, y); + break; + case 1: // Camera rotated 90 degrees + canvas_draw_dot(canvas, y, FRAME_WIDTH - 1 - x); + break; + case 2: // Camera rotated 180 degrees (upside down) + canvas_draw_dot(canvas, FRAME_WIDTH - 1 - x, FRAME_HEIGHT - 1 - y); + break; + case 3: // Camera rotated 270 degrees + canvas_draw_dot(canvas, FRAME_HEIGHT - 1 - y, x); + break; + default: + break; + } +} + +static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) { + UartDumpModel* model = _model; + // Clear the screen. canvas_set_color(canvas, ColorBlack); @@ -41,60 +59,17 @@ static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) CameraSuite* app = current_instance->context; - // Draw the pixels with rotation. for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) { uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15 uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63 - // Apply rotation - int16_t rotated_x, rotated_y; - switch(app->orientation) { - case 1: // 90 degrees - rotated_x = y; - rotated_y = FRAME_WIDTH - 1 - x; - break; - case 2: // 180 degrees - rotated_x = FRAME_WIDTH - 1 - x; - rotated_y = FRAME_HEIGHT - 1 - y; - break; - case 3: // 270 degrees - rotated_x = FRAME_HEIGHT - 1 - y; - rotated_y = x; - break; - case 0: // 0 degrees - default: - rotated_x = x; - rotated_y = y; - break; - } - for(uint8_t i = 0; i < 8; ++i) { - if((model->pixels[p] & (1 << i)) != 0) { - // Adjust the coordinates based on the new screen dimensions - uint16_t screen_x, screen_y; - switch(app->orientation) { - case 1: // 90 degrees - screen_x = rotated_x; - screen_y = FRAME_HEIGHT - 8 + (rotated_y * 8) + i; - break; - case 2: // 180 degrees - screen_x = FRAME_WIDTH - 8 + (rotated_x * 8) + i; - screen_y = FRAME_HEIGHT - 1 - rotated_y; - break; - case 3: // 270 degrees - screen_x = FRAME_WIDTH - 1 - rotated_x; - screen_y = rotated_y * 8 + i; - break; - case 0: // 0 degrees - default: - screen_x = rotated_x * 8 + i; - screen_y = rotated_y; - break; - } - canvas_draw_dot(canvas, screen_x, screen_y); + if((model->pixels[p] & (1 << (7 - i))) != 0) { + draw_pixel_by_orientation(canvas, (x * 8) + i, y, app->orientation); } } } + // Draw the guide if the camera is not initialized. if(!model->initialized) { canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48); @@ -107,15 +82,15 @@ static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) } } -static void camera_suite_view_style_1_model_init(UartDumpModel* const model) { +static void camera_suite_view_camera_model_init(UartDumpModel* const model) { for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) { model->pixels[i] = 0; } } -static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { +static bool camera_suite_view_camera_input(InputEvent* event, void* context) { furi_assert(context); - CameraSuiteViewStyle1* instance = context; + CameraSuiteViewCamera* instance = context; if(event->type == InputTypeRelease) { switch(event->key) { default: // Stop all sounds, reset the LED. @@ -144,7 +119,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { UartDumpModel * model, { UNUSED(model); - instance->callback(CameraSuiteCustomEventSceneStyle1Back, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraBack, instance->context); }, true); break; @@ -159,7 +134,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Left, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraLeft, instance->context); }, true); break; @@ -174,7 +149,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Right, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraRight, instance->context); }, true); break; @@ -189,7 +164,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Up, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraUp, instance->context); }, true); break; @@ -204,18 +179,13 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Down, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraDown, instance->context); }, true); break; case InputKeyOk: - if(current_dithering == 0) { - data[0] = 'd'; // Update to Floyd Steinberg dithering. - current_dithering = 1; - } else { - data[0] = 'D'; // Update to Atkinson dithering. - current_dithering = 0; - } + // Switch dithering types. + data[0] = 'D'; with_view_model( instance->view, UartDumpModel * model, @@ -224,7 +194,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Ok, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context); }, true); break; @@ -237,16 +207,16 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { return true; } -static void camera_suite_view_style_1_exit(void* context) { +static void camera_suite_view_camera_exit(void* context) { furi_assert(context); } -static void camera_suite_view_style_1_enter(void* context) { +static void camera_suite_view_camera_enter(void* context) { // Check `context` for null. If it is null, abort program, else continue. furi_assert(context); - // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`. - CameraSuiteViewStyle1* instance = (CameraSuiteViewStyle1*)context; + // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. + CameraSuiteViewCamera* instance = (CameraSuiteViewCamera*)context; // Assign the current instance to the global variable current_instance = instance; @@ -259,7 +229,7 @@ static void camera_suite_view_style_1_enter(void* context) { with_view_model( instance->view, UartDumpModel * model, - { camera_suite_view_style_1_model_init(model); }, + { camera_suite_view_camera_model_init(model); }, true); } @@ -267,8 +237,8 @@ static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* cont // Check `context` for null. If it is null, abort program, else continue. furi_assert(context); - // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`. - CameraSuiteViewStyle1* instance = context; + // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. + CameraSuiteViewCamera* instance = context; // If `uartIrqEvent` is `UartIrqEventRXNE`, send the data to the // `rx_stream` and set the `WorkerEventRx` flag. @@ -319,7 +289,7 @@ static void process_ringbuffer(UartDumpModel* model, uint8_t byte) { static int32_t camera_worker(void* context) { furi_assert(context); - CameraSuiteViewStyle1* instance = context; + CameraSuiteViewCamera* instance = context; while(1) { uint32_t events = @@ -348,14 +318,17 @@ static int32_t camera_worker(void* context) { false); } } while(length > 0); + + with_view_model( + instance->view, UartDumpModel * model, { UNUSED(model); }, true); } } return 0; } -CameraSuiteViewStyle1* camera_suite_view_style_1_alloc() { - CameraSuiteViewStyle1* instance = malloc(sizeof(CameraSuiteViewStyle1)); +CameraSuiteViewCamera* camera_suite_view_camera_alloc() { + CameraSuiteViewCamera* instance = malloc(sizeof(CameraSuiteViewCamera)); instance->view = view_alloc(); @@ -364,15 +337,15 @@ CameraSuiteViewStyle1* camera_suite_view_style_1_alloc() { // Set up views view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel)); view_set_context(instance->view, instance); // furi_assert crashes in events without this - view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_style_1_draw); - view_set_input_callback(instance->view, camera_suite_view_style_1_input); - view_set_enter_callback(instance->view, camera_suite_view_style_1_enter); - view_set_exit_callback(instance->view, camera_suite_view_style_1_exit); + view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_camera_draw); + view_set_input_callback(instance->view, camera_suite_view_camera_input); + view_set_enter_callback(instance->view, camera_suite_view_camera_enter); + view_set_exit_callback(instance->view, camera_suite_view_camera_exit); with_view_model( instance->view, UartDumpModel * model, - { camera_suite_view_style_1_model_init(model); }, + { camera_suite_view_camera_model_init(model); }, true); instance->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance); @@ -386,7 +359,7 @@ CameraSuiteViewStyle1* camera_suite_view_style_1_alloc() { return instance; } -void camera_suite_view_style_1_free(CameraSuiteViewStyle1* instance) { +void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) { furi_assert(instance); with_view_model( @@ -395,7 +368,7 @@ void camera_suite_view_style_1_free(CameraSuiteViewStyle1* instance) { free(instance); } -View* camera_suite_view_style_1_get_view(CameraSuiteViewStyle1* instance) { +View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* instance) { furi_assert(instance); return instance->view; -} +} \ No newline at end of file diff --git a/applications/external/camera_suite/views/camera_suite_view_style_1.h b/applications/external/camera_suite/views/camera_suite_view_camera.h similarity index 56% rename from applications/external/camera_suite/views/camera_suite_view_style_1.h rename to applications/external/camera_suite/views/camera_suite_view_camera.h index c460c94ba..5ccbac71a 100644 --- a/applications/external/camera_suite/views/camera_suite_view_style_1.h +++ b/applications/external/camera_suite/views/camera_suite_view_camera.h @@ -19,11 +19,12 @@ #define FRAME_WIDTH 128 #define FRAME_HEIGHT 64 #define FRAME_BIT_DEPTH 1 -#define FRAME_BUFFER_LENGTH \ - (FRAME_WIDTH * FRAME_HEIGHT * FRAME_BIT_DEPTH / 8) // 128*64*1 / 8 = 1024 -#define ROW_BUFFER_LENGTH (FRAME_WIDTH / 8) // 128/8 = 16 -#define RING_BUFFER_LENGTH (ROW_BUFFER_LENGTH + 3) // ROW_BUFFER_LENGTH + Header => 16 + 3 = 19 -#define LAST_ROW_INDEX (FRAME_BUFFER_LENGTH - ROW_BUFFER_LENGTH) // 1024 - 16 = 1008 +#define FRAME_BUFFER_LENGTH 1024 +#define ROW_BUFFER_LENGTH 16 +#define RING_BUFFER_LENGTH 19 +#define LAST_ROW_INDEX 1008 + +extern const Icon I_DolphinCommon_56x48; typedef struct UartDumpModel UartDumpModel; @@ -35,20 +36,20 @@ struct UartDumpModel { uint8_t row_ringbuffer[RING_BUFFER_LENGTH]; }; -typedef struct CameraSuiteViewStyle1 CameraSuiteViewStyle1; +typedef struct CameraSuiteViewCamera CameraSuiteViewCamera; -typedef void (*CameraSuiteViewStyle1Callback)(CameraSuiteCustomEvent event, void* context); +typedef void (*CameraSuiteViewCameraCallback)(CameraSuiteCustomEvent event, void* context); -void camera_suite_view_style_1_set_callback( - CameraSuiteViewStyle1* camera_suite_view_style_1, - CameraSuiteViewStyle1Callback callback, +void camera_suite_view_camera_set_callback( + CameraSuiteViewCamera* camera_suite_view_camera, + CameraSuiteViewCameraCallback callback, void* context); -CameraSuiteViewStyle1* camera_suite_view_style_1_alloc(); +CameraSuiteViewCamera* camera_suite_view_camera_alloc(); -void camera_suite_view_style_1_free(CameraSuiteViewStyle1* camera_suite_static); +void camera_suite_view_camera_free(CameraSuiteViewCamera* camera_suite_static); -View* camera_suite_view_style_1_get_view(CameraSuiteViewStyle1* camera_suite_static); +View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* camera_suite_static); typedef enum { // Reserved for StreamBuffer internal event diff --git a/applications/external/camera_suite/views/camera_suite_view_style_2.c b/applications/external/camera_suite/views/camera_suite_view_style_2.c deleted file mode 100644 index 0a7ac0b83..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_style_2.c +++ /dev/null @@ -1,249 +0,0 @@ -#include "../camera_suite.h" -#include -#include -#include -#include -#include -#include "../helpers/camera_suite_haptic.h" -#include "../helpers/camera_suite_speaker.h" -#include "../helpers/camera_suite_led.h" - -struct CameraSuiteViewStyle2 { - View* view; - CameraSuiteViewStyle2Callback callback; - void* context; -}; - -typedef struct { - int screen_text; -} CameraSuiteViewStyle2Model; - -char buttonText[11][14] = { - "", - "Press Up", - "Press Down", - "Press Left", - "Press Right", - "Press Ok", - "Release Up", - "Release Down", - "Release Left", - "Release Right", - "Release Ok", -}; - -void camera_suite_view_style_2_set_callback( - CameraSuiteViewStyle2* instance, - CameraSuiteViewStyle2Callback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} - -void camera_suite_view_style_2_draw(Canvas* canvas, CameraSuiteViewStyle2Model* model) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, "Scene 2: Input Examples"); - canvas_set_font(canvas, FontSecondary); - char* strInput = malloc(15); - strcpy(strInput, buttonText[model->screen_text]); - canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, strInput); - free(strInput); -} - -static void camera_suite_view_style_2_model_init(CameraSuiteViewStyle2Model* const model) { - model->screen_text = 0; -} - -bool camera_suite_view_style_2_input(InputEvent* event, void* context) { - furi_assert(context); - CameraSuiteViewStyle2* instance = context; - if(event->type == InputTypeRelease) { - switch(event->key) { - case InputKeyBack: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - UNUSED(model); - camera_suite_stop_all_sound(instance->context); - instance->callback(CameraSuiteCustomEventSceneStyle2Back, instance->context); - camera_suite_play_long_bump(instance->context); - }, - true); - break; - case InputKeyUp: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 6; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 0, 255); - }, - true); - break; - case InputKeyDown: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 7; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 255, 0); - }, - true); - break; - case InputKeyLeft: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 8; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 255, 255); - }, - true); - break; - case InputKeyRight: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 9; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 0, 0); - }, - true); - break; - case InputKeyOk: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 10; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 255, 255); - }, - true); - break; - case InputKeyMAX: - break; - } - } else if(event->type == InputTypePress) { - switch(event->key) { - case InputKeyUp: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 1; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyDown: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 2; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyLeft: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 3; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyRight: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 4; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyOk: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 5; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyBack: - case InputKeyMAX: - break; - } - } - - return true; -} - -void camera_suite_view_style_2_exit(void* context) { - furi_assert(context); - CameraSuite* app = context; - camera_suite_stop_all_sound(app); - //camera_suite_led_reset(app); -} - -void camera_suite_view_style_2_enter(void* context) { - furi_assert(context); - dolphin_deed(DolphinDeedPluginStart); -} - -CameraSuiteViewStyle2* camera_suite_view_style_2_alloc() { - CameraSuiteViewStyle2* instance = malloc(sizeof(CameraSuiteViewStyle2)); - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewStyle2Model)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_style_2_draw); - view_set_input_callback(instance->view, camera_suite_view_style_2_input); - //view_set_enter_callback(instance->view, camera_suite_view_style_2_enter); - view_set_exit_callback(instance->view, camera_suite_view_style_2_exit); - - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { camera_suite_view_style_2_model_init(model); }, - true); - - return instance; -} - -void camera_suite_view_style_2_free(CameraSuiteViewStyle2* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* camera_suite_view_style_2_get_view(CameraSuiteViewStyle2* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/camera_suite/views/camera_suite_view_style_2.h b/applications/external/camera_suite/views/camera_suite_view_style_2.h deleted file mode 100644 index d24b895f8..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_style_2.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "../helpers/camera_suite_custom_event.h" - -typedef struct CameraSuiteViewStyle2 CameraSuiteViewStyle2; - -typedef void (*CameraSuiteViewStyle2Callback)(CameraSuiteCustomEvent event, void* context); - -void camera_suite_view_style_2_set_callback( - CameraSuiteViewStyle2* instance, - CameraSuiteViewStyle2Callback callback, - void* context); - -CameraSuiteViewStyle2* camera_suite_view_style_2_alloc(); - -void camera_suite_view_style_2_free(CameraSuiteViewStyle2* camera_suite_static); - -View* camera_suite_view_style_2_get_view(CameraSuiteViewStyle2* boilerpate_static); From e698d6274fe5c7fe364fcbbbe5a8b618da2b71bf Mon Sep 17 00:00:00 2001 From: Cody Tolene Date: Fri, 28 Jul 2023 17:49:42 -0500 Subject: [PATCH 2/2] Update to match changes from here: https://github.com/flipperdevices/flipper-application-catalog/pull/90 --- .../external/camera_suite/application.fam | 4 +- .../external/camera_suite/docs/CHANGELOG.md | 20 ++++++++++ .../external/camera_suite/docs/README.md | 35 ++++++++++++++++++ .../screenshots/camera_preview.png | Bin 0 -> 3342 bytes .../camera_suite/screenshots/guide.png | Bin 0 -> 2233 bytes .../camera_suite/screenshots/main_menu.png | Bin 0 -> 1499 bytes .../camera_suite/screenshots/settings.png | Bin 0 -> 1900 bytes .../camera_suite/screenshots/start_screen.png | Bin 0 -> 1707 bytes 8 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 applications/external/camera_suite/docs/CHANGELOG.md create mode 100644 applications/external/camera_suite/docs/README.md create mode 100644 applications/external/camera_suite/screenshots/camera_preview.png create mode 100644 applications/external/camera_suite/screenshots/guide.png create mode 100644 applications/external/camera_suite/screenshots/main_menu.png create mode 100644 applications/external/camera_suite/screenshots/settings.png create mode 100644 applications/external/camera_suite/screenshots/start_screen.png diff --git a/applications/external/camera_suite/application.fam b/applications/external/camera_suite/application.fam index 40131ae9a..63687833e 100644 --- a/applications/external/camera_suite/application.fam +++ b/applications/external/camera_suite/application.fam @@ -1,9 +1,9 @@ App( - appid="camerasuite", + appid="camera_suite", apptype=FlipperAppType.EXTERNAL, cdefines=["APP_CAMERA_SUITE"], entry_point="camera_suite_app", - fap_author="Cody Tolene", + fap_author="@CodyTolene @Z4urce @leedave", fap_category="GPIO", fap_description="A camera suite application for the Flipper Zero ESP32-CAM module.", fap_icon="icons/camera_suite.png", diff --git a/applications/external/camera_suite/docs/CHANGELOG.md b/applications/external/camera_suite/docs/CHANGELOG.md new file mode 100644 index 000000000..40f20dfc4 --- /dev/null +++ b/applications/external/camera_suite/docs/CHANGELOG.md @@ -0,0 +1,20 @@ +## v1.1 + +- Support and picture stabilization for all camera orientations (0°, 90°, 180°, 270°). +- Rename "Scene 1" to "Camera". No UX changes, strictly internal. +- Clean up unused "Scene 2". This was inaccessible to users previously and unused. +- Add new dithering variations (needs new module firmware, see https://github.com/CodyTolene/Flipper-Zero-Camera-Suite#firmware-installation): + - Add `Jarvis Judice` Ninke Dithering option + - Add `Stucki` dithering option. + - Add ability to toggle dithering options from default `Floyd-Steinberg` and back. +- Resolves issue https://github.com/CodyTolene/Flipper-Zero-Camera-Suite/issues/7 +- Resolves issue https://github.com/CodyTolene/Flipper-Zero-Camera-Suite/pull/17 + +## v1.0 + +- Builds upon Z4urce's software found here (updated 6 months ago): https://github.com/Z4urce/flipperzero-camera +- Utilizes the superb C boilerplate examples laid out by leedave (updated last month): https://github.com/leedave/flipper-zero-fap-boilerplate +- Repurpose and build upon the "[ESP32] Camera" software into the new "[ESP32] Camera Suite" application with new purpose: + - Adding more scene for a guide. + - Adding more scene for saveable settings. + - Add ability to rotate the camera orientation. diff --git a/applications/external/camera_suite/docs/README.md b/applications/external/camera_suite/docs/README.md new file mode 100644 index 000000000..1f436a067 --- /dev/null +++ b/applications/external/camera_suite/docs/README.md @@ -0,0 +1,35 @@ +## Flipper Zero - Camera Suite + +Software to run an ESP32-CAM module on your Flipper Zero device. + +## Software Guide + +### Flipper Zero button mappings: + +🔼 = Contrast Up + +🔽 = Contrast Down + +◀️ = Toggle invert. + +▶️ = Toggle dithering on/off. + +⚪ = Cycle Floyd–Steinberg/Jarvis-Judice-Ninke/Stucki dithering types. + +↩️ = Go back. + +### Camera Suite settings: + +**Orientation** = Rotate the camera image 90 degrees counter-clockwise starting at zero by default (0, 90, 180, 270). This is useful if you have your camera module mounted in a different orientation than the default. + +**Haptic FX** = Toggle haptic feedback on/off. + +**Sound FX** = Toggle sound effects on/off. + +**LED FX** = Toggle LED effects on/off. + +## Links + +Full setup, wiring guide, etc. in the main project README here: https://github.com/CodyTolene/Flipper-Zero-Camera-Suite + +A firmware is needed for the ESP32-CAM module, see here for more information: https://github.com/CodyTolene/Flipper-Zero-Camera-Suite#firmware-installation diff --git a/applications/external/camera_suite/screenshots/camera_preview.png b/applications/external/camera_suite/screenshots/camera_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..5f66ec42c833d6464c028af3aa5be312a7bc09a0 GIT binary patch literal 3342 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 literal 0 HcmV?d00001 diff --git a/applications/external/camera_suite/screenshots/guide.png b/applications/external/camera_suite/screenshots/guide.png new file mode 100644 index 0000000000000000000000000000000000000000..87ed51218cb4f7b5cd39414798c3fa0335e10d28 GIT binary patch literal 2233 zcmaJ@dr*^C7QYc`kq)t|18tPZ&a%E&D>BG4fUf1yvC&!-kf?0al0r-nD9;dxEw+n7 z5Tzg$5?ERD077d>Qh5YBl#o_14v?e}61R#56CSJZB@pd2Dq&(A3RIk<6PF9jY-S`0Q>XGhxv9Vb|V08 z)YyajSc%}VXTxfJmYkIHuc}K!ftz9` z5r@N$n(r?oTnB3INl&Fwsx|hwo?J~1AX>G&T)G54nx$MY1dFbkkz zllvB)ApWg>_e=@K?d0pe%z(W_!jSaQ$L3J|;z=?42TE}Y>Jrplsj%bnIr-+8Nk<8J0tcnbbjLdqu|MKS3Qlm13@6RuoC!uV@z%cM z)if^8gPBI$Ua!o{P6xI@bDXb@j7n_?+ve zrdWm8j{P4ckk{<)waJ>0 ztuu+IcswpcQzGD8_cZ2CBox%i-#|_F#6ib;OC!_*ZFq(I6|R3!pE5YJNRT-={&z^+ z4vx#!M?W*Fhs)}GjWs*80~DzCN4%UOoQ;5Z6`XYN4Q8}y@vC9&KZ}R*Z)NElw;~(k z;7hGk%(NoU@6sX2&5c1!%a0Xu$~Cc^s@d0UzE&}x)J&s_KIzLXZ=W8O9Ukngvy7Ky+WNNMGbDwEEXaZD$*P-MnkDJNCmZ-nTL73^xzb6gtk z-zD2_r$gzMAl|p>5Bn51ixq`n3T`%oSaY#QC@OVq7qh!5p`Ca}MVf=e6#86lqwwMS z)3#(BvgXeHRYX|_mG5iwV5Hp$K+r`-OKZP@UFAiK-nryayQBkOEp|R^~OjhZb zC@6a8m}N?bCC{(v3DCf!E;S2?6<+bzBl{tZNynwXxq882$_XP z;Zm3>lwY*dJUCIEA@n?35cx|U`%klHCS(su%a1#{xljb%j^)ZWA-SGFi{?apE@?Fc z5}utT(3*9)QMpfLB%DnHmw^EV2R7TS@UA4A6%>M^lpj_wuH3apo5u8YA zJ$96)+t8ELedAYUf`R^!wM(oxI$PJ18l17KKCM_fc4{!>^v=)1PYhCctL|x|j)fYC`osW0^i-6eo Li3ddolE3^P)2XN* literal 0 HcmV?d00001 diff --git a/applications/external/camera_suite/screenshots/main_menu.png b/applications/external/camera_suite/screenshots/main_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..3ae802cc2d31175779a3914ea594a0390fe8809e GIT binary patch literal 1499 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z;^_M8K-LVNi#68 z_IkQFhE&XXdpEFeikCz~;O#R-G4s0%YS$hAacik2JIk)d_j9(-{-SW$_nCL#-@}iu z_vgnm?3lj(etq4)nc{QU8Pru67y_9X1ipU!Bk%oAlJP@a_1n8qj0`TE3=UHm7+h@c z)iN>s*?+UH!IFVt84H7g7X!m0e3YHt{}WZ-^Fm{*zc*wv&FK||m;*HKN?tX?`R_ZX z-#H!k`=@r1Z-en>CWa0nh6W8rh7PL~zJ%_*`}FsJvz?j#W8S}M_p{69?6s@kmR;BO zKfa(e4B|{xAhrB9bH%nN|K7j(_x0BNyPw|6<-UDyy7}q%s_Ulri{>5JABk}N+$#PA z?(a?U=S|zQHzF+C^;YhH=zQkluiqJCKF(Qqh70bktnIc9y5|?nkNjQpbML(D{nPiZ zJ9+Ezo=e*6f6iRb95WHgH;?bCe^|b{UR>_M)NOC}B3TLzIamPJyq9|r^{wjOEF`@j z>=@2Ru)o-Va1t~aD5R?X{Q8*ty}9o1zt5M?%lv)6&i3E5`K&vXv>|~EG<`+?UcN2g z4&G3F#`@Yotgwyj_4oJ>mW=K$grlU)?4L8fbt$)<@oF{>gp`j6&hCp$Ddmd|v z>%PbQF%yC5hz*$FKrz{`{4QJn=e|F;zO(6Do{M|W9PxYeH?{`tO^9%@O5tZ{_-w$= zaKHg#_bEAGf0WN@5uLGjp0Z!vWcCcpcoj+5*(16eUq7?14;VU+qnlA=1fL1gQhVu dhzze|{_==S09OD2 literal 0 HcmV?d00001 diff --git a/applications/external/camera_suite/screenshots/settings.png b/applications/external/camera_suite/screenshots/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..32de46d72a82b836a5cb4da71a8b5dee4f9166f3 GIT binary patch literal 1900 zcmaJ?eN<9s7=MZKYcpIso*DVkvK>8oI$2F;8feYUkENChMfs7_tb;XI9GyW1xm>mU z*m9H=qNbH8*;y?6Km%fnEa6%b99y84#gqiU@Tv&zvT$8@I{V|E_rCA@+~?!>Jip&P zz9}x!*$M9i0C0|uibw!p9`cF-4%Q`Rgc5)(j^9R6XaFqy-TGkI{tK4?VDA|nu|6rE zGpJr)`Xb0>K}UXvv~6<#G0sY@<247DiU38;*}5bJGbhmP=FV-+)8g|d=w`FlAjAwM z3oR2mAydfI&fLxj0MFt9II;+Unp7nKtIuHGZ03XT`l|waAlfzsz-|Y?+B6R={e_6E zS%@ie>x%&{FBoZVj{(@=h}?ZO1vI+Y$Si_k^WMfnf2ndbnGLy`&gQnJO}2`K z?KU&SmEt!di*I~MG42i&ACi+|Lz`snw>5?-&CP_ORXUW*m*ew7Oxt_-LS|Tq{JtCA zl9$Pegc#$u{HhvL25#|}3&;6%Vg|c}cYx<630-5$6aWX(H_koksFFQOP4KRnyt-G! zgwtM^SCE6ZIC7I<1LXw%5x?|$tw4z#!+;K#H&xX?&IbI~sh{;N_M7%RSVUo%!}>Yy zkL*FXqqTm-tLv|JLkjwH{UYj}$hW@kQcV+XI&e@4rdP`T^31{9sfPNOQt{QE*(qfu3GeslJG94XPv7 z<{s@t<#f@Aaeq>gMX|i7o*F5a0S!e0IRQm#Dpfk zyE>{TTBv#Js`z?|I1q(iy{eo_I~4Cc6pd<$p9&#NGAf#)7THYH{ZCt8FPJ|RPJpRF zNV~0Fw6aKs5095Quo$v|?$xN{f9ME#XbfABkdd<#w2jBRx70SmVa9kvoB$S?*7hG5 z^fna;y_kImIyK(JlH`Z}M2WYww|udU&4{bB&34twmeNrPo1FZRPn3kFzQ-2oP*b8f zNo09?3yTbs>ftVF`#_4@q-?~AK)`rwohLL=X>uxRCagT*(dk9>vHMfv9}6~TU5PJN zEb~|Rcd6Lfd)Q%(Pn6iXz5xRx3)D>)^U_7f`dV2Ul0b|D|qN&ta(4&<%v4hIa5Zu7{(zk+S<@Rg? z(%5i=T~8;EAVo)@g|RN_cj81oC{h^{VEg>$TGP67Z=?8T&n1>ZH4`?URf*O~WGXhG zl-5c`+$GeM#OkoY4XaArwY-bXxg){5q%B`_2pJW*-1Jl2?B2il1Eb89s4PggaXZK* zIk|f`uQ>TZsPg&~J$xSJjh%{(>gfEiBQcDWp*{iY%4yf+z3{z1UVv%`o0#~u+67#b z-e+f(&@#)aowSDym-jJq`bHrbJ@be-3KSM{cj9`&*y5H;<}dYwVjq;`G>qZH3%T2d zc;>=-2}1?ZMQA~ywjrt*k^*_cjb(hsU@koD^XG7D==Rgy!-2%(L;&zMi8Usi!{!H} q{64_Xm9m$I{(~2S?HsYRU@Cmq(bqmZDnx#tAo`2Ah^7rY3jYS@P22$h literal 0 HcmV?d00001 diff --git a/applications/external/camera_suite/screenshots/start_screen.png b/applications/external/camera_suite/screenshots/start_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe690c58414675c4fff5ef8552d1619c1ad7442 GIT binary patch literal 1707 zcmd5-ZA@EL7(N9Y4h81o7aNo%lW4+1hD2aMDJXJ*Et{V})iTD%uCWM=YT9z4m(4^s zMrjtNp#&~E{j<>yVnx289j4$YTuH{Q4B9#n3M=-)@DA5P>#ZzuuRr~xKW?6LbIy6+ z=Y5~&&AIT}aVRigUjP6gFf$|V4FG)TB?mm}g{*sm!!+zJ&k%?K2>QYMaXya>+7G}k zVP;zDThiv=%&Dcbu^?sg_UM_4!O$DFFjW}gB^?B4}EZ3mo{9VDcZ?{g}dCN2(DugUzi)Oxk5Z){;q?f$x zP?W&umaGvaf;D6`;WPlR`+z+WG~5SK9Zvb(O9N-05^YJOj`@yO$crTy z+3!_@;P2+#jyClmf9o24cd7RQHc%^Ba||_{DA=_fj*eO!{EI>(cZ`S4KIkilCO+=s zU5FWQaSaWPY^p>8skXWJQw`Yu^>18GC}|mX)*Au1;s*|7(NGnxQy44LH?hL{*)sW* z_*cl%=hQY)!ohd5>rhJT-sqR(h+yRL?c?;Vn_ODTkbfcPUkX$~LuL1;CP^h#z1P!7 zsfJYyGAo><*;=YtM4$;yRozO6?hvbo@g=y`zhwHI$Z#2n&8w41F65EV8AV4bqcA;_ zpl-OO$1q@PLyW|ZnQCOkuqu}@ABACjweEdu+Zr2A=XoVf{<6;s$q zt!qK#ifhJMJ4fr1PP&(njjHGMD*G}-TG6l>m8qf&SFAI zI^v-*XjmX=85m8V74Z$L=5DoVcwg~GLcOIL_u;j4y>2y*5}iLMR_%(}cBV;xE{v2!x(A{VPy5Tel(`2{<rJ}h= zgZML1|M0W#zsL>%k%6f2lf1P%y=4KiuG7! z#XFu^5TqOvf7DW0By<_Y`ZpOSoA%8gC2l(>k@cMR$+9r9s2MTDR45L%ceU!-^CLZa zP-I8ywXhi_EIQ1Ty8%DKGD$xV_c`Zb&-yFrj<@;iE>Oc~=WG(-ww{@u^ohqLr*~*l or@|U?99PkQuxC4-{YS-R{fMTn1YbTuzdj)I)#GW`U(P@GC%ZCr_5c6? literal 0 HcmV?d00001