From 98bdab443adfbf4d6ecc485874806016771ddbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Tue, 28 Dec 2021 23:10:45 +0100 Subject: [PATCH] Hardcode Exif orientation in thumbnails --- fiv-io.c | 55 +++++++++++++++++++++++++++++++++++++ fiv-io.h | 8 ++++++ fiv-thumbnail.c | 36 ++++++++++++++---------- fiv-view.c | 73 ++++++++++--------------------------------------- 4 files changed, 99 insertions(+), 73 deletions(-) diff --git a/fiv-io.c b/fiv-io.c index 759cb4e..ee597bb 100644 --- a/fiv-io.c +++ b/fiv-io.c @@ -2627,6 +2627,61 @@ fiv_io_save(cairo_surface_t *page, cairo_surface_t *frame, FivIoProfile target, // --- Metadata ---------------------------------------------------------------- +gboolean +fiv_io_orientation_is_sideways(FivIoOrientation orientation) +{ + switch (orientation) { + case FivIoOrientation90: + case FivIoOrientationMirror90: + case FivIoOrientation270: + case FivIoOrientationMirror270: + return TRUE; + default: + return FALSE; + } +} + +cairo_matrix_t +fiv_io_orientation_matrix( + FivIoOrientation orientation, double width, double height) +{ + cairo_matrix_t matrix = {}; + cairo_matrix_init_identity(&matrix); + switch (orientation) { + case FivIoOrientation90: + cairo_matrix_rotate(&matrix, -M_PI_2); + cairo_matrix_translate(&matrix, -width, 0); + break; + case FivIoOrientation180: + cairo_matrix_scale(&matrix, -1, -1); + cairo_matrix_translate(&matrix, -width, -height); + break; + case FivIoOrientation270: + cairo_matrix_rotate(&matrix, +M_PI_2); + cairo_matrix_translate(&matrix, 0, -height); + break; + case FivIoOrientationMirror0: + cairo_matrix_scale(&matrix, -1, +1); + cairo_matrix_translate(&matrix, -width, 0); + break; + case FivIoOrientationMirror90: + cairo_matrix_rotate(&matrix, +M_PI_2); + cairo_matrix_scale(&matrix, -1, +1); + cairo_matrix_translate(&matrix, -width, -height); + break; + case FivIoOrientationMirror180: + cairo_matrix_scale(&matrix, +1, -1); + cairo_matrix_translate(&matrix, 0, -height); + break; + case FivIoOrientationMirror270: + cairo_matrix_rotate(&matrix, -M_PI_2); + cairo_matrix_scale(&matrix, -1, +1); + default: + break; + } + return matrix; +} + FivIoOrientation fiv_io_exif_orientation(const guint8 *tiff, gsize len) { diff --git a/fiv-io.h b/fiv-io.h index 54e2727..35eff32 100644 --- a/fiv-io.h +++ b/fiv-io.h @@ -108,6 +108,14 @@ typedef enum _FivIoOrientation { FivIoOrientation270 = 8 } FivIoOrientation; +/// Returns whether dimensions need to be swapped for rendering. +gboolean fiv_io_orientation_is_sideways(FivIoOrientation orientation); + +/// Returns a rendering matrix for a surface. Dimensions need to be pre-swapped. +cairo_matrix_t fiv_io_orientation_matrix( + FivIoOrientation orientation, double width, double height); + +/// Extracts the orientation field from Exif, if there's any. FivIoOrientation fiv_io_exif_orientation(const guint8 *exif, gsize len); /// Save metadata attached by this module in Exiv2 format. diff --git a/fiv-thumbnail.c b/fiv-thumbnail.c index 1384213..24063f3 100644 --- a/fiv-thumbnail.c +++ b/fiv-thumbnail.c @@ -95,26 +95,33 @@ fiv_thumbnail_get_root(void) // In principle similar to rescale_thumbnail() from fiv-browser.c. static cairo_surface_t * -rescale_thumbnail(cairo_surface_t *thumbnail, double row_height) +adjust_thumbnail(cairo_surface_t *thumbnail, double row_height) { cairo_format_t format = cairo_image_surface_get_format(thumbnail); - int width = cairo_image_surface_get_width(thumbnail); - int height = cairo_image_surface_get_height(thumbnail); + int w = 0, width = cairo_image_surface_get_width(thumbnail); + int h = 0, height = cairo_image_surface_get_height(thumbnail); + + // Hardcode orientation. + FivIoOrientation orientation = (uintptr_t) cairo_surface_get_user_data( + thumbnail, &fiv_io_key_orientation); + cairo_matrix_t matrix = fiv_io_orientation_is_sideways(orientation) + ? fiv_io_orientation_matrix(orientation, (w = height), (h = width)) + : fiv_io_orientation_matrix(orientation, (w = width), (h = height)); double scale_x = 1; double scale_y = 1; - if (width > FIV_THUMBNAIL_WIDE_COEFFICIENT * height) { - scale_x = FIV_THUMBNAIL_WIDE_COEFFICIENT * row_height / width; - scale_y = round(scale_x * height) / height; + if (w > FIV_THUMBNAIL_WIDE_COEFFICIENT * h) { + scale_x = FIV_THUMBNAIL_WIDE_COEFFICIENT * row_height / w; + scale_y = round(scale_x * h) / h; } else { - scale_y = row_height / height; - scale_x = round(scale_y * width) / width; + scale_y = row_height / h; + scale_x = round(scale_y * w) / w; } - if (scale_x == 1 && scale_y == 1) + if (orientation <= FivIoOrientation0 && scale_x == 1 && scale_y == 1) return cairo_surface_reference(thumbnail); - int projected_width = round(scale_x * width); - int projected_height = round(scale_y * height); + int projected_width = round(scale_x * w); + int projected_height = round(scale_y * h); cairo_surface_t *scaled = cairo_image_surface_create( (format == CAIRO_FORMAT_RGB24 || format == CAIRO_FORMAT_RGB30) ? CAIRO_FORMAT_RGB24 @@ -126,13 +133,14 @@ rescale_thumbnail(cairo_surface_t *thumbnail, double row_height) cairo_set_source_surface(cr, thumbnail, 0, 0); cairo_pattern_t *pattern = cairo_get_source(cr); - cairo_pattern_set_filter(pattern, CAIRO_FILTER_BEST); + // CAIRO_FILTER_BEST, for some reason, works bad with CAIRO_FORMAT_RGB30. + cairo_pattern_set_filter(pattern, CAIRO_FILTER_GOOD); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); + cairo_pattern_set_matrix(pattern, &matrix); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); cairo_destroy(cr); - mark_thumbnail_lq(scaled); return scaled; } @@ -260,7 +268,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error) for (int use = max_size; use >= FIV_THUMBNAIL_SIZE_MIN; use--) { cairo_surface_t *scaled = - rescale_thumbnail(surface, fiv_thumbnail_sizes[use].size); + adjust_thumbnail(surface, fiv_thumbnail_sizes[use].size); gchar *path = g_strdup_printf("%s/wide-%s/%s.webp", thumbnails_dir, fiv_thumbnail_sizes[use].thumbnail_spec_name, sum); save_thumbnail(scaled, path, thum); diff --git a/fiv-view.c b/fiv-view.c index 467dd0f..5162f32 100644 --- a/fiv-view.c +++ b/fiv-view.c @@ -55,9 +55,9 @@ struct _FivView { G_DEFINE_TYPE(FivView, fiv_view, GTK_TYPE_WIDGET) -struct size { +typedef struct _Dimensions { double width, height; -}; +} Dimensions; static FivIoOrientation view_left[9] = { [FivIoOrientationUnknown] = FivIoOrientationUnknown, @@ -201,11 +201,11 @@ fiv_view_set_property( } } -static struct size +static Dimensions get_surface_dimensions(FivView *self) { if (!self->image) - return (struct size) {}; + return (Dimensions) {}; cairo_rectangle_t extents = {}; switch (cairo_surface_get_type(self->page)) { @@ -222,65 +222,20 @@ get_surface_dimensions(FivView *self) g_assert_not_reached(); } - switch (self->orientation) { - case FivIoOrientation90: - case FivIoOrientationMirror90: - case FivIoOrientation270: - case FivIoOrientationMirror270: - return (struct size) {extents.height, extents.width}; - default: - return (struct size) {extents.width, extents.height}; - } + if (fiv_io_orientation_is_sideways(self->orientation)) + return (Dimensions) {extents.height, extents.width}; + + return (Dimensions) {extents.width, extents.height}; } static void get_display_dimensions(FivView *self, int *width, int *height) { - struct size surface_dimensions = get_surface_dimensions(self); + Dimensions surface_dimensions = get_surface_dimensions(self); *width = ceil(surface_dimensions.width * self->scale); *height = ceil(surface_dimensions.height * self->scale); } -static cairo_matrix_t -get_orientation_matrix(FivIoOrientation o, double width, double height) -{ - cairo_matrix_t matrix = {}; - cairo_matrix_init_identity(&matrix); - switch (o) { - case FivIoOrientation90: - cairo_matrix_rotate(&matrix, -M_PI_2); - cairo_matrix_translate(&matrix, -width, 0); - break; - case FivIoOrientation180: - cairo_matrix_scale(&matrix, -1, -1); - cairo_matrix_translate(&matrix, -width, -height); - break; - case FivIoOrientation270: - cairo_matrix_rotate(&matrix, +M_PI_2); - cairo_matrix_translate(&matrix, 0, -height); - break; - case FivIoOrientationMirror0: - cairo_matrix_scale(&matrix, -1, +1); - cairo_matrix_translate(&matrix, -width, 0); - break; - case FivIoOrientationMirror90: - cairo_matrix_rotate(&matrix, +M_PI_2); - cairo_matrix_scale(&matrix, -1, +1); - cairo_matrix_translate(&matrix, -width, -height); - break; - case FivIoOrientationMirror180: - cairo_matrix_scale(&matrix, +1, -1); - cairo_matrix_translate(&matrix, 0, -height); - break; - case FivIoOrientationMirror270: - cairo_matrix_rotate(&matrix, -M_PI_2); - cairo_matrix_scale(&matrix, -1, +1); - default: - break; - } - return matrix; -} - static void fiv_view_get_preferred_height(GtkWidget *widget, gint *minimum, gint *natural) { @@ -318,7 +273,7 @@ fiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) if (!self->image || !self->scale_to_fit) return; - struct size surface_dimensions = get_surface_dimensions(self); + Dimensions surface_dimensions = get_surface_dimensions(self); self->scale = 1; if (ceil(surface_dimensions.width * self->scale) > allocation->width) @@ -443,8 +398,8 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr) if (h < allocation.height) y = round((allocation.height - h) / 2.); - struct size surface_dimensions = get_surface_dimensions(self); - cairo_matrix_t matrix = get_orientation_matrix( + Dimensions surface_dimensions = get_surface_dimensions(self); + cairo_matrix_t matrix = fiv_io_orientation_matrix( self->orientation, surface_dimensions.width, surface_dimensions.height); cairo_translate(cr, x, y); if (self->checkerboard) { @@ -731,7 +686,7 @@ on_draw_page(G_GNUC_UNUSED GtkPrintOperation *operation, { // Any DPI will be wrong, unless we import that information from the image. double scale = 1 / 96.; - struct size surface_dimensions = get_surface_dimensions(self); + Dimensions surface_dimensions = get_surface_dimensions(self); double w = surface_dimensions.width * scale; double h = surface_dimensions.height * scale; @@ -743,7 +698,7 @@ on_draw_page(G_GNUC_UNUSED GtkPrintOperation *operation, cairo_t *cr = gtk_print_context_get_cairo_context(context); cairo_scale(cr, scale, scale); cairo_set_source_surface(cr, self->frame, 0, 0); - cairo_matrix_t matrix = get_orientation_matrix( + cairo_matrix_t matrix = fiv_io_orientation_matrix( self->orientation, surface_dimensions.width, surface_dimensions.height); cairo_pattern_set_matrix(cairo_get_source(cr), &matrix); cairo_paint(cr);