From 64b75bcbe58a9b673a18087d93018192d1660490 Mon Sep 17 00:00:00 2001 From: Benjamin Schaaf Date: Tue, 4 May 2021 00:06:55 +1000 Subject: [PATCH] Preview rotation follows device --- data/blit.vert | 4 +- src/io_pipeline.c | 7 ++- src/io_pipeline.h | 2 + src/main.c | 114 ++++++++++++++++++++++++++++++++++++++--- src/process_pipeline.c | 28 ++++++++-- src/process_pipeline.h | 2 + 6 files changed, 142 insertions(+), 15 deletions(-) diff --git a/data/blit.vert b/data/blit.vert index 557c872..e6e75d4 100644 --- a/data/blit.vert +++ b/data/blit.vert @@ -5,10 +5,12 @@ precision mediump float; attribute vec2 vert; attribute vec2 tex_coord; +uniform mat3 transform; + varying vec2 uv; void main() { uv = tex_coord; - gl_Position = vec4(vert, 0, 1); + gl_Position = vec4(transform * vec3(vert, 1), 1); } diff --git a/src/io_pipeline.c b/src/io_pipeline.c index 08edd7e..1281df3 100644 --- a/src/io_pipeline.c +++ b/src/io_pipeline.c @@ -75,6 +75,8 @@ static int captures_remaining = 0; static int preview_width; static int preview_height; +static int device_rotation; + struct control_state { bool gain_is_manual; int gain; @@ -275,6 +277,7 @@ update_process_pipeline() .burst_length = burst_length, .preview_width = preview_width, .preview_height = preview_height, + .device_rotation = device_rotation, .gain_is_manual = current_controls.gain_is_manual, .gain = current_controls.gain, .gain_max = info->gain_max, @@ -521,11 +524,13 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) has_changed = has_changed || burst_length != state->burst_length || preview_width != state->preview_width || - preview_height != state->preview_height; + preview_height != state->preview_height || + device_rotation != state->device_rotation; burst_length = state->burst_length; preview_width = state->preview_width; preview_height = state->preview_height; + device_rotation = state->device_rotation; if (camera) { struct control_state previous_desired = desired_controls; diff --git a/src/io_pipeline.h b/src/io_pipeline.h index 1814f84..62b0c8b 100644 --- a/src/io_pipeline.h +++ b/src/io_pipeline.h @@ -10,6 +10,8 @@ struct mp_io_pipeline_state { int preview_width; int preview_height; + int device_rotation; + bool gain_is_manual; int gain; diff --git a/src/main.c b/src/main.c index ae486f9..8458daa 100644 --- a/src/main.c +++ b/src/main.c @@ -40,6 +40,8 @@ static MPCameraMode mode; static int preview_width = -1; static int preview_height = -1; +static int device_rotation = 0; + static bool gain_is_manual = false; static int gain; static int gain_max; @@ -95,6 +97,7 @@ update_io_pipeline() .burst_length = burst_length, .preview_width = preview_width, .preview_height = preview_height, + .device_rotation = device_rotation, .gain_is_manual = gain_is_manual, .gain = gain, .exposure_is_manual = exposure_is_manual, @@ -215,6 +218,7 @@ mp_main_capture_completed(GdkTexture *thumb, const char *fname) } static GLuint blit_program; +static GLuint blit_uniform_transform; static GLuint blit_uniform_texture; static GLuint solid_program; static GLuint solid_uniform_color; @@ -247,6 +251,7 @@ preview_realize(GtkGLArea *area) glBindAttribLocation(blit_program, GL_UTIL_TEX_COORD_ATTRIBUTE, "tex_coord"); check_gl(); + blit_uniform_transform = glGetUniformLocation(blit_program, "transform"); blit_uniform_texture = glGetUniformLocation(blit_program, "texture"); GLuint solid_shaders[] = { @@ -266,17 +271,26 @@ preview_realize(GtkGLArea *area) static void position_preview(float *offset_x, float *offset_y, float *size_x, float *size_y) { - int scale = gtk_widget_get_scale_factor(preview); - int preview_height = gtk_widget_get_allocated_height(preview) * scale; - int top_height = gtk_widget_get_allocated_height(preview_top_box) * scale; - int bottom_height = gtk_widget_get_allocated_height(preview_bottom_box) * scale; + int buffer_width, buffer_height; + if (device_rotation == 0 || device_rotation == 180) { + buffer_width = preview_buffer_width; + buffer_height = preview_buffer_height; + } else { + buffer_width = preview_buffer_height; + buffer_height = preview_buffer_width; + } + + int scale_factor = gtk_widget_get_scale_factor(preview); + int top_height = gtk_widget_get_allocated_height(preview_top_box) * scale_factor; + int bottom_height = gtk_widget_get_allocated_height(preview_bottom_box) * scale_factor; int inner_height = preview_height - top_height - bottom_height; - double ratio = preview_buffer_height / (double)preview_buffer_width; + double scale = MIN(preview_width / (float) buffer_width, preview_height / (float) buffer_height); - *offset_x = 0; - *size_x = preview_width; - *size_y = preview_width * ratio; + *size_x = scale * buffer_width; + *size_y = scale * buffer_height; + + *offset_x = (preview_width - *size_x) / 2.0; if (*size_y > inner_height) { *offset_y = (preview_height - *size_y) / 2.0; @@ -314,6 +328,19 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) if (current_preview_buffer) { glUseProgram(blit_program); + GLfloat rotation_list[4] = { 0, -1, 0, 1 }; + int rotation_index = device_rotation / 90; + + GLfloat sin_rot = rotation_list[rotation_index]; + GLfloat cos_rot = rotation_list[(4 + rotation_index - 1) % 4]; + GLfloat matrix[9] = { + cos_rot, sin_rot, 0, + -sin_rot, cos_rot, 0, + 0, 0, 1, + }; + glUniformMatrix3fv(blit_uniform_transform, 1, GL_FALSE, matrix); + check_gl(); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mp_process_pipeline_buffer_get_texture_id(current_preview_buffer)); glUniform1i(blit_uniform_texture, 0); @@ -719,6 +746,62 @@ create_simple_action(GtkApplication *app, const char *name, GCallback callback) return action; } +static void display_config_received(GDBusConnection *conn, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + GVariant *result = g_dbus_connection_call_finish(conn, res, &error); + + if (!result) { + printf("Failed to get display configuration: %s\n", error->message); + return; + } + + GVariant *configs = g_variant_get_child_value(result, 1); + if (g_variant_n_children(configs) == 0) { + return; + } + + GVariant *config = g_variant_get_child_value(configs, 0); + GVariant *rot_config = g_variant_get_child_value(config, 7); + uint32_t rotation_index = g_variant_get_uint32(rot_config); + + assert(rotation_index < 4); + int new_rotation = rotation_index * 90; + + if (new_rotation != device_rotation) { + device_rotation = new_rotation; + update_io_pipeline(); + } +} + +static void update_screen_rotation(GDBusConnection *conn) +{ + g_dbus_connection_call(conn, + "org.gnome.Mutter.DisplayConfig", + "/org/gnome/Mutter/DisplayConfig", + "org.gnome.Mutter.DisplayConfig", + "GetResources", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, + NULL, + (GAsyncReadyCallback)display_config_received, + NULL); +} + +static void on_screen_rotate( + GDBusConnection *conn, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + update_screen_rotation(conn); +} + static void activate(GtkApplication *app, gpointer data) { @@ -779,6 +862,21 @@ activate(GtkApplication *app, gpointer data) const char *quit_accels[] = { "q", "w", NULL }; gtk_application_set_accels_for_action(app, "app.quit", quit_accels); + // Listen for phosh rotation + GDBusConnection *conn = g_application_get_dbus_connection(G_APPLICATION(app)); + g_dbus_connection_signal_subscribe( + conn, + NULL, + "org.gnome.Mutter.DisplayConfig", + "MonitorsChanged", + "/org/gnome/Mutter/DisplayConfig", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + &on_screen_rotate, + NULL, + NULL); + update_screen_rotation(conn); + mp_io_pipeline_start(); gtk_application_add_window(app, GTK_WINDOW(window)); diff --git a/src/process_pipeline.c b/src/process_pipeline.c index 3690a6b..7802ca6 100644 --- a/src/process_pipeline.c +++ b/src/process_pipeline.c @@ -31,6 +31,7 @@ static volatile int frames_processed = 0; static volatile int frames_received = 0; static const struct mp_camera_config *camera; +static int camera_rotation; static MPCameraMode mode; @@ -40,6 +41,8 @@ static int captures_remaining = 0; static int preview_width; static int preview_height; +static int device_rotation; + static int output_buffer_width = -1; static int output_buffer_height = -1; @@ -370,13 +373,13 @@ process_image_for_capture(const uint8_t *image, int count) TIFFSetField(tif, TIFFTAG_MAKE, mp_get_device_make()); TIFFSetField(tif, TIFFTAG_MODEL, mp_get_device_model()); uint16_t orientation; - if (camera->rotate == 0) { + if (camera_rotation == 0) { orientation = camera->mirrored ? ORIENTATION_TOPRIGHT : ORIENTATION_TOPLEFT; - } else if (camera->rotate == 90) { + } else if (camera_rotation == 90) { orientation = camera->mirrored ? ORIENTATION_RIGHTBOT : ORIENTATION_LEFTBOT; - } else if (camera->rotate == 180) { + } else if (camera_rotation == 180) { orientation = camera->mirrored ? ORIENTATION_BOTLEFT : ORIENTATION_BOTRIGHT; } else { @@ -583,7 +586,7 @@ process_image(MPPipeline *pipeline, const MPBuffer *buffer) memcpy(image, buffer->data, size); mp_io_pipeline_release_buffer(buffer->index); - MPZBarImage *zbar_image = mp_zbar_image_new(image, mode.pixel_format, mode.width, mode.height, camera->rotate, camera->mirrored); + MPZBarImage *zbar_image = mp_zbar_image_new(image, mode.pixel_format, mode.width, mode.height, camera_rotation, camera->mirrored); mp_zbar_pipeline_process_image(mp_zbar_image_ref(zbar_image)); #ifdef PROFILE_PROCESS @@ -692,10 +695,21 @@ on_output_changed() camera->blacklevel); } +static int +mod(int a, int b) +{ + int r = a % b; + return r < 0 ? r + b : r; +} + static void update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state) { - const bool output_changed = (!mp_camera_mode_is_equivalent(&mode, &state->mode) || preview_width != state->preview_width || preview_height != state->preview_height); + const bool output_changed = + !mp_camera_mode_is_equivalent(&mode, &state->mode) + || preview_width != state->preview_width + || preview_height != state->preview_height + || device_rotation != state->device_rotation; camera = state->camera; mode = state->mode; @@ -703,6 +717,8 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state preview_width = state->preview_width; preview_height = state->preview_height; + device_rotation = state->device_rotation; + burst_length = state->burst_length; // gain_is_manual = state->gain_is_manual; @@ -713,6 +729,8 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state exposure = state->exposure; if (output_changed) { + camera_rotation = mod(camera->rotate - device_rotation, 360); + on_output_changed(); } diff --git a/src/process_pipeline.h b/src/process_pipeline.h index 9d0f69b..2413d35 100644 --- a/src/process_pipeline.h +++ b/src/process_pipeline.h @@ -13,6 +13,8 @@ struct mp_process_pipeline_state { int preview_width; int preview_height; + int device_rotation; + bool gain_is_manual; int gain; int gain_max;