Compare commits
6 Commits
4f57070e27
...
bbfa2344d6
Author | SHA1 | Date | |
---|---|---|---|
bbfa2344d6 | |||
2ff853b7e0 | |||
bb4d3acd12 | |||
074bd4d37f | |||
add96b37a6 | |||
c2e8b65d0f |
181
fiv-io.h
181
fiv-io.h
@ -39,50 +39,93 @@ extern const char *fiv_io_supported_media_types[];
|
||||
|
||||
gchar **fiv_io_all_supported_media_types(void);
|
||||
|
||||
// Userdata are typically attached to all Cairo surfaces in an animation.
|
||||
typedef enum _FivIoOrientation FivIoOrientation;
|
||||
typedef struct _FivIoRenderClosure FivIoRenderClosure;
|
||||
typedef struct _FivIoImage FivIoImage;
|
||||
|
||||
/// GBytes with plain Exif/TIFF data.
|
||||
extern cairo_user_data_key_t fiv_io_key_exif;
|
||||
/// FivIoOrientation, as a uintptr_t.
|
||||
extern cairo_user_data_key_t fiv_io_key_orientation;
|
||||
/// GBytes with plain ICC profile data.
|
||||
extern cairo_user_data_key_t fiv_io_key_icc;
|
||||
/// GBytes with plain XMP data.
|
||||
extern cairo_user_data_key_t fiv_io_key_xmp;
|
||||
/// GBytes with a WebP's THUM chunk, used for our thumbnails.
|
||||
extern cairo_user_data_key_t fiv_io_key_thum;
|
||||
/// GHashTable with key-value pairs from PNG's tEXt, zTXt, iTXt chunks.
|
||||
/// Currently only read by fiv_io_open_png_thumbnail().
|
||||
extern cairo_user_data_key_t fiv_io_key_text;
|
||||
// https://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf Table 6
|
||||
enum _FivIoOrientation {
|
||||
FivIoOrientationUnknown = 0,
|
||||
FivIoOrientation0 = 1,
|
||||
FivIoOrientationMirror0 = 2,
|
||||
FivIoOrientation180 = 3,
|
||||
FivIoOrientationMirror180 = 4,
|
||||
FivIoOrientationMirror270 = 5,
|
||||
FivIoOrientation90 = 6,
|
||||
FivIoOrientationMirror90 = 7,
|
||||
FivIoOrientation270 = 8
|
||||
};
|
||||
|
||||
/// The next frame in a sequence, as a surface, in a chain, pre-composited.
|
||||
/// There is no wrap-around.
|
||||
extern cairo_user_data_key_t fiv_io_key_frame_next;
|
||||
/// The previous frame in a sequence, as a surface, in a chain, pre-composited.
|
||||
/// This is a weak pointer that wraps around, and needn't be present
|
||||
/// for static images.
|
||||
extern cairo_user_data_key_t fiv_io_key_frame_previous;
|
||||
/// Frame duration in milliseconds as an intptr_t.
|
||||
extern cairo_user_data_key_t fiv_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 fiv_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 fiv_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 fiv_io_key_page_previous;
|
||||
|
||||
typedef struct _FivIoRenderClosure {
|
||||
struct _FivIoRenderClosure {
|
||||
/// The rendering is allowed to fail, returning NULL.
|
||||
cairo_surface_t *(*render)(struct _FivIoRenderClosure *, double scale);
|
||||
} FivIoRenderClosure;
|
||||
FivIoImage *(*render)(FivIoRenderClosure *, double scale);
|
||||
void (*destroy)(FivIoRenderClosure *);
|
||||
};
|
||||
|
||||
/// A FivIoRenderClosure for parametrized re-rendering of vector formats.
|
||||
/// This is attached at the page level.
|
||||
/// The rendered image will not have this key.
|
||||
extern cairo_user_data_key_t fiv_io_key_render;
|
||||
// Metadata are typically attached to all Cairo surfaces in an animation.
|
||||
|
||||
struct _FivIoImage {
|
||||
uint8_t *data; ///< Raw image data
|
||||
cairo_format_t format; ///< Data format
|
||||
uint32_t width; ///< Width of the image in pixels
|
||||
uint32_t stride; ///< Row stride in bytes
|
||||
uint32_t height; ///< Height of the image in pixels
|
||||
|
||||
FivIoOrientation orientation; ///< Orientation to use for display
|
||||
|
||||
GBytes *exif; ///< Raw Exif/TIFF segment
|
||||
GBytes *icc; ///< Raw ICC profile data
|
||||
GBytes *xmp; ///< Raw XMP data
|
||||
GBytes *thum; ///< WebP THUM chunk, for our thumbnails
|
||||
|
||||
/// GHashTable with key-value pairs from PNG's tEXt, zTXt, iTXt chunks.
|
||||
/// Currently only read by fiv_io_open_png_thumbnail().
|
||||
GHashTable *text;
|
||||
|
||||
/// A FivIoRenderClosure for parametrized re-rendering of vector formats.
|
||||
/// This is attached at the page level.
|
||||
FivIoRenderClosure *render;
|
||||
|
||||
/// The first frame of the next page, in a chain.
|
||||
/// There is no wrap-around.
|
||||
FivIoImage *page_next;
|
||||
|
||||
/// The first frame of the previous page, in a chain.
|
||||
/// There is no wrap-around. This is a weak pointer.
|
||||
FivIoImage *page_previous;
|
||||
|
||||
/// The next frame in a sequence, in a chain, pre-composited.
|
||||
/// There is no wrap-around.
|
||||
FivIoImage *frame_next;
|
||||
|
||||
/// The previous frame in a sequence, in a chain, pre-composited.
|
||||
/// This is a weak pointer that wraps around,
|
||||
/// and needn't be present for static images.
|
||||
FivIoImage *frame_previous;
|
||||
|
||||
/// Frame duration in milliseconds.
|
||||
int64_t frame_duration;
|
||||
|
||||
/// How many times to repeat the animation, or zero for +inf.
|
||||
uint64_t loops;
|
||||
};
|
||||
|
||||
FivIoImage *fiv_io_image_ref(FivIoImage *image);
|
||||
void fiv_io_image_unref(FivIoImage *image);
|
||||
|
||||
/// Analogous to cairo_image_surface_create(). May return NULL.
|
||||
FivIoImage *fiv_io_image_new(
|
||||
cairo_format_t format, uint32_t width, uint32_t height);
|
||||
|
||||
/// Return a new Cairo image surface referencing the same data as the image,
|
||||
/// eating the reference to it.
|
||||
cairo_surface_t *fiv_io_image_to_surface(FivIoImage *image);
|
||||
|
||||
/// Return a new Cairo image surface referencing the same data as the image,
|
||||
/// without eating the image's reference.
|
||||
cairo_surface_t *fiv_io_image_to_surface_noref(const FivIoImage *image);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
typedef struct {
|
||||
const char *uri; ///< Source URI
|
||||
@ -93,10 +136,27 @@ typedef struct {
|
||||
GPtrArray *warnings; ///< String vector for non-fatal errors
|
||||
} FivIoOpenContext;
|
||||
|
||||
cairo_surface_t *fiv_io_open(const FivIoOpenContext *ctx, GError **error);
|
||||
cairo_surface_t *fiv_io_open_from_data(
|
||||
FivIoImage *fiv_io_open(const FivIoOpenContext *ctx, GError **error);
|
||||
FivIoImage *fiv_io_open_from_data(
|
||||
const char *data, size_t len, const FivIoOpenContext *ctx, GError **error);
|
||||
cairo_surface_t *fiv_io_open_png_thumbnail(const char *path, GError **error);
|
||||
|
||||
FivIoImage *fiv_io_open_png_thumbnail(const char *path, GError **error);
|
||||
|
||||
// --- Metadata ----------------------------------------------------------------
|
||||
|
||||
/// Returns a rendering matrix for an image (user space to pattern space),
|
||||
/// and its target dimensions.
|
||||
cairo_matrix_t fiv_io_orientation_apply(const FivIoImage *image,
|
||||
FivIoOrientation orientation, double *width, double *height);
|
||||
void fiv_io_orientation_dimensions(const FivIoImage *image,
|
||||
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.
|
||||
gboolean fiv_io_save_metadata(
|
||||
const FivIoImage *page, const char *path, GError **error);
|
||||
|
||||
// --- Thumbnail passing utilities ---------------------------------------------
|
||||
|
||||
@ -109,41 +169,12 @@ GBytes *fiv_io_serialize_for_search(cairo_surface_t *surface, GError **error);
|
||||
|
||||
// --- Export ------------------------------------------------------------------
|
||||
|
||||
/// Encodes a Cairo surface as a WebP bitstream, following the configuration.
|
||||
/// Encodes an image as a WebP bitstream, following the configuration.
|
||||
/// The result needs to be freed using WebPFree/WebPDataClear().
|
||||
unsigned char *fiv_io_encode_webp(
|
||||
cairo_surface_t *surface, const WebPConfig *config, size_t *len);
|
||||
FivIoImage *image, const WebPConfig *config, size_t *len);
|
||||
|
||||
/// Saves the page as a lossless WebP still picture or animation.
|
||||
/// If no exact frame is specified, this potentially creates an animation.
|
||||
gboolean fiv_io_save(cairo_surface_t *page, cairo_surface_t *frame,
|
||||
gboolean fiv_io_save(FivIoImage *page, FivIoImage *frame,
|
||||
FivIoProfile target, const char *path, GError **error);
|
||||
|
||||
// --- Metadata ----------------------------------------------------------------
|
||||
|
||||
// https://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf Table 6
|
||||
typedef enum _FivIoOrientation {
|
||||
FivIoOrientationUnknown = 0,
|
||||
FivIoOrientation0 = 1,
|
||||
FivIoOrientationMirror0 = 2,
|
||||
FivIoOrientation180 = 3,
|
||||
FivIoOrientationMirror180 = 4,
|
||||
FivIoOrientationMirror270 = 5,
|
||||
FivIoOrientation90 = 6,
|
||||
FivIoOrientationMirror90 = 7,
|
||||
FivIoOrientation270 = 8
|
||||
} FivIoOrientation;
|
||||
|
||||
/// 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,
|
||||
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.
|
||||
gboolean fiv_io_save_metadata(
|
||||
cairo_surface_t *page, const char *path, GError **error);
|
||||
|
203
fiv-thumbnail.c
203
fiv-thumbnail.c
@ -125,7 +125,7 @@ might_be_a_thumbnail(const char *path_or_uri)
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static cairo_surface_t *
|
||||
static FivIoImage *
|
||||
render(GFile *target, GBytes *data, gboolean *color_managed, GError **error)
|
||||
{
|
||||
FivIoOpenContext ctx = {
|
||||
@ -137,23 +137,22 @@ render(GFile *target, GBytes *data, gboolean *color_managed, GError **error)
|
||||
.warnings = g_ptr_array_new_with_free_func(g_free),
|
||||
};
|
||||
|
||||
cairo_surface_t *surface = fiv_io_open_from_data(
|
||||
FivIoImage *image = fiv_io_open_from_data(
|
||||
g_bytes_get_data(data, NULL), g_bytes_get_size(data), &ctx, error);
|
||||
g_free((gchar *) ctx.uri);
|
||||
g_ptr_array_free(ctx.warnings, TRUE);
|
||||
if ((*color_managed = !!ctx.screen_profile))
|
||||
fiv_io_profile_free(ctx.screen_profile);
|
||||
g_bytes_unref(data);
|
||||
return surface;
|
||||
return image;
|
||||
}
|
||||
|
||||
// In principle similar to rescale_thumbnail() from fiv-browser.c.
|
||||
static cairo_surface_t *
|
||||
adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
|
||||
static FivIoImage *
|
||||
adjust_thumbnail(FivIoImage *thumbnail, double row_height)
|
||||
{
|
||||
// Hardcode orientation.
|
||||
FivIoOrientation orientation = (uintptr_t) cairo_surface_get_user_data(
|
||||
thumbnail, &fiv_io_key_orientation);
|
||||
FivIoOrientation orientation = thumbnail->orientation;
|
||||
|
||||
double w = 0, h = 0;
|
||||
cairo_matrix_t matrix =
|
||||
@ -170,33 +169,40 @@ adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
|
||||
}
|
||||
|
||||
// Vector images should not have orientation, this should handle them all.
|
||||
FivIoRenderClosure *closure =
|
||||
cairo_surface_get_user_data(thumbnail, &fiv_io_key_render);
|
||||
FivIoRenderClosure *closure = thumbnail->render;
|
||||
if (closure && orientation <= FivIoOrientation0) {
|
||||
// This API doesn't accept non-uniform scaling; prefer a vertical fit.
|
||||
cairo_surface_t *scaled = closure->render(closure, scale_y);
|
||||
FivIoImage *scaled = closure->render(closure, scale_y);
|
||||
if (scaled)
|
||||
return scaled;
|
||||
}
|
||||
|
||||
// This will be CAIRO_FORMAT_INVALID with non-image surfaces, which is fine.
|
||||
cairo_format_t format = cairo_image_surface_get_format(thumbnail);
|
||||
if (format != CAIRO_FORMAT_INVALID &&
|
||||
orientation <= FivIoOrientation0 && scale_x == 1 && scale_y == 1)
|
||||
return cairo_surface_reference(thumbnail);
|
||||
if (orientation <= FivIoOrientation0 && scale_x == 1 && scale_y == 1)
|
||||
return fiv_io_image_ref(thumbnail);
|
||||
|
||||
cairo_format_t format = thumbnail->format;
|
||||
int projected_width = round(scale_x * w);
|
||||
int projected_height = round(scale_y * h);
|
||||
cairo_surface_t *scaled = cairo_image_surface_create(
|
||||
FivIoImage *scaled = fiv_io_image_new(
|
||||
(format == CAIRO_FORMAT_RGB24 || format == CAIRO_FORMAT_RGB30)
|
||||
? CAIRO_FORMAT_RGB24
|
||||
: CAIRO_FORMAT_ARGB32,
|
||||
projected_width, projected_height);
|
||||
if (!scaled) {
|
||||
g_warning("image allocation failure");
|
||||
return fiv_io_image_ref(thumbnail);
|
||||
}
|
||||
|
||||
cairo_surface_t *surface = fiv_io_image_to_surface_noref(scaled);
|
||||
cairo_t *cr = cairo_create(surface);
|
||||
cairo_surface_destroy(surface);
|
||||
|
||||
cairo_t *cr = cairo_create(scaled);
|
||||
cairo_scale(cr, scale_x, scale_y);
|
||||
|
||||
cairo_set_source_surface(cr, thumbnail, 0, 0);
|
||||
surface = fiv_io_image_to_surface_noref(thumbnail);
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_surface_destroy(surface);
|
||||
|
||||
cairo_pattern_t *pattern = cairo_get_source(cr);
|
||||
// CAIRO_FILTER_BEST, for some reason, works bad with CAIRO_FORMAT_RGB30.
|
||||
cairo_pattern_set_filter(pattern, CAIRO_FILTER_GOOD);
|
||||
@ -208,9 +214,7 @@ adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
|
||||
|
||||
// Note that this doesn't get triggered with oversize input surfaces,
|
||||
// even though nothing will be rendered.
|
||||
if (cairo_surface_status(thumbnail) != CAIRO_STATUS_SUCCESS ||
|
||||
cairo_surface_status(scaled) != CAIRO_STATUS_SUCCESS ||
|
||||
cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS ||
|
||||
if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS ||
|
||||
cairo_status(cr) != CAIRO_STATUS_SUCCESS)
|
||||
g_warning("thumbnail scaling failed");
|
||||
|
||||
@ -218,27 +222,32 @@ adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
|
||||
return scaled;
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
orient_thumbnail(cairo_surface_t *surface)
|
||||
static FivIoImage *
|
||||
orient_thumbnail(FivIoImage *image)
|
||||
{
|
||||
int orientation = (intptr_t) cairo_surface_get_user_data(
|
||||
surface, &fiv_io_key_orientation);
|
||||
if (orientation <= FivIoOrientation0)
|
||||
return surface;
|
||||
if (image->orientation <= FivIoOrientation0)
|
||||
return image;
|
||||
|
||||
double w = 0, h = 0;
|
||||
cairo_matrix_t matrix =
|
||||
fiv_io_orientation_apply(surface, orientation, &w, &h);
|
||||
cairo_surface_t *oriented =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
|
||||
fiv_io_orientation_apply(image, image->orientation, &w, &h);
|
||||
FivIoImage *oriented = fiv_io_image_new(image->format, w, h);
|
||||
if (!oriented) {
|
||||
g_warning("image allocation failure");
|
||||
return image;
|
||||
}
|
||||
|
||||
cairo_t *cr = cairo_create(oriented);
|
||||
cairo_surface_t *surface = fiv_io_image_to_surface_noref(oriented);
|
||||
cairo_t *cr = cairo_create(surface);
|
||||
cairo_surface_destroy(surface);
|
||||
|
||||
surface = fiv_io_image_to_surface(image);
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_surface_destroy(surface);
|
||||
cairo_pattern_set_matrix(cairo_get_source(cr), &matrix);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint(cr);
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_destroy(surface);
|
||||
return oriented;
|
||||
}
|
||||
|
||||
@ -390,7 +399,7 @@ extract_libraw_unflip(int flip)
|
||||
}
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
static FivIoImage *
|
||||
extract_libraw_bitmap(libraw_processed_image_t *image, int flip, GError **error)
|
||||
{
|
||||
// Anything else is extremely rare.
|
||||
@ -399,24 +408,26 @@ extract_libraw_bitmap(libraw_processed_image_t *image, int flip, GError **error)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cairo_surface_t *surface = cairo_image_surface_create(
|
||||
FivIoImage *I = fiv_io_image_new(
|
||||
CAIRO_FORMAT_RGB24, image->width, image->height);
|
||||
guint32 *out = (guint32 *) cairo_image_surface_get_data(surface);
|
||||
if (!I) {
|
||||
set_error(error, "image allocation failure");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guint32 *out = (guint32 *) I->data;
|
||||
const unsigned char *in = image->data;
|
||||
for (guint64 i = 0; i < image->width * image->height; in += 3)
|
||||
out[i++] = in[0] << 16 | in[1] << 8 | in[2];
|
||||
cairo_surface_mark_dirty(surface);
|
||||
|
||||
FivIoOrientation orient = extract_libraw_unflip(flip);
|
||||
cairo_surface_set_user_data(
|
||||
surface, &fiv_io_key_orientation, (void *) (intptr_t) orient, NULL);
|
||||
return surface;
|
||||
I->orientation = extract_libraw_unflip(flip);
|
||||
return I;
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
static FivIoImage *
|
||||
extract_libraw(GFile *target, GMappedFile *mf, GError **error)
|
||||
{
|
||||
cairo_surface_t *surface = NULL;
|
||||
FivIoImage *I = NULL;
|
||||
libraw_data_t *iprc = libraw_init(
|
||||
LIBRAW_OPIONS_NO_MEMERR_CALLBACK | LIBRAW_OPIONS_NO_DATAERR_CALLBACK);
|
||||
if (!iprc) {
|
||||
@ -467,11 +478,11 @@ extract_libraw(GFile *target, GMappedFile *mf, GError **error)
|
||||
switch (image->type) {
|
||||
gboolean dummy;
|
||||
case LIBRAW_IMAGE_JPEG:
|
||||
surface = render(
|
||||
I = render(
|
||||
target, g_bytes_new(image->data, image->data_size), &dummy, error);
|
||||
break;
|
||||
case LIBRAW_IMAGE_BITMAP:
|
||||
surface = extract_libraw_bitmap(image, flip, error);
|
||||
I = extract_libraw_bitmap(image, flip, error);
|
||||
break;
|
||||
default:
|
||||
set_error(error, "unsupported embedded thumbnail");
|
||||
@ -480,7 +491,7 @@ extract_libraw(GFile *target, GMappedFile *mf, GError **error)
|
||||
libraw_dcraw_clear_mem(image);
|
||||
fail:
|
||||
libraw_close(iprc);
|
||||
return surface;
|
||||
return I;
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBRAW
|
||||
@ -506,30 +517,30 @@ fiv_thumbnail_extract(GFile *target, FivThumbnailSize max_size, GError **error)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cairo_surface_t *surface = NULL;
|
||||
FivIoImage *image = NULL;
|
||||
#ifdef HAVE_LIBRAW
|
||||
surface = extract_libraw(target, mf, error);
|
||||
image = extract_libraw(target, mf, error);
|
||||
#else // ! HAVE_LIBRAW
|
||||
// TODO(p): Implement our own thumbnail extractors.
|
||||
set_error(error, "unsupported file");
|
||||
#endif // ! HAVE_LIBRAW
|
||||
g_mapped_file_unref(mf);
|
||||
|
||||
if (!surface)
|
||||
if (!image)
|
||||
return NULL;
|
||||
if (max_size < FIV_THUMBNAIL_SIZE_MIN || max_size > FIV_THUMBNAIL_SIZE_MAX)
|
||||
return orient_thumbnail(surface);
|
||||
return fiv_io_image_to_surface(orient_thumbnail(image));
|
||||
|
||||
cairo_surface_t *result =
|
||||
adjust_thumbnail(surface, fiv_thumbnail_sizes[max_size].size);
|
||||
cairo_surface_destroy(surface);
|
||||
return result;
|
||||
FivIoImage *result =
|
||||
adjust_thumbnail(image, fiv_thumbnail_sizes[max_size].size);
|
||||
fiv_io_image_unref(image);
|
||||
return fiv_io_image_to_surface(result);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static WebPData
|
||||
encode_thumbnail(cairo_surface_t *surface)
|
||||
encode_thumbnail(FivIoImage *image)
|
||||
{
|
||||
WebPData bitstream = {};
|
||||
WebPConfig config = {};
|
||||
@ -541,12 +552,12 @@ encode_thumbnail(cairo_surface_t *surface)
|
||||
if (!WebPValidateConfig(&config))
|
||||
return bitstream;
|
||||
|
||||
bitstream.bytes = fiv_io_encode_webp(surface, &config, &bitstream.size);
|
||||
bitstream.bytes = fiv_io_encode_webp(image, &config, &bitstream.size);
|
||||
return bitstream;
|
||||
}
|
||||
|
||||
static void
|
||||
save_thumbnail(cairo_surface_t *thumbnail, const char *path, GString *thum)
|
||||
save_thumbnail(FivIoImage *thumbnail, const char *path, GString *thum)
|
||||
{
|
||||
WebPMux *mux = WebPMuxNew();
|
||||
WebPData bitstream = encode_thumbnail(thumbnail);
|
||||
@ -602,15 +613,15 @@ fiv_thumbnail_produce_for_search(
|
||||
return NULL;
|
||||
|
||||
gboolean color_managed = FALSE;
|
||||
cairo_surface_t *surface = render(target, data, &color_managed, error);
|
||||
if (!surface)
|
||||
FivIoImage *image = render(target, data, &color_managed, error);
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
// TODO(p): Might want to keep this a square.
|
||||
cairo_surface_t *result =
|
||||
adjust_thumbnail(surface, fiv_thumbnail_sizes[max_size].size);
|
||||
cairo_surface_destroy(surface);
|
||||
return result;
|
||||
FivIoImage *result =
|
||||
adjust_thumbnail(image, fiv_thumbnail_sizes[max_size].size);
|
||||
fiv_io_image_unref(image);
|
||||
return fiv_io_image_to_surface(result);
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
@ -638,14 +649,14 @@ produce_fallback(GFile *target, FivThumbnailSize size, GError **error)
|
||||
return NULL;
|
||||
|
||||
gboolean color_managed = FALSE;
|
||||
cairo_surface_t *surface = render(target, data, &color_managed, error);
|
||||
if (!surface)
|
||||
FivIoImage *image = render(target, data, &color_managed, error);
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
cairo_surface_t *result =
|
||||
adjust_thumbnail(surface, fiv_thumbnail_sizes[size].size);
|
||||
cairo_surface_destroy(surface);
|
||||
return result;
|
||||
FivIoImage *result =
|
||||
adjust_thumbnail(image, fiv_thumbnail_sizes[size].size);
|
||||
fiv_io_image_unref(image);
|
||||
return fiv_io_image_to_surface(result);
|
||||
}
|
||||
|
||||
cairo_surface_t *
|
||||
@ -690,10 +701,10 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error)
|
||||
}
|
||||
|
||||
gboolean color_managed = FALSE;
|
||||
cairo_surface_t *surface =
|
||||
FivIoImage *image =
|
||||
render(target, g_mapped_file_get_bytes(mf), &color_managed, error);
|
||||
g_mapped_file_unref(mf);
|
||||
if (!surface)
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
// Boilerplate copied from fiv_thumbnail_lookup().
|
||||
@ -709,12 +720,10 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error)
|
||||
g_string_append_printf(
|
||||
thum, "%s%c%llu%c", THUMB_SIZE, 0, (unsigned long long) filesize, 0);
|
||||
|
||||
if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) {
|
||||
g_string_append_printf(thum, "%s%c%d%c", THUMB_IMAGE_WIDTH, 0,
|
||||
cairo_image_surface_get_width(surface), 0);
|
||||
g_string_append_printf(thum, "%s%c%d%c", THUMB_IMAGE_HEIGHT, 0,
|
||||
cairo_image_surface_get_height(surface), 0);
|
||||
}
|
||||
g_string_append_printf(thum, "%s%c%u%c", THUMB_IMAGE_WIDTH, 0,
|
||||
(unsigned) image->width, 0);
|
||||
g_string_append_printf(thum, "%s%c%u%c", THUMB_IMAGE_HEIGHT, 0,
|
||||
(unsigned) image->height, 0);
|
||||
|
||||
// Without a CMM, no conversion is attempted.
|
||||
if (color_managed) {
|
||||
@ -722,19 +731,19 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error)
|
||||
thum, "%s%c%s%c", THUMB_COLORSPACE, 0, THUMB_COLORSPACE_SRGB, 0);
|
||||
}
|
||||
|
||||
cairo_surface_t *max_size_surface = NULL;
|
||||
FivIoImage *max_size_image = NULL;
|
||||
for (int use = max_size; use >= FIV_THUMBNAIL_SIZE_MIN; use--) {
|
||||
cairo_surface_t *scaled =
|
||||
adjust_thumbnail(surface, fiv_thumbnail_sizes[use].size);
|
||||
FivIoImage *scaled =
|
||||
adjust_thumbnail(image, 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);
|
||||
g_free(path);
|
||||
|
||||
if (!max_size_surface)
|
||||
max_size_surface = scaled;
|
||||
if (!max_size_image)
|
||||
max_size_image = scaled;
|
||||
else
|
||||
cairo_surface_destroy(scaled);
|
||||
fiv_io_image_unref(scaled);
|
||||
}
|
||||
|
||||
g_string_free(thum, TRUE);
|
||||
@ -742,8 +751,8 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error)
|
||||
g_free(thumbnails_dir);
|
||||
g_free(sum);
|
||||
g_free(uri);
|
||||
cairo_surface_destroy(surface);
|
||||
return max_size_surface;
|
||||
fiv_io_image_unref(image);
|
||||
return fiv_io_image_to_surface(max_size_image);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@ -793,23 +802,23 @@ read_wide_thumbnail(const char *path, const Stat *st, GError **error)
|
||||
if (!thumbnail_uri)
|
||||
return NULL;
|
||||
|
||||
cairo_surface_t *surface =
|
||||
FivIoImage *image =
|
||||
fiv_io_open(&(FivIoOpenContext){.uri = thumbnail_uri}, error);
|
||||
g_free(thumbnail_uri);
|
||||
if (!surface)
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
bool sRGB = false;
|
||||
GBytes *thum = cairo_surface_get_user_data(surface, &fiv_io_key_thum);
|
||||
if (!thum) {
|
||||
if (!image->thum) {
|
||||
g_clear_error(error);
|
||||
set_error(error, "not a thumbnail");
|
||||
} else if (!check_wide_thumbnail_texts(thum, st, &sRGB)) {
|
||||
} else if (!check_wide_thumbnail_texts(image->thum, st, &sRGB)) {
|
||||
g_clear_error(error);
|
||||
set_error(error, "mismatch");
|
||||
} else {
|
||||
// TODO(p): Add a function or a non-valueless define to check
|
||||
// for CMM presence, then remove this ifdef.
|
||||
cairo_surface_t *surface = fiv_io_image_to_surface(image);
|
||||
#ifdef HAVE_LCMS2
|
||||
if (!sRGB)
|
||||
mark_thumbnail_lq(surface);
|
||||
@ -817,21 +826,21 @@ read_wide_thumbnail(const char *path, const Stat *st, GError **error)
|
||||
return surface;
|
||||
}
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
fiv_io_image_unref(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
read_png_thumbnail(const char *path, const Stat *st, GError **error)
|
||||
{
|
||||
cairo_surface_t *surface = fiv_io_open_png_thumbnail(path, error);
|
||||
if (!surface)
|
||||
FivIoImage *image = fiv_io_open_png_thumbnail(path, error);
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
GHashTable *texts = cairo_surface_get_user_data(surface, &fiv_io_key_text);
|
||||
GHashTable *texts = image->text;
|
||||
if (!texts) {
|
||||
set_error(error, "not a thumbnail");
|
||||
cairo_surface_destroy(surface);
|
||||
fiv_io_image_unref(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -843,16 +852,16 @@ read_png_thumbnail(const char *path, const Stat *st, GError **error)
|
||||
if (!text_uri || strcmp(text_uri, st->uri) ||
|
||||
!text_mtime || atol(text_mtime) != st->mtime) {
|
||||
set_error(error, "mismatch or not a thumbnail");
|
||||
cairo_surface_destroy(surface);
|
||||
fiv_io_image_unref(image);
|
||||
return NULL;
|
||||
}
|
||||
if (text_size && strtoull(text_size, NULL, 10) != st->size) {
|
||||
set_error(error, "file size mismatch");
|
||||
cairo_surface_destroy(surface);
|
||||
fiv_io_image_unref(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return surface;
|
||||
return fiv_io_image_to_surface(image);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
142
fiv-view.c
142
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);
|
||||
@ -810,15 +797,13 @@ stop_animating(FivView *self)
|
||||
|
||||
self->frame_time = 0;
|
||||
self->frame_update_connection = 0;
|
||||
self->remaining_loops = 0;
|
||||
g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_PLAYING]);
|
||||
}
|
||||
|
||||
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 +821,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,32 +859,39 @@ 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);
|
||||
|
||||
// Only restart looping the animation if it has stopped at the end.
|
||||
if (!self->remaining_loops) {
|
||||
self->remaining_loops = self->page->loops;
|
||||
if (self->remaining_loops && !self->frame->frame_next) {
|
||||
self->frame = self->page;
|
||||
gtk_widget_queue_draw(GTK_WIDGET(self));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
self->remaining_loops = 0;
|
||||
start_animating(self);
|
||||
gtk_widget_queue_resize(GTK_WIDGET(self));
|
||||
|
||||
@ -1027,7 +1018,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 +1056,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 +1091,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 +1353,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,7 +1365,7 @@ open_without_swapping_in(FivView *self, const char *uri)
|
||||
};
|
||||
|
||||
GError *error = NULL;
|
||||
cairo_surface_t *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);
|
||||
@ -1387,7 +1378,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.
|
||||
@ -1395,18 +1386,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.
|
||||
@ -1418,15 +1409,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);
|
||||
}
|
||||
@ -1435,31 +1426,35 @@ 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)))
|
||||
|
||||
if (step > 0) {
|
||||
// Decrease the loop counter as if running on a timer.
|
||||
(void) advance_frame(self);
|
||||
} else if (!step || !(self->frame = self->frame->frame_previous)) {
|
||||
self->frame = self->page;
|
||||
self->remaining_loops = 0;
|
||||
}
|
||||
gtk_widget_queue_draw(GTK_WIDGET(self));
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -1546,9 +1541,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:
|
||||
|
14
meson.build
14
meson.build
@ -161,11 +161,14 @@ tiff_tables = custom_target('tiff-tables.h',
|
||||
)
|
||||
|
||||
desktops = ['fiv.desktop', 'fiv-browse.desktop']
|
||||
exe = executable('fiv', 'fiv.c', 'fiv-view.c', 'fiv-io.c', 'fiv-context-menu.c',
|
||||
iolib = static_library('fiv-io', 'fiv-io.c', 'xdg.c', tiff_tables,
|
||||
dependencies : dependencies).extract_all_objects(recursive : true)
|
||||
exe = executable('fiv', 'fiv.c', 'fiv-view.c', 'fiv-context-menu.c',
|
||||
'fiv-browser.c', 'fiv-sidebar.c', 'fiv-thumbnail.c', 'fiv-collection.c',
|
||||
'fiv-io-model.c', 'xdg.c', tiff_tables, gresources, rc, config,
|
||||
install : true,
|
||||
'fiv-io-model.c', gresources, rc, config,
|
||||
objects : iolib,
|
||||
dependencies : dependencies,
|
||||
install : true,
|
||||
win_subsystem : 'windows',
|
||||
)
|
||||
|
||||
@ -193,8 +196,9 @@ if get_option('tools').enabled()
|
||||
endforeach
|
||||
|
||||
if gdkpixbuf.found()
|
||||
executable('benchmark-io', 'tools/benchmark-io.c', 'fiv-io.c', 'xdg.c',
|
||||
tiff_tables, dependencies : [dependencies, gdkpixbuf])
|
||||
executable('benchmark-io', 'tools/benchmark-io.c',
|
||||
objects : iolib,
|
||||
dependencies : [dependencies, gdkpixbuf])
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -41,14 +41,14 @@ one_file(const char *filename)
|
||||
.warnings = g_ptr_array_new_with_free_func(g_free),
|
||||
};
|
||||
|
||||
cairo_surface_t *loaded_by_us = fiv_io_open(&ctx, NULL);
|
||||
FivIoImage *loaded_by_us = fiv_io_open(&ctx, NULL);
|
||||
g_clear_object(&file);
|
||||
g_free((char *) ctx.uri);
|
||||
g_ptr_array_free(ctx.warnings, TRUE);
|
||||
if (!loaded_by_us)
|
||||
return;
|
||||
|
||||
cairo_surface_destroy(loaded_by_us);
|
||||
fiv_io_image_unref(loaded_by_us);
|
||||
us = timestamp() - since_us;
|
||||
|
||||
double since_pixbuf = timestamp(), pixbuf = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user