Handle Exif rotation

Does not currently work for SVG and X11 cursors.
This commit is contained in:
Přemysl Eric Janouch 2021-11-26 19:43:36 +01:00
parent 8c89759325
commit bd7f2f8c98
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 113 additions and 9 deletions

View File

@ -57,9 +57,9 @@ typedef enum _FastivIoOrientation {
FastivIoOrientationMirror0 = 2, FastivIoOrientationMirror0 = 2,
FastivIoOrientation180 = 3, FastivIoOrientation180 = 3,
FastivIoOrientationMirror180 = 4, FastivIoOrientationMirror180 = 4,
FastivIoOrientationMirror90 = 5, FastivIoOrientationMirror270 = 5,
FastivIoOrientation90 = 6, FastivIoOrientation90 = 6,
FastivIoOrientationMirror270 = 7, FastivIoOrientationMirror90 = 7,
FastivIoOrientation270 = 8 FastivIoOrientation270 = 8
} FastivIoOrientation; } FastivIoOrientation;

View File

@ -33,12 +33,49 @@ struct _FastivView {
GtkWidget parent_instance; GtkWidget parent_instance;
cairo_surface_t *surface; ///< The loaded image (sequence) cairo_surface_t *surface; ///< The loaded image (sequence)
cairo_surface_t *frame; ///< Current frame within, unreferenced cairo_surface_t *frame; ///< Current frame within, unreferenced
FastivIoOrientation orientation; ///< Current orientation
bool scale_to_fit; bool scale_to_fit;
double scale; double scale;
}; };
G_DEFINE_TYPE(FastivView, fastiv_view, GTK_TYPE_WIDGET) G_DEFINE_TYPE(FastivView, fastiv_view, GTK_TYPE_WIDGET)
static FastivIoOrientation view_left[9] = {
[FastivIoOrientationUnknown] = FastivIoOrientationUnknown,
[FastivIoOrientation0] = FastivIoOrientation270,
[FastivIoOrientationMirror0] = FastivIoOrientationMirror270,
[FastivIoOrientation180] = FastivIoOrientation90,
[FastivIoOrientationMirror180] = FastivIoOrientationMirror90,
[FastivIoOrientationMirror270] = FastivIoOrientationMirror180,
[FastivIoOrientation90] = FastivIoOrientation0,
[FastivIoOrientationMirror90] = FastivIoOrientationMirror0,
[FastivIoOrientation270] = FastivIoOrientation180
};
static FastivIoOrientation view_mirror[9] = {
[FastivIoOrientationUnknown] = FastivIoOrientationUnknown,
[FastivIoOrientation0] = FastivIoOrientationMirror0,
[FastivIoOrientationMirror0] = FastivIoOrientation0,
[FastivIoOrientation180] = FastivIoOrientationMirror180,
[FastivIoOrientationMirror180] = FastivIoOrientation180,
[FastivIoOrientationMirror270] = FastivIoOrientation270,
[FastivIoOrientation90] = FastivIoOrientationMirror270,
[FastivIoOrientationMirror90] = FastivIoOrientation90,
[FastivIoOrientation270] = FastivIoOrientationMirror270
};
static FastivIoOrientation view_right[9] = {
[FastivIoOrientationUnknown] = FastivIoOrientationUnknown,
[FastivIoOrientation0] = FastivIoOrientation90,
[FastivIoOrientationMirror0] = FastivIoOrientationMirror90,
[FastivIoOrientation180] = FastivIoOrientation270,
[FastivIoOrientationMirror180] = FastivIoOrientationMirror270,
[FastivIoOrientationMirror270] = FastivIoOrientationMirror0,
[FastivIoOrientation90] = FastivIoOrientation180,
[FastivIoOrientationMirror90] = FastivIoOrientationMirror180,
[FastivIoOrientation270] = FastivIoOrientation0
};
enum { enum {
PROP_SCALE = 1, PROP_SCALE = 1,
PROP_SCALE_TO_FIT, PROP_SCALE_TO_FIT,
@ -83,13 +120,24 @@ get_surface_dimensions(FastivView *self, double *width, double *height)
cairo_rectangle_t extents = {}; cairo_rectangle_t extents = {};
switch (cairo_surface_get_type(self->surface)) { switch (cairo_surface_get_type(self->surface)) {
case CAIRO_SURFACE_TYPE_IMAGE: case CAIRO_SURFACE_TYPE_IMAGE:
switch (self->orientation) {
case FastivIoOrientation90:
case FastivIoOrientationMirror90:
case FastivIoOrientation270:
case FastivIoOrientationMirror270:
*width = cairo_image_surface_get_height(self->surface);
*height = cairo_image_surface_get_width(self->surface);
break;
default:
*width = cairo_image_surface_get_width(self->surface); *width = cairo_image_surface_get_width(self->surface);
*height = cairo_image_surface_get_height(self->surface); *height = cairo_image_surface_get_height(self->surface);
}
return; return;
case CAIRO_SURFACE_TYPE_RECORDING: case CAIRO_SURFACE_TYPE_RECORDING:
if (!cairo_recording_surface_get_extents(self->surface, &extents)) if (!cairo_recording_surface_get_extents(self->surface, &extents)) {
cairo_recording_surface_ink_extents(self->surface, cairo_recording_surface_ink_extents(self->surface,
&extents.x, &extents.y, &extents.width, &extents.height); &extents.x, &extents.y, &extents.width, &extents.height);
}
*width = extents.width; *width = extents.width;
*height = extents.height; *height = extents.height;
@ -227,7 +275,9 @@ fastiv_view_draw(GtkWidget *widget, cairo_t *cr)
return TRUE; return TRUE;
int w, h; int w, h;
double sw, sh;
get_display_dimensions(self, &w, &h); get_display_dimensions(self, &w, &h);
get_surface_dimensions(self, &sw, &sh);
double x = 0; double x = 0;
double y = 0; double y = 0;
@ -258,11 +308,47 @@ fastiv_view_draw(GtkWidget *widget, cairo_t *cr)
cairo_rectangle(cr, x, y, w, h); cairo_rectangle(cr, x, y, w, h);
cairo_clip(cr); cairo_clip(cr);
cairo_translate(cr, x, y);
cairo_scale(cr, self->scale, self->scale); cairo_scale(cr, self->scale, self->scale);
cairo_set_source_surface( cairo_set_source_surface(cr, self->frame, 0, 0);
cr, self->frame, x / self->scale, y / self->scale);
cairo_matrix_t matrix = {};
cairo_matrix_init_identity(&matrix);
switch (self->orientation) {
case FastivIoOrientation90:
cairo_matrix_rotate(&matrix, -M_PI_2);
cairo_matrix_translate(&matrix, -sw, 0);
break;
case FastivIoOrientation180:
cairo_matrix_scale(&matrix, -1, -1);
cairo_matrix_translate(&matrix, -sw, -sh);
break;
case FastivIoOrientation270:
cairo_matrix_rotate(&matrix, +M_PI_2);
cairo_matrix_translate(&matrix, 0, -sh);
break;
case FastivIoOrientationMirror0:
cairo_matrix_scale(&matrix, -1, +1);
cairo_matrix_translate(&matrix, -sw, 0);
break;
case FastivIoOrientationMirror90:
cairo_matrix_rotate(&matrix, +M_PI_2);
cairo_matrix_scale(&matrix, -1, +1);
cairo_matrix_translate(&matrix, -sw, -sh);
break;
case FastivIoOrientationMirror180:
cairo_matrix_scale(&matrix, +1, -1);
cairo_matrix_translate(&matrix, 0, -sh);
break;
case FastivIoOrientationMirror270:
cairo_matrix_rotate(&matrix, -M_PI_2);
cairo_matrix_scale(&matrix, -1, +1);
default:
break;
}
cairo_pattern_t *pattern = cairo_get_source(cr); cairo_pattern_t *pattern = cairo_get_source(cr);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
// TODO(p): Prescale it ourselves to an off-screen bitmap, gamma-correctly. // TODO(p): Prescale it ourselves to an off-screen bitmap, gamma-correctly.
cairo_pattern_set_filter(pattern, CAIRO_FILTER_GOOD); cairo_pattern_set_filter(pattern, CAIRO_FILTER_GOOD);
@ -352,14 +438,27 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
return set_scale(self, self->scale / SCALE_STEP); return set_scale(self, self->scale / SCALE_STEP);
case GDK_KEY_F: case GDK_KEY_F:
return set_scale_to_fit(self, !self->scale_to_fit); return set_scale_to_fit(self, !self->scale_to_fit);
case GDK_KEY_greater: {
case GDK_KEY_less:
self->orientation = view_left[self->orientation];
gtk_widget_queue_resize(widget);
return TRUE;
case GDK_KEY_equal:
self->orientation = view_mirror[self->orientation];
gtk_widget_queue_draw(widget);
return TRUE;
case GDK_KEY_greater:
self->orientation = view_right[self->orientation];
gtk_widget_queue_resize(widget);
return TRUE;
case GDK_KEY_bracketright:
if (!(self->frame = cairo_surface_get_user_data( if (!(self->frame = cairo_surface_get_user_data(
self->frame, &fastiv_io_key_frame_next))) self->frame, &fastiv_io_key_frame_next)))
self->frame = self->surface; self->frame = self->surface;
gtk_widget_queue_draw(widget); gtk_widget_queue_draw(widget);
return TRUE; return TRUE;
} }
}
return FALSE; return FALSE;
} }
@ -416,5 +515,10 @@ fastiv_view_open(FastivView *self, const gchar *path, GError **error)
self->frame = self->surface = surface; self->frame = self->surface = surface;
set_scale_to_fit(self, true); set_scale_to_fit(self, true);
if ((self->orientation = (uintptr_t) cairo_surface_get_user_data(
self->surface, &fastiv_io_key_orientation)) ==
FastivIoOrientationUnknown)
self->orientation = FastivIoOrientation0;
return TRUE; return TRUE;
} }