Skip to content

Commit

Permalink
Merge pull request #10 from CodyTolene/ct/feature-3-add-toggle-able-d…
Browse files Browse the repository at this point in the history
…ithering-styles

Add toggle-able dithering styles by pressing OK button.
  • Loading branch information
CodyTolene authored Jul 8, 2023
2 parents 753cc99 + 43ffa6c commit 4ffff0b
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 53 deletions.
6 changes: 3 additions & 3 deletions src-fap/scenes/camera_suite_scene_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ void camera_suite_scene_menu_on_enter(void* context) {

submenu_add_item(
app->submenu,
"Open Camera", // Style: Atkinson
"Open Camera",
SubmenuIndexSceneStyle1,
camera_suite_scene_menu_submenu_callback,
app);
// TODO: Uncomment when style 2 is implemented
// Staged view for the future.
// submenu_add_item(
// app->submenu,
// "Style: Floyd-Steinberg",
// "Test",
// SubmenuIndexSceneStyle2,
// camera_suite_scene_menu_submenu_callback,
// app);
Expand Down
3 changes: 2 additions & 1 deletion src-fap/views/camera_suite_view_guide.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ void camera_suite_view_guide_draw(Canvas* canvas, CameraSuiteViewGuideModel* mod
canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Right = Toggle Dithering");
canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "Up = Contrast Up");
canvas_draw_str_aligned(canvas, 0, 42, AlignLeft, AlignTop, "Down = Contrast Down");
canvas_draw_str_aligned(canvas, 0, 52, AlignLeft, AlignTop, "Center = Take Picture (TODO)");
// TODO: Possibly update to take picture instead.
canvas_draw_str_aligned(canvas, 0, 52, AlignLeft, AlignTop, "Center = Toggle Dither Type");
}

static void camera_suite_view_guide_model_init(CameraSuiteViewGuideModel* const model) {
Expand Down
46 changes: 45 additions & 1 deletion src-fap/views/camera_suite_view_style_1.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
#include <input/input.h>
#include <gui/elements.h>
#include <dolphin/dolphin.h>
#include "../helpers/camera_suite_haptic.h"
#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;

struct CameraSuiteViewStyle1 {
CameraSuiteViewStyle1Callback callback;
Expand Down Expand Up @@ -110,6 +117,22 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) {
furi_assert(context);
CameraSuiteViewStyle1* instance = context;
if(event->type == InputTypeRelease) {
switch(event->key) {
default: // Stop all sounds, reset the LED.
with_view_model(
instance->view,
UartDumpModel * model,
{
UNUSED(model);
camera_suite_play_bad_bump(instance->context);
camera_suite_stop_all_sound(instance->context);
camera_suite_led_set_rgb(instance->context, 0, 0, 0);
},
true);
break;
}
// Send `data` to the ESP32-CAM
} else if(event->type == InputTypePress) {
uint8_t data[1];
switch(event->key) {
case InputKeyBack:
Expand All @@ -133,6 +156,9 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) {
UartDumpModel * model,
{
UNUSED(model);
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);
},
true);
Expand All @@ -145,6 +171,9 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) {
UartDumpModel * model,
{
UNUSED(model);
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);
},
true);
Expand All @@ -157,6 +186,9 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) {
UartDumpModel * model,
{
UNUSED(model);
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);
},
true);
Expand All @@ -169,17 +201,29 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) {
UartDumpModel * model,
{
UNUSED(model);
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);
},
true);
break;
case InputKeyOk:
// TODO: Take picture.
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;
}
with_view_model(
instance->view,
UartDumpModel * model,
{
UNUSED(model);
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);
},
true);
Expand Down
25 changes: 0 additions & 25 deletions src-fap/views/camera_suite_view_style_1.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,6 @@
#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

// const uint8_t _I_DolphinCommon_56x48_0[] = {
// 0x01, 0x00, 0xdf, 0x00, 0x00, 0x1f, 0xfe, 0x0e, 0x05, 0x3f, 0x04, 0x06, 0x78, 0x06, 0x30, 0x20,
// 0xf8, 0x00, 0xc6, 0x12, 0x1c, 0x04, 0x0c, 0x0a, 0x38, 0x08, 0x08, 0x0c, 0x60, 0xc0, 0x21, 0xe0,
// 0x04, 0x0a, 0x18, 0x02, 0x1b, 0x00, 0x18, 0xa3, 0x00, 0x21, 0x90, 0x01, 0x8a, 0x20, 0x02, 0x19,
// 0x80, 0x18, 0x80, 0x64, 0x09, 0x20, 0x89, 0x81, 0x8c, 0x3e, 0x41, 0xe2, 0x80, 0x50, 0x00, 0x43,
// 0x08, 0x01, 0x0c, 0xfc, 0x68, 0x40, 0x61, 0xc0, 0x50, 0x30, 0x00, 0x63, 0xa0, 0x7f, 0x80, 0xc4,
// 0x41, 0x19, 0x07, 0xff, 0x02, 0x06, 0x18, 0x24, 0x03, 0x41, 0xf3, 0x2b, 0x10, 0x19, 0x38, 0x10,
// 0x30, 0x31, 0x7f, 0xe0, 0x34, 0x08, 0x30, 0x19, 0x60, 0x80, 0x65, 0x86, 0x0a, 0x4c, 0x0c, 0x30,
// 0x81, 0xb9, 0x41, 0xa0, 0x54, 0x08, 0xc7, 0xe2, 0x06, 0x8a, 0x18, 0x25, 0x02, 0x21, 0x0f, 0x19,
// 0x88, 0xd8, 0x6e, 0x1b, 0x01, 0xd1, 0x1b, 0x86, 0x39, 0x66, 0x3a, 0xa4, 0x1a, 0x50, 0x06, 0x48,
// 0x18, 0x18, 0xd0, 0x03, 0x01, 0x41, 0x98, 0xcc, 0x60, 0x39, 0x01, 0x49, 0x2d, 0x06, 0x03, 0x50,
// 0xf8, 0x40, 0x3e, 0x02, 0xc1, 0x82, 0x86, 0xc7, 0xfe, 0x0f, 0x28, 0x2c, 0x91, 0xd2, 0x90, 0x9a,
// 0x18, 0x19, 0x3e, 0x6d, 0x73, 0x12, 0x16, 0x00, 0x32, 0x49, 0x72, 0xc0, 0x7e, 0x5d, 0x44, 0xba,
// 0x2c, 0x08, 0xa4, 0xc8, 0x82, 0x06, 0x17, 0xe0, 0x81, 0x90, 0x2a, 0x40, 0x61, 0xe1, 0xa2, 0x44,
// 0x0c, 0x76, 0x2b, 0xe8, 0x89, 0x26, 0x43, 0x83, 0x31, 0x8c, 0x78, 0x0c, 0xb0, 0x48, 0x10, 0x1a,
// 0xe0, 0x00, 0x63,
// };
// const uint8_t* const _I_DolphinCommon_56x48[] = {_I_DolphinCommon_56x48_0};
// const Icon I_DolphinCommon_56x48 = {
// .width = 56,
// .height = 48,
// .frame_count = 1,
// .frame_rate = 0,
// .frames = _I_DolphinCommon_56x48};

typedef struct UartDumpModel UartDumpModel;

struct UartDumpModel {
Expand Down
84 changes: 61 additions & 23 deletions src-firmware/esp32_cam_uart_stream/esp32_cam_uart_stream.ino
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,22 @@ camera_config_t config;
void handleSerialInput();
void initializeCamera();
void processImage(camera_fb_t* fb);
void ditherImage(camera_fb_t* fb);
void ditherImage(camera_fb_t* fb, int dt);
bool isDarkBit(uint8_t bit);

// Serial input flags
bool disableDithering = false;
bool invert = false;
bool rotated = false;
bool stopStream = false;
// Dithering type:
// 0 = Floyd Steinberg (default)
// 1 = Atkinson
int dtType = 0;


void setup() {
Serial.begin(230400);

initializeCamera();
}

Expand All @@ -48,6 +52,7 @@ void loop() {
return;
}

// Frame buffer.
camera_fb_t* fb = esp_camera_fb_get();

if (!fb) {
Expand Down Expand Up @@ -85,11 +90,11 @@ void handleSerialInput() {
case 'c': // Remove contrast.
cameraSensor->set_contrast(cameraSensor, cameraSensor->status.contrast - 1);
break;
case 'D': // Enable dithering.
disableDithering = false;
case 'D': // Use Floyd Steinberg dithering.
dtType = 0;
break;
case 'd': // Disable dithering.
disableDithering = true;
case 'd': // Use Atkinson dithering.
dtType = 1;
break;
case 'M': // Toggle Mirror
cameraSensor->set_hmirror(cameraSensor, !cameraSensor->status.hmirror);
Expand Down Expand Up @@ -149,7 +154,7 @@ void initializeCamera() {

void processImage(camera_fb_t* frameBuffer) {
if (!disableDithering) {
ditherImage(frameBuffer);
ditherImage(frameBuffer, dtType);
}

uint8_t flipper_y = 0;
Expand Down Expand Up @@ -186,22 +191,55 @@ void processImage(camera_fb_t* frameBuffer) {
}

// Dither image.
void ditherImage(camera_fb_t* fb) {
for (int y = 0; y < fb->height - 1; ++y) {
for (int x = 1; x < fb->width - 1; ++x) {
int current = y * fb->width + x;
// Convert to black or white
uint8_t oldpixel = fb->buf[current];
uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
fb->buf[current] = newpixel;
// Compute quantization error
int quant_error = oldpixel - newpixel;
// Propagate the error
fb->buf[current + 1] += quant_error * 7 / 16;
fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 3 / 16;
fb->buf[(y + 1) * fb->width + x] += quant_error * 5 / 16;
fb->buf[(y + 1) * fb->width + x + 1] += quant_error / 16;
}
// @param fb Frame buffer
// @param dt Dithering type:
// 0 = Floyd Steinberg (default)
// 1 = Atkinson
void ditherImage(camera_fb_t* fb, int dt) {
switch (dt) {
default:
case 0: // Floyd Steinberg dithering
for (int y = 0; y < fb->height - 1; ++y) {
for (int x = 1; x < fb->width - 1; ++x) {
int current = y * fb->width + x;
// Convert to black or white
uint8_t oldpixel = fb->buf[current];
uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
fb->buf[current] = newpixel;
// Compute quantization error
int quant_error = oldpixel - newpixel;
// Propagate the error
fb->buf[current + 1] += quant_error * 7 / 16;
fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 3 / 16;
fb->buf[(y + 1) * fb->width + x] += quant_error * 5 / 16;
fb->buf[(y + 1) * fb->width + x + 1] += quant_error / 16;
}
}
break;
case 1: // Atkinson dithering
for (int y = 0; y < fb->height; ++y) {
for (int x = 0; x < fb->width; ++x) {
int current = y * fb->width + x;
uint8_t oldpixel = fb->buf[current];
uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
fb->buf[current] = newpixel;
int quant_error = oldpixel - newpixel;

if (x + 1 < fb->width)
fb->buf[current + 1] += quant_error * 1 / 8;
if (x + 2 < fb->width)
fb->buf[current + 2] += quant_error * 1 / 8;
if (x > 0 && y + 1 < fb->height)
fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 1 / 8;
if (y + 1 < fb->height)
fb->buf[(y + 1) * fb->width + x] += quant_error * 1 / 8;
if (y + 1 < fb->height && x + 1 < fb->width)
fb->buf[(y + 1) * fb->width + x + 1] += quant_error * 1 / 8;
if (y + 2 < fb->height)
fb->buf[(y + 2) * fb->width + x] += quant_error * 1 / 8;
}
}
break;
}
}

Expand Down

0 comments on commit 4ffff0b

Please sign in to comment.