aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-10-03 22:57:39 +0200
committerPřemysl Eric Janouch <p@janouch.name>2022-10-03 23:00:16 +0200
commit9cdc641b0a233d46dffc3595574af30b66369190 (patch)
treeee09fd5291f41b75117c7a53a9b482b3771c7a24
parent7b88e894897383d80a4f71c6e96d8fd380ce91f3 (diff)
downloadfiv-9cdc641b0a233d46dffc3595574af30b66369190.tar.gz
fiv-9cdc641b0a233d46dffc3595574af30b66369190.tar.xz
fiv-9cdc641b0a233d46dffc3595574af30b66369190.zip
Center view rotations/flips
-rw-r--r--fiv-io.h3
-rw-r--r--fiv-view.c131
2 files changed, 97 insertions, 37 deletions
diff --git a/fiv-io.h b/fiv-io.h
index 4e031f9..7788a76 100644
--- a/fiv-io.h
+++ b/fiv-io.h
@@ -165,7 +165,8 @@ typedef enum _FivIoOrientation {
FivIoOrientation270 = 8
} FivIoOrientation;
-/// Returns a rendering matrix for a surface, and its target dimensions.
+/// Returns a rendering matrix for a surface (user space to pattern space),
+/// and its target dimensions.
cairo_matrix_t fiv_io_orientation_apply(cairo_surface_t *surface,
FivIoOrientation orientation, double *width, double *height);
void fiv_io_orientation_dimensions(cairo_surface_t *surface,
diff --git a/fiv-view.c b/fiv-view.c
index 14e4db9..d90af18 100644
--- a/fiv-view.c
+++ b/fiv-view.c
@@ -191,20 +191,19 @@ get_display_dimensions(FivView *self, int *width, int *height)
static void
update_adjustments(FivView *self)
{
- int w = 0, h = 0;
- get_display_dimensions(self, &w, &h);
-
+ int dw = 0, dh = 0;
+ get_display_dimensions(self, &dw, &dh);
GtkAllocation alloc;
gtk_widget_get_allocation(GTK_WIDGET(self), &alloc);
if (self->hadjustment) {
gtk_adjustment_configure(self->hadjustment,
- gtk_adjustment_get_value(self->hadjustment), 0, w,
+ gtk_adjustment_get_value(self->hadjustment), 0, dw,
alloc.width * 0.1, alloc.width * 0.9, alloc.width);
}
if (self->vadjustment) {
gtk_adjustment_configure(self->vadjustment,
- gtk_adjustment_get_value(self->vadjustment), 0, h,
+ gtk_adjustment_get_value(self->vadjustment), 0, dh,
alloc.height * 0.1, alloc.height * 0.9, alloc.height);
}
}
@@ -576,8 +575,8 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
!gtk_cairo_should_draw_window(cr, gtk_widget_get_window(widget)))
return TRUE;
- int w, h;
- get_display_dimensions(self, &w, &h);
+ int dw, dh;
+ get_display_dimensions(self, &dw, &dh);
double x = 0;
double y = 0;
@@ -585,11 +584,13 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
x = -floor(gtk_adjustment_get_value(self->hadjustment));
if (self->vadjustment)
y = -floor(gtk_adjustment_get_value(self->vadjustment));
- if (w < allocation.width)
- x = round((allocation.width - w) / 2.);
- if (h < allocation.height)
- y = round((allocation.height - h) / 2.);
+ if (dw < allocation.width)
+ x = round((allocation.width - dw) / 2.);
+ if (dh < allocation.height)
+ y = round((allocation.height - dh) / 2.);
+ // XXX: This naming is confusing, because it isn't actually for the surface,
+ // but rather for our possibly rotated rendition of it.
Dimensions surface_dimensions = {};
cairo_matrix_t matrix = fiv_io_orientation_apply(
self->page_scaled ? self->page_scaled : self->page, self->orientation,
@@ -599,7 +600,7 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
if (self->checkerboard) {
gtk_style_context_save(style);
gtk_style_context_add_class(style, "checkerboard");
- gtk_render_background(style, cr, 0, 0, w, h);
+ gtk_render_background(style, cr, 0, 0, dw, dh);
gtk_style_context_restore(style);
}
@@ -615,7 +616,7 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
// we always get a shitty pixmap, where transparency contains junk.
if (cairo_surface_get_type(self->frame) == CAIRO_SURFACE_TYPE_RECORDING) {
cairo_surface_t *image =
- cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dw, dh);
cairo_t *tcr = cairo_create(image);
cairo_scale(tcr, self->scale, self->scale);
cairo_set_source_surface(tcr, self->frame, 0, 0);
@@ -631,7 +632,7 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
// XXX: The rounding together with padding may result in up to
// a pixel's worth of made-up picture data.
- cairo_rectangle(cr, 0, 0, w, h);
+ cairo_rectangle(cr, 0, 0, dw, dh);
cairo_clip(cr);
cairo_scale(cr, self->scale, self->scale);
@@ -689,6 +690,28 @@ set_scale_to_fit(FivView *self, bool scale_to_fit)
return TRUE;
}
+static void
+widget_to_surface(FivView *self, double *x, double *y)
+{
+ int dw, dh;
+ get_display_dimensions(self, &dw, &dh);
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
+
+ // Unneeded, thus unimplemented: this means zero adjustment values.
+ if (!self->hadjustment || !self->vadjustment)
+ return;
+
+ *x = (*x + (dw < allocation.width
+ ? -round((allocation.width - dw) / 2.)
+ : +floor(gtk_adjustment_get_value(self->hadjustment))))
+ / self->scale;
+ *y = (*y + (dh < allocation.height
+ ? -round((allocation.height - dh) / 2.)
+ : +floor(gtk_adjustment_get_value(self->vadjustment))))
+ / self->scale;
+}
+
static gboolean
set_scale(FivView *self, double scale, const GdkEvent *event)
{
@@ -703,34 +726,25 @@ set_scale(FivView *self, double scale, const GdkEvent *event)
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
-
- double focus_x = 0, focus_y = 0, surface_x = 0, surface_y = 0;
+ double focus_x = 0, focus_y = 0;
if (!event || !gdk_event_get_coords(event, &focus_x, &focus_y)) {
focus_x = 0.5 * allocation.width;
focus_y = 0.5 * allocation.height;
}
- if (self->hadjustment && self->vadjustment) {
- int w, h;
- get_display_dimensions(self, &w, &h);
-
- surface_x = (focus_x + (w < allocation.width
- ? -round((allocation.width - w) / 2.)
- : +floor(gtk_adjustment_get_value(self->hadjustment))))
- / self->scale;
- surface_y = (focus_y + (h < allocation.height
- ? -round((allocation.height - h) / 2.)
- : +floor(gtk_adjustment_get_value(self->vadjustment))))
- / self->scale;
- }
+
+ double surface_x = focus_x;
+ double surface_y = focus_y;
+ widget_to_surface(self, &surface_x, &surface_y);
self->scale = scale;
g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_SCALE]);
prescale_page(self);
+ // Similar to set_orientation().
if (self->hadjustment && self->vadjustment) {
+ Dimensions surface_dimensions = get_surface_dimensions(self);
update_adjustments(self);
- Dimensions surface_dimensions = get_surface_dimensions(self);
if (surface_dimensions.width * self->scale > allocation.width)
gtk_adjustment_set_value(
self->hadjustment, surface_x * self->scale - focus_x);
@@ -1036,6 +1050,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.;
Dimensions surface_dimensions = {};
+ // XXX: Perhaps use self->frame, even though their sizes should match.
cairo_matrix_t matrix =
fiv_io_orientation_apply(self->page, self->orientation,
&surface_dimensions.width, &surface_dimensions.height);
@@ -1457,6 +1472,53 @@ swap_enhanced_image(FivView *self)
}
}
+static void
+transformed_to_real(FivView *self, double *x, double *y)
+{
+ double sw = 0, sh = 0;
+ cairo_matrix_t matrix =
+ fiv_io_orientation_apply(self->page, self->orientation, &sw, &sh);
+ cairo_matrix_transform_point(&matrix, x, y);
+}
+
+static void
+set_orientation(FivView *self, FivIoOrientation orientation)
+{
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
+
+ // In the future, rotating gestures can pick another centre point.
+ double focus_x = 0.5 * allocation.width;
+ double focus_y = 0.5 * allocation.height;
+
+ double surface_x = focus_x;
+ double surface_y = focus_y;
+ widget_to_surface(self, &surface_x, &surface_y);
+ transformed_to_real(self, &surface_x, &surface_y);
+
+ self->orientation = orientation;
+
+ // Similar to set_scale().
+ Dimensions surface_dimensions = {};
+ cairo_matrix_t matrix =
+ fiv_io_orientation_apply(self->page, self->orientation,
+ &surface_dimensions.width, &surface_dimensions.height);
+ if (self->hadjustment && self->vadjustment &&
+ cairo_matrix_invert(&matrix) == CAIRO_STATUS_SUCCESS) {
+ cairo_matrix_transform_point(&matrix, &surface_x, &surface_y);
+ update_adjustments(self);
+
+ if (surface_dimensions.width * self->scale > allocation.width)
+ gtk_adjustment_set_value(
+ self->hadjustment, surface_x * self->scale - focus_x);
+ if (surface_dimensions.height * self->scale > allocation.height)
+ gtk_adjustment_set_value(
+ self->vadjustment, surface_y * self->scale - focus_y);
+ }
+
+ gtk_widget_queue_resize(GTK_WIDGET(self));
+}
+
void
fiv_view_command(FivView *self, FivViewCommand command)
{
@@ -1471,14 +1533,11 @@ fiv_view_command(FivView *self, FivViewCommand command)
reload(self);
break; case FIV_VIEW_COMMAND_ROTATE_LEFT:
- self->orientation = view_left[self->orientation];
- gtk_widget_queue_resize(widget);
+ set_orientation(self, view_left[self->orientation]);
break; case FIV_VIEW_COMMAND_MIRROR:
- self->orientation = view_mirror[self->orientation];
- gtk_widget_queue_resize(widget);
+ set_orientation(self, view_mirror[self->orientation]);
break; case FIV_VIEW_COMMAND_ROTATE_RIGHT:
- self->orientation = view_right[self->orientation];
- gtk_widget_queue_resize(widget);
+ set_orientation(self, view_right[self->orientation]);
break; case FIV_VIEW_COMMAND_PAGE_FIRST:
switch_page(self, self->image);