From 074bd4d37f36fa8963e66aa0f8f9de3ff6cadba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Fri, 23 Jun 2023 22:28:09 +0200 Subject: Stop abusing Cairo user data, part 2 With the shift from cairo_surface_t, we've lost our ability to directly render vector surfaces, but it doesn't matter. --- fiv-view.c | 130 ++++++++++++++++++++++++++----------------------------------- 1 file changed, 56 insertions(+), 74 deletions(-) (limited to 'fiv-view.c') diff --git a/fiv-view.c b/fiv-view.c index 43b4055..4005e40 100644 --- a/fiv-view.c +++ b/fiv-view.c @@ -63,10 +63,10 @@ struct _FivView { gchar *messages; ///< Image load information gchar *uri; ///< Path to the current image (if any) - cairo_surface_t *image; ///< The loaded image (sequence) - cairo_surface_t *page; ///< Current page within image, weak - cairo_surface_t *page_scaled; ///< Current page within image, scaled - cairo_surface_t *frame; ///< Current frame within page, weak + FivIoImage *image; ///< The loaded image (sequence) + FivIoImage *page; ///< Current page within image, weak + FivIoImage *page_scaled; ///< Current page within image, scaled + FivIoImage *frame; ///< Current frame within page, weak FivIoOrientation orientation; ///< Current page orientation bool enable_cms : 1; ///< Smooth scaling toggle bool filter : 1; ///< Smooth scaling toggle @@ -77,7 +77,7 @@ struct _FivView { double scale; ///< Scaling factor double drag_start[2]; ///< Adjustment values for drag origin - cairo_surface_t *enhance_swap; ///< Quick swap in/out + FivIoImage *enhance_swap; ///< Quick swap in/out FivIoProfile screen_cms_profile; ///< Target colour profile for widget int remaining_loops; ///< Greater than zero if limited @@ -234,9 +234,9 @@ fiv_view_finalize(GObject *gobject) { FivView *self = FIV_VIEW(gobject); g_clear_pointer(&self->screen_cms_profile, fiv_io_profile_free); - g_clear_pointer(&self->enhance_swap, cairo_surface_destroy); - g_clear_pointer(&self->image, cairo_surface_destroy); - g_clear_pointer(&self->page_scaled, cairo_surface_destroy); + g_clear_pointer(&self->enhance_swap, fiv_io_image_unref); + g_clear_pointer(&self->image, fiv_io_image_unref); + g_clear_pointer(&self->page_scaled, fiv_io_image_unref); g_free(self->uri); g_free(self->messages); @@ -283,15 +283,13 @@ fiv_view_get_property( g_value_set_boolean(value, !!self->image); break; case PROP_CAN_ANIMATE: - g_value_set_boolean(value, self->page && - cairo_surface_get_user_data(self->page, &fiv_io_key_frame_next)); + g_value_set_boolean(value, self->page && self->page->frame_next); break; case PROP_HAS_PREVIOUS_PAGE: g_value_set_boolean(value, self->image && self->page != self->image); break; case PROP_HAS_NEXT_PAGE: - g_value_set_boolean(value, self->page && - cairo_surface_get_user_data(self->page, &fiv_io_key_page_next)); + g_value_set_boolean(value, self->page && self->page->page_next); break; case PROP_HADJUSTMENT: @@ -403,20 +401,27 @@ static void prescale_page(FivView *self) { FivIoRenderClosure *closure = NULL; - if (!self->image || !(closure = - cairo_surface_get_user_data(self->page, &fiv_io_key_render))) + if (!self->image || !(closure = self->page->render)) return; // TODO(p): Restart the animation. No vector formats currently animate. g_return_if_fail(!self->frame_update_connection); // If it fails, the previous frame pointer may become invalid. - g_clear_pointer(&self->page_scaled, cairo_surface_destroy); + g_clear_pointer(&self->page_scaled, fiv_io_image_unref); self->frame = self->page_scaled = closure->render(closure, self->scale); if (!self->page_scaled) self->frame = self->page; } +static void +set_source_image(FivView *self, cairo_t *cr) +{ + cairo_surface_t *surface = fiv_io_image_to_surface_noref(self->frame); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_surface_destroy(surface); +} + static void fiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { @@ -606,37 +611,19 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr) // Then all frames are pre-scaled. if (self->page_scaled) { - cairo_set_source_surface(cr, self->frame, 0, 0); + set_source_image(self, cr); cairo_pattern_set_matrix(cairo_get_source(cr), &matrix); cairo_paint(cr); return TRUE; } - // FIXME: Recording surfaces do not work well with CAIRO_SURFACE_TYPE_XLIB, - // 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, dw, dh); - cairo_t *tcr = cairo_create(image); - cairo_scale(tcr, self->scale, self->scale); - cairo_set_source_surface(tcr, self->frame, 0, 0); - cairo_pattern_set_matrix(cairo_get_source(tcr), &matrix); - cairo_paint(tcr); - cairo_destroy(tcr); - - cairo_set_source_surface(cr, image, 0, 0); - cairo_paint(cr); - cairo_surface_destroy(image); - return TRUE; - } - // 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, dw, dh); cairo_clip(cr); cairo_scale(cr, self->scale, self->scale); - cairo_set_source_surface(cr, self->frame, 0, 0); + set_source_image(self, cr); cairo_pattern_t *pattern = cairo_get_source(cr); cairo_pattern_set_matrix(pattern, &matrix); @@ -817,8 +804,7 @@ stop_animating(FivView *self) static gboolean advance_frame(FivView *self) { - cairo_surface_t *next = - cairo_surface_get_user_data(self->frame, &fiv_io_key_frame_next); + FivIoImage *next = self->frame->frame_next; if (next) { self->frame = next; } else { @@ -836,8 +822,7 @@ advance_animation(FivView *self, GdkFrameClock *clock) gint64 now = gdk_frame_clock_get_frame_time(clock); while (true) { // TODO(p): See if infinite frames can actually happen, and how. - intptr_t duration = (intptr_t) cairo_surface_get_user_data( - self->frame, &fiv_io_key_frame_duration); + int64_t duration = self->frame->frame_duration; if (duration < 0) return FALSE; @@ -875,30 +860,28 @@ start_animating(FivView *self) stop_animating(self); GdkFrameClock *clock = gtk_widget_get_frame_clock(GTK_WIDGET(self)); - if (!clock || !self->image || - !cairo_surface_get_user_data(self->page, &fiv_io_key_frame_next)) + if (!clock || !self->image || !self->page->frame_next) return; self->frame_time = gdk_frame_clock_get_frame_time(clock); self->frame_update_connection = g_signal_connect( clock, "update", G_CALLBACK(on_frame_clock_update), self); - self->remaining_loops = - (uintptr_t) cairo_surface_get_user_data(self->page, &fiv_io_key_loops); + self->remaining_loops = self->page->loops; gdk_frame_clock_begin_updating(clock); g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_PLAYING]); } static void -switch_page(FivView *self, cairo_surface_t *page) +switch_page(FivView *self, FivIoImage *page) { - g_clear_pointer(&self->page_scaled, cairo_surface_destroy); + g_clear_pointer(&self->page_scaled, fiv_io_image_unref); self->frame = self->page = page; prescale_page(self); if (!self->page || - (self->orientation = (uintptr_t) cairo_surface_get_user_data( - self->page, &fiv_io_key_orientation)) == FivIoOrientationUnknown) + (self->orientation = self->page->orientation) == + FivIoOrientationUnknown) self->orientation = FivIoOrientation0; start_animating(self); @@ -1027,7 +1010,7 @@ copy(FivView *self) cairo_surface_t *transformed = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); cairo_t *cr = cairo_create(transformed); - cairo_set_source_surface(cr, self->frame, 0, 0); + set_source_image(self, cr); cairo_pattern_set_matrix(cairo_get_source(cr), &matrix); cairo_paint(cr); cairo_destroy(cr); @@ -1065,7 +1048,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); + set_source_image(self, cr); cairo_pattern_set_matrix(cairo_get_source(cr), &matrix); cairo_paint(cr); } @@ -1100,7 +1083,7 @@ print(FivView *self) } static gboolean -save_as(FivView *self, cairo_surface_t *frame) +save_as(FivView *self, FivIoImage *frame) { GtkWindow *window = get_toplevel(GTK_WIDGET(self)); FivIoProfile target = NULL; @@ -1362,7 +1345,7 @@ fiv_view_init(FivView *self) // --- Public interface -------------------------------------------------------- -static cairo_surface_t * +static FivIoImage * open_without_swapping_in(FivView *self, const char *uri) { FivIoOpenContext ctx = { @@ -1374,8 +1357,7 @@ open_without_swapping_in(FivView *self, const char *uri) }; GError *error = NULL; - cairo_surface_t *surface = - fiv_io_image_to_surface(fiv_io_open(&ctx, &error)); + FivIoImage *image = fiv_io_open(&ctx, &error); if (error) { g_ptr_array_add(ctx.warnings, g_strdup(error->message)); g_error_free(error); @@ -1388,7 +1370,7 @@ open_without_swapping_in(FivView *self, const char *uri) } g_ptr_array_free(ctx.warnings, TRUE); - return surface; + return image; } // TODO(p): Progressive picture loading, or at least async/cancellable. @@ -1396,18 +1378,18 @@ gboolean fiv_view_set_uri(FivView *self, const char *uri) { // This is extremely expensive, and only works sometimes. - g_clear_pointer(&self->enhance_swap, cairo_surface_destroy); + g_clear_pointer(&self->enhance_swap, fiv_io_image_unref); if (self->enhance) { self->enhance = FALSE; g_object_notify_by_pspec( G_OBJECT(self), view_properties[PROP_ENHANCE]); } - cairo_surface_t *surface = open_without_swapping_in(self, uri); - g_clear_pointer(&self->image, cairo_surface_destroy); + FivIoImage *image = open_without_swapping_in(self, uri); + g_clear_pointer(&self->image, fiv_io_image_unref); self->frame = self->page = NULL; - self->image = surface; + self->image = image; switch_page(self, self->image); // Otherwise, adjustment values and zoom are retained implicitly. @@ -1419,15 +1401,15 @@ fiv_view_set_uri(FivView *self, const char *uri) g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_MESSAGES]); g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_HAS_IMAGE]); - return surface != NULL; + return image != NULL; } static void page_step(FivView *self, int step) { - cairo_user_data_key_t *key = - step < 0 ? &fiv_io_key_page_previous : &fiv_io_key_page_next; - cairo_surface_t *page = cairo_surface_get_user_data(self->page, key); + FivIoImage *page = step < 0 + ? self->page->page_previous + : self->page->page_next; if (page) switch_page(self, page); } @@ -1436,9 +1418,10 @@ static void frame_step(FivView *self, int step) { stop_animating(self); - cairo_user_data_key_t *key = - step < 0 ? &fiv_io_key_frame_previous : &fiv_io_key_frame_next; - if (!step || !(self->frame = cairo_surface_get_user_data(self->frame, key))) + FivIoImage *frame = step < 0 + ? self->frame->frame_previous + : self->frame->frame_next; + if (!step || !(self->frame = frame)) self->frame = self->page; gtk_widget_queue_draw(GTK_WIDGET(self)); } @@ -1446,21 +1429,21 @@ frame_step(FivView *self, int step) static gboolean reload(FivView *self) { - cairo_surface_t *surface = open_without_swapping_in(self, self->uri); + FivIoImage *image = open_without_swapping_in(self, self->uri); g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_MESSAGES]); - if (!surface) + if (!image) return FALSE; - g_clear_pointer(&self->image, cairo_surface_destroy); - g_clear_pointer(&self->enhance_swap, cairo_surface_destroy); - switch_page(self, (self->image = surface)); + g_clear_pointer(&self->image, fiv_io_image_unref); + g_clear_pointer(&self->enhance_swap, fiv_io_image_unref); + switch_page(self, (self->image = image)); return TRUE; } static void swap_enhanced_image(FivView *self) { - cairo_surface_t *saved = self->image; + FivIoImage *saved = self->image; self->image = self->page = self->frame = NULL; if (self->enhance_swap) { @@ -1547,9 +1530,8 @@ fiv_view_command(FivView *self, FivViewCommand command) break; case FIV_VIEW_COMMAND_PAGE_NEXT: page_step(self, +1); break; case FIV_VIEW_COMMAND_PAGE_LAST: - for (cairo_surface_t *s = self->page; - (s = cairo_surface_get_user_data(s, &fiv_io_key_page_next)); ) - self->page = s; + for (FivIoImage *I = self->page; (I = I->page_next); ) + self->page = I; switch_page(self, self->page); break; case FIV_VIEW_COMMAND_FRAME_FIRST: -- cgit v1.2.3