From 0ab2c540d442997360830fb1ab76284c318c28ca Mon Sep 17 00:00:00 2001
From: pommicket <pommicket@gmail.com>
Date: Wed, 19 Feb 2025 18:55:55 -0500
Subject: flash effects

---
 camera.c    | 16 ++++++++++++----
 camera.h    |  4 ++--
 main.c      | 49 +++++++++++++++++++++++++++++--------------------
 meson.build | 10 ++++++++--
 4 files changed, 51 insertions(+), 28 deletions(-)

diff --git a/camera.c b/camera.c
index f064073..e82c29a 100644
--- a/camera.c
+++ b/camera.c
@@ -298,16 +298,24 @@ static uint8_t *camera_curr_frame(Camera *camera) {
 	assert(camera->userp_frames[camera->curr_frame_idx]);
 	return camera->userp_frames[camera->curr_frame_idx];
 }
-void camera_save_jpg(Camera *camera, const char *name, int quality) {
+bool camera_save_jpg(Camera *camera, const char *name, int quality) {
 	uint8_t *frame = camera_curr_frame(camera);
 	if (frame) {
-		stbi_write_jpg(name, camera_frame_width(camera), camera_frame_height(camera), 3, frame, quality);
+		uint32_t frame_width = camera_frame_width(camera);
+		uint32_t frame_height = camera_frame_height(camera);
+		return stbi_write_jpg(name, frame_width, frame_height, 3, frame, quality) != 0;
+	} else {
+		return false;
 	}
 }
-void camera_save_png(Camera *camera, const char *name) {
+bool camera_save_png(Camera *camera, const char *name) {
 	uint8_t *frame = camera_curr_frame(camera);
 	if (frame) {
-		stbi_write_png(name, camera_frame_width(camera), camera_frame_height(camera), 3, frame, camera_frame_width(camera) * 3);
+		uint32_t frame_width = camera_frame_width(camera);
+		uint32_t frame_height = camera_frame_height(camera);
+		return stbi_write_png(name, frame_width, frame_height, 3, frame, frame_width * 3) != 0;
+	} else {
+		return false;
 	}
 }
 bool camera_next_frame(Camera *camera) {
diff --git a/camera.h b/camera.h
index 4888729..5fe1c4d 100644
--- a/camera.h
+++ b/camera.h
@@ -103,8 +103,8 @@ PictureFormat camera_closest_resolution(Camera *camera, uint32_t pixfmt, int32_t
 int32_t camera_frame_width(Camera *camera);
 int32_t camera_frame_height(Camera *camera);
 PictureFormat camera_picture_format(Camera *camera);
-void camera_save_jpg(Camera *camera, const char *path, int quality);
-void camera_save_png(Camera *camera, const char *path);
+bool camera_save_jpg(Camera *camera, const char *path, int quality);
+bool camera_save_png(Camera *camera, const char *path);
 bool camera_next_frame(Camera *camera);
 void camera_update_gl_textures(Camera *camera, const GLuint textures[3]);
 const char *camera_name(Camera *camera);
diff --git a/main.c b/main.c
index 6e7b58d..52ecdd6 100644
--- a/main.c
+++ b/main.c
@@ -1,8 +1,6 @@
 /*
 TODO
--set saved image format
 -add support for more pixfmts
--screen effect when picture is taken
 -view previous pictures (thumbnails)
 -click in menus
 -left/right in resolution menu
@@ -381,6 +379,7 @@ void main() {\n\
 in vec2 tex_coord;\n\
 uniform sampler2D u_sampler;\n\
 uniform int u_pixel_format;\n\
+uniform float u_flash;\n\
 uniform float u_opacity;\n\
 void main() {\n\
 	vec3 color;\n\
@@ -396,7 +395,7 @@ void main() {\n\
 		color = texture2D(u_sampler, tex_coord).xyz;\n\
 		break;\n\
 	}\n\
-	gl_FragColor = vec4(color, opacity);\n\
+	gl_FragColor = vec4(mix(color, vec3(1.0), u_flash), opacity);\n\
 }\n\
 ";
 	char err[256] = {0};
@@ -410,6 +409,7 @@ void main() {\n\
 	gl.GenVertexArrays(1, &vao);
 	const GLuint u_sampler = gl.GetUniformLocation(program, "u_sampler");
 	const GLuint u_offset = gl.GetUniformLocation(program, "u_offset");
+	const GLuint u_flash = gl.GetUniformLocation(program, "u_flash");
 	const GLuint u_pixel_format = gl.GetUniformLocation(program, "u_pixel_format");
 	const GLuint u_scale = gl.GetUniformLocation(program, "u_scale");
 	const GLuint u_opacity = gl.GetUniformLocation(program, "u_opacity");
@@ -508,6 +508,7 @@ void main() {\n\
 		if (!camera_open(state->camera))
 			return EXIT_FAILURE;
 	}
+	double flash_time = INFINITY;
 	while(true) {
 		struct udev_device *dev = NULL;
 		while (udev_monitor && (dev = udev_monitor_receive_device(udev_monitor))) {
@@ -525,24 +526,29 @@ void main() {\n\
 		while (SDL_PollEvent(&event)) {
 			if (event.type == SDL_QUIT) goto quit;
 			if (event.type == SDL_KEYDOWN) switch (event.key.keysym.sym) {
-			case SDLK_SPACE: {
-				time_t t = time(NULL);
-				struct tm *tm = localtime(&t);
-				static char name[64];
-				strftime(name, sizeof name, "%Y-%m-%d-%H-%M-%S", tm);
-				sprintf(name + strlen(name), ".%s", image_format_extensions[state->image_format]);
-				switch (state->image_format) {
-				case IMG_FMT_JPEG:
-					camera_save_jpg(state->camera, name, 90);
-					break;
-				case IMG_FMT_PNG:
-					camera_save_png(state->camera, name);
-					break;
-				case IMG_FMT_COUNT:
-					assert(false);
-					break;
+			case SDLK_SPACE:
+				if (state->camera) {
+					time_t t = time(NULL);
+					struct tm *tm = localtime(&t);
+					static char name[64];
+					strftime(name, sizeof name, "%Y-%m-%d-%H-%M-%S", tm);
+					sprintf(name + strlen(name), ".%s", image_format_extensions[state->image_format]);
+					bool success = false;
+					switch (state->image_format) {
+					case IMG_FMT_JPEG:
+						success = camera_save_jpg(state->camera, name, 90);
+						break;
+					case IMG_FMT_PNG:
+						success = camera_save_png(state->camera, name);
+						break;
+					case IMG_FMT_COUNT:
+						assert(false);
+						break;
+					}
+					if (success)
+						flash_time = 0;
 				}
-				} break;
+				break;
 			case SDLK_ESCAPE:
 				if (state->curr_menu == MENU_NONE) {
 					state->curr_menu = MENU_MAIN;
@@ -779,6 +785,7 @@ void main() {\n\
 		clock_gettime(CLOCK_MONOTONIC, &ts);
 		double curr_time = (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9;
 		double frame_time = curr_time - last_time;
+		flash_time += frame_time;
 		last_time = curr_time;
 		
 		gl.UseProgram(program);
@@ -824,7 +831,9 @@ void main() {\n\
 		gl.Disable(GL_BLEND);
 		gl.BindBuffer(GL_ARRAY_BUFFER, vbo);
 		gl.BindVertexArray(vao);
+		gl.Uniform1f(u_flash, expf(-flash_time * 3));
 		gl.DrawArrays(GL_TRIANGLES, 0, 6);
+		gl.Uniform1f(u_flash, 0);
 		if (state->curr_menu) {
 			gl.Enable(GL_BLEND);
 			gl.ActiveTexture(GL_TEXTURE0);
diff --git a/meson.build b/meson.build
index b4acbd6..7d721bf 100644
--- a/meson.build
+++ b/meson.build
@@ -4,7 +4,7 @@ project('camlet',
 		'warning_level=3',
 	]
 )
-
+cc = meson.get_compiler('c')
 v4l2 = dependency('libv4l2')
 sdl2 = dependency('SDL2')
 sdl2_ttf = dependency('SDL2_ttf')
@@ -12,9 +12,15 @@ udev = dependency('libudev')
 gl = dependency('GL')
 sodium = dependency('libsodium')
 fontconfig = dependency('fontconfig')
+m_dep = cc.find_library('m', required: false)
+if m_dep.found()
+	add_project_link_arguments('-lm', language: 'c')
+endif
 if get_option('debug')
 	debug_def = '-DDEBUG=1'
 else
 	debug_def = '-DDEBUG=0'
 endif
-executable('camlet', 'main.c', 'camera.c', '3rd_party/stb_image_write.c', dependencies: [v4l2, sdl2, sdl2_ttf, gl, udev, sodium, fontconfig], c_args: ['-Wno-unused-function', '-Wno-format-truncation', '-Wshadow', debug_def])
+executable('camlet', 'main.c', 'camera.c', '3rd_party/stb_image_write.c',
+	dependencies: [v4l2, sdl2, sdl2_ttf, gl, udev, sodium, fontconfig],
+	c_args: ['-Wno-unused-function', '-Wno-format-truncation', '-Wshadow', debug_def])
-- 
cgit v1.2.3