diff options
-rw-r--r-- | fastiv-io.c | 68 | ||||
-rw-r--r-- | fastiv-io.h | 7 | ||||
-rw-r--r-- | fastiv-view.c | 65 |
3 files changed, 94 insertions, 46 deletions
diff --git a/fastiv-io.c b/fastiv-io.c index 0e9b8c4..f9756f9 100644 --- a/fastiv-io.c +++ b/fastiv-io.c @@ -955,39 +955,58 @@ open_xcursor(const gchar *data, gsize len, GError **error) return NULL; } - // Let's just arrange the pixel data in a recording surface. - static cairo_user_data_key_t key = {}; - cairo_surface_t *recording = - cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, NULL); - cairo_surface_set_user_data( - recording, &key, images, (cairo_destroy_func_t) XcursorImagesDestroy); - cairo_t *cr = cairo_create(recording); + // Interpret cursors as animated pages. + cairo_surface_t *pages = NULL, *frames_head = NULL, *frames_tail = NULL; - // Unpack horizontally by animation, vertically by size. + // XXX: Assuming that all "nominal sizes" have the same dimensions. XcursorDim last_nominal = -1; - int x = 0, y = 0, row_height = 0; for (int i = 0; i < images->nimage; i++) { XcursorImage *image = images->images[i]; - if (image->size != last_nominal) { - x = 0; - y += row_height; - row_height = 0; - last_nominal = image->size; - } // The library automatically byte swaps in _XcursorReadImage(). - cairo_surface_t *source = cairo_image_surface_create_for_data( + cairo_surface_t *surface = cairo_image_surface_create_for_data( (unsigned char *) image->pixels, CAIRO_FORMAT_ARGB32, image->width, image->height, image->width * sizeof *image->pixels); - cairo_set_source_surface(cr, source, x, y); - cairo_surface_destroy(source); - cairo_paint(cr); + cairo_surface_set_user_data(surface, &fastiv_io_key_frame_duration, + (void *) (intptr_t) image->delay, NULL); + + if (pages && image->size == last_nominal) { + cairo_surface_set_user_data(surface, + &fastiv_io_key_frame_previous, frames_tail, NULL); + cairo_surface_set_user_data(frames_tail, + &fastiv_io_key_frame_next, surface, + (cairo_destroy_func_t) cairo_surface_destroy); + } else if (frames_head) { + cairo_surface_set_user_data(frames_head, + &fastiv_io_key_frame_previous, frames_tail, NULL); + + cairo_surface_set_user_data(frames_head, + &fastiv_io_key_page_next, surface, + (cairo_destroy_func_t) cairo_surface_destroy); + cairo_surface_set_user_data(surface, + &fastiv_io_key_page_previous, frames_head, NULL); + frames_head = surface; + } else { + pages = frames_head = surface; + } - x += image->width; - row_height = MAX(row_height, (int) image->height); + frames_tail = surface; + last_nominal = image->size; } - cairo_destroy(cr); - return recording; + if (!pages) { + XcursorImagesDestroy(images); + return NULL; + } + + // Wrap around animations in the last page. + cairo_surface_set_user_data( + frames_head, &fastiv_io_key_frame_previous, frames_tail, NULL); + + // There is no need to copy data, assign it to the surface. + static cairo_user_data_key_t key = {}; + cairo_surface_set_user_data( + pages, &key, images, (cairo_destroy_func_t) XcursorImagesDestroy); + return pages; } #endif // HAVE_XCURSOR -------------------------------------------------------- @@ -1041,6 +1060,9 @@ cairo_user_data_key_t fastiv_io_key_frame_previous; cairo_user_data_key_t fastiv_io_key_frame_duration; cairo_user_data_key_t fastiv_io_key_loops; +cairo_user_data_key_t fastiv_io_key_page_next; +cairo_user_data_key_t fastiv_io_key_page_previous; + cairo_surface_t * fastiv_io_open(const gchar *path, GError **error) { diff --git a/fastiv-io.h b/fastiv-io.h index fc12fa8..db94a46 100644 --- a/fastiv-io.h +++ b/fastiv-io.h @@ -46,6 +46,13 @@ extern cairo_user_data_key_t fastiv_io_key_frame_duration; /// How many times to repeat the animation, or zero for +inf, as a uintptr_t. extern cairo_user_data_key_t fastiv_io_key_loops; +/// The first frame of the next page, as a surface, in a chain. +/// There is no wrap-around. +extern cairo_user_data_key_t fastiv_io_key_page_next; +/// The first frame of the previous page, as a surface, in a chain. +/// There is no wrap-around. This is a weak pointer. +extern cairo_user_data_key_t fastiv_io_key_page_previous; + cairo_surface_t *fastiv_io_open(const gchar *path, GError **error); cairo_surface_t *fastiv_io_open_from_data( const char *data, size_t len, const gchar *path, GError **error); diff --git a/fastiv-view.c b/fastiv-view.c index 1747266..c8bce86 100644 --- a/fastiv-view.c +++ b/fastiv-view.c @@ -31,8 +31,9 @@ struct _FastivView { GtkWidget parent_instance; - cairo_surface_t *surface; ///< The loaded image (sequence) - cairo_surface_t *frame; ///< Current frame within, unreferenced + cairo_surface_t *image; ///< The loaded image (sequence) + cairo_surface_t *page; ///< Current page within image, weak + cairo_surface_t *frame; ///< Current frame within page, weak FastivIoOrientation orientation; ///< Current orientation bool filter; bool scale_to_fit; @@ -89,7 +90,7 @@ static void fastiv_view_finalize(GObject *gobject) { FastivView *self = FASTIV_VIEW(gobject); - cairo_surface_destroy(self->surface); + cairo_surface_destroy(self->image); G_OBJECT_CLASS(fastiv_view_parent_class)->finalize(gobject); } @@ -115,28 +116,28 @@ static void get_surface_dimensions(FastivView *self, double *width, double *height) { *width = *height = 0; - if (!self->surface) + if (!self->image) return; cairo_rectangle_t extents = {}; - switch (cairo_surface_get_type(self->surface)) { + switch (cairo_surface_get_type(self->page)) { 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); + *width = cairo_image_surface_get_height(self->page); + *height = cairo_image_surface_get_width(self->page); break; default: - *width = cairo_image_surface_get_width(self->surface); - *height = cairo_image_surface_get_height(self->surface); + *width = cairo_image_surface_get_width(self->page); + *height = cairo_image_surface_get_height(self->page); } return; case CAIRO_SURFACE_TYPE_RECORDING: - if (!cairo_recording_surface_get_extents(self->surface, &extents)) { - cairo_recording_surface_ink_extents(self->surface, + if (!cairo_recording_surface_get_extents(self->page, &extents)) { + cairo_recording_surface_ink_extents(self->page, &extents.x, &extents.y, &extents.width, &extents.height); } @@ -198,7 +199,7 @@ fastiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) ->size_allocate(widget, allocation); FastivView *self = FASTIV_VIEW(widget); - if (!self->surface || !self->scale_to_fit) + if (!self->image || !self->scale_to_fit) return; double w, h; @@ -271,7 +272,7 @@ fastiv_view_draw(GtkWidget *widget, cairo_t *cr) allocation.width, allocation.height); FastivView *self = FASTIV_VIEW(widget); - if (!self->surface || + if (!self->image || !gtk_cairo_should_draw_window(cr, gtk_widget_get_window(widget))) return TRUE; @@ -408,7 +409,7 @@ static gboolean fastiv_view_scroll_event(GtkWidget *widget, GdkEventScroll *event) { FastivView *self = FASTIV_VIEW(widget); - if (!self->surface) + if (!self->image) return FALSE; if (event->state & gtk_accelerator_get_default_mod_mask()) return FALSE; @@ -431,7 +432,7 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event) FastivView *self = FASTIV_VIEW(widget); if (event->state & ~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask()) return FALSE; - if (!self->surface) + if (!self->image) return FALSE; switch (event->keyval) { @@ -462,16 +463,33 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event) gtk_widget_queue_resize(widget); return TRUE; - case GDK_KEY_bracketleft: + case GDK_KEY_bracketleft: { + cairo_surface_t *page = cairo_surface_get_user_data( + self->page, &fastiv_io_key_page_previous); + if (page) + self->frame = self->page = page; + gtk_widget_queue_resize(widget); + return TRUE; + } + case GDK_KEY_bracketright: { + cairo_surface_t *page = cairo_surface_get_user_data( + self->page, &fastiv_io_key_page_next); + if (page) + self->frame = self->page = page; + gtk_widget_queue_resize(widget); + return TRUE; + } + + case GDK_KEY_braceleft: if (!(self->frame = cairo_surface_get_user_data( self->frame, &fastiv_io_key_frame_previous))) - self->frame = self->surface; + self->frame = self->page; gtk_widget_queue_draw(widget); return TRUE; - case GDK_KEY_bracketright: + case GDK_KEY_braceright: if (!(self->frame = cairo_surface_get_user_data( self->frame, &fastiv_io_key_frame_next))) - self->frame = self->surface; + self->frame = self->page; gtk_widget_queue_draw(widget); return TRUE; } @@ -527,14 +545,15 @@ fastiv_view_open(FastivView *self, const gchar *path, GError **error) cairo_surface_t *surface = fastiv_io_open(path, error); if (!surface) return FALSE; - if (self->surface) - cairo_surface_destroy(self->surface); + if (self->image) + cairo_surface_destroy(self->image); - self->frame = self->surface = surface; + self->frame = self->page = self->image = surface; set_scale_to_fit(self, true); + // TODO(p): This is actually per-page. if ((self->orientation = (uintptr_t) cairo_surface_get_user_data( - self->surface, &fastiv_io_key_orientation)) == + self->image, &fastiv_io_key_orientation)) == FastivIoOrientationUnknown) self->orientation = FastivIoOrientation0; return TRUE; |