Compare commits
	
		
			4 Commits
		
	
	
		
			4ca8825e02
			...
			84f8c9436f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 84f8c9436f | |||
| a8f7532abd | |||
| 8dfbd0dee2 | |||
| 930744e165 | 
| @ -439,6 +439,9 @@ entry_add_thumbnail(gpointer data, gpointer user_data) | ||||
| 		(intptr_t) cairo_surface_get_user_data( | ||||
| 			cached, &fiv_browser_key_mtime_msec) == self->mtime_msec) { | ||||
| 		self->thumbnail = cairo_surface_reference(cached); | ||||
| 		// TODO(p): If this hit is low-quality, see if a high-quality thumbnail
 | ||||
| 		// hasn't been produced without our knowledge (avoid launching a minion
 | ||||
| 		// unnecessarily; we might also shift the concern there).
 | ||||
| 	} else { | ||||
| 		cairo_surface_t *found = fiv_thumbnail_lookup( | ||||
| 			self->uri, self->mtime_msec, browser->item_size); | ||||
| @ -555,18 +558,31 @@ thumbnailer_reprocess_entry(FivBrowser *self, GBytes *output, Entry *entry) | ||||
| { | ||||
| 	g_clear_object(&entry->icon); | ||||
| 	g_clear_pointer(&entry->thumbnail, cairo_surface_destroy); | ||||
| 	if (!output || !(entry->thumbnail = rescale_thumbnail( | ||||
| 			fiv_io_deserialize(output), self->item_height))) { | ||||
| 		entry_add_thumbnail(entry, self); | ||||
| 		materialize_icon(self, entry); | ||||
| 	} else { | ||||
| 		// This choice of mtime favours unnecessary thumbnail reloading.
 | ||||
| 		cairo_surface_set_user_data(entry->thumbnail, | ||||
| 			&fiv_browser_key_mtime_msec, (void *) (intptr_t) entry->mtime_msec, | ||||
| 			NULL); | ||||
| 	} | ||||
| 
 | ||||
| 	gtk_widget_queue_resize(GTK_WIDGET(self)); | ||||
| 
 | ||||
| 	guint64 flags = 0; | ||||
| 	if (!output || !(entry->thumbnail = rescale_thumbnail( | ||||
| 			fiv_io_deserialize(output, &flags), self->item_height))) { | ||||
| 		entry_add_thumbnail(entry, self); | ||||
| 		materialize_icon(self, entry); | ||||
| 		return; | ||||
| 	} | ||||
| 	if ((flags & FIV_IO_SERIALIZE_LOW_QUALITY)) { | ||||
| 		cairo_surface_set_user_data(entry->thumbnail, &fiv_thumbnail_key_lq, | ||||
| 			(void *) (intptr_t) 1, NULL); | ||||
| 
 | ||||
| 		// TODO(p): Improve complexity; this will iterate the whole linked list.
 | ||||
| 		self->thumbnailers_queue = | ||||
| 			g_list_append(self->thumbnailers_queue, entry); | ||||
| 	} | ||||
| 
 | ||||
| 	// This choice of mtime favours unnecessary thumbnail reloading.
 | ||||
| 	cairo_surface_set_user_data(entry->thumbnail, | ||||
| 		&fiv_browser_key_mtime_msec, (void *) (intptr_t) entry->mtime_msec, | ||||
| 		NULL); | ||||
| 	g_hash_table_insert(self->thumbnail_cache, g_strdup(entry->uri), | ||||
| 		cairo_surface_reference(entry->thumbnail)); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| @ -628,11 +644,23 @@ thumbnailer_next(Thumbnailer *t) | ||||
| 	self->thumbnailers_queue = | ||||
| 		g_list_delete_link(self->thumbnailers_queue, self->thumbnailers_queue); | ||||
| 
 | ||||
| 	// Case analysis:
 | ||||
| 	//  - We haven't found any thumbnail for the entry at all
 | ||||
| 	//    (and it has a symbolic icon as a result):
 | ||||
| 	//    we want to fill the void ASAP, so go for embedded thumbnails first.
 | ||||
| 	//  - We've found one, but we're not quite happy with it:
 | ||||
| 	//    always run the full process for a high-quality wide thumbnail.
 | ||||
| 	//  - We can't end up here in any other cases.
 | ||||
| 	const char *argv_faster[] = {PROJECT_NAME, "--extract-thumbnail", | ||||
| 		"--thumbnail", fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name, | ||||
| 		"--", t->target->uri, NULL}; | ||||
| 	const char *argv_slower[] = {PROJECT_NAME, | ||||
| 		"--thumbnail", fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name, | ||||
| 		"--", t->target->uri, NULL}; | ||||
| 
 | ||||
| 	GError *error = NULL; | ||||
| 	t->minion = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error, | ||||
| 		PROJECT_NAME, "--thumbnail", | ||||
| 		fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name, "--", | ||||
| 		t->target->uri, NULL); | ||||
| 	t->minion = g_subprocess_newv(t->target->icon ? argv_faster : argv_slower, | ||||
| 		G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error); | ||||
| 	if (error) { | ||||
| 		g_warning("%s", error->message); | ||||
| 		g_error_free(error); | ||||
|  | ||||
							
								
								
									
										7
									
								
								fiv-io.c
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								fiv-io.c
									
									
									
									
									
								
							| @ -2891,11 +2891,12 @@ fiv_io_open_from_data( | ||||
| // --- Thumbnail passing utilities ---------------------------------------------
 | ||||
| 
 | ||||
| typedef struct { | ||||
| 	guint64 user_data; | ||||
| 	int width, height, stride, format; | ||||
| } CairoHeader; | ||||
| 
 | ||||
| void | ||||
| fiv_io_serialize_to_stdout(cairo_surface_t *surface) | ||||
| fiv_io_serialize_to_stdout(cairo_surface_t *surface, guint64 user_data) | ||||
| { | ||||
| 	if (!surface || cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) | ||||
| 		return; | ||||
| @ -2907,6 +2908,7 @@ fiv_io_serialize_to_stdout(cairo_surface_t *surface) | ||||
| #endif | ||||
| 
 | ||||
| 	CairoHeader h = { | ||||
| 		.user_data = user_data, | ||||
| 		.width = cairo_image_surface_get_width(surface), | ||||
| 		.height = cairo_image_surface_get_height(surface), | ||||
| 		.stride = cairo_image_surface_get_stride(surface), | ||||
| @ -2921,7 +2923,7 @@ fiv_io_serialize_to_stdout(cairo_surface_t *surface) | ||||
| } | ||||
| 
 | ||||
| cairo_surface_t * | ||||
| fiv_io_deserialize(GBytes *bytes) | ||||
| fiv_io_deserialize(GBytes *bytes, guint64 *user_data) | ||||
| { | ||||
| 	CairoHeader h = {}; | ||||
| 	GByteArray *array = g_bytes_unref_to_array(bytes); | ||||
| @ -2949,6 +2951,7 @@ fiv_io_deserialize(GBytes *bytes) | ||||
| 	static cairo_user_data_key_t key; | ||||
| 	cairo_surface_set_user_data( | ||||
| 		surface, &key, array, (cairo_destroy_func_t) g_byte_array_unref); | ||||
| 	*user_data = h.user_data; | ||||
| 	return surface; | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										6
									
								
								fiv-io.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								fiv-io.h
									
									
									
									
									
								
							| @ -102,8 +102,10 @@ cairo_surface_t *fiv_io_open_png_thumbnail(const char *path, GError **error); | ||||
| 
 | ||||
| // --- Thumbnail passing utilities ---------------------------------------------
 | ||||
| 
 | ||||
| void fiv_io_serialize_to_stdout(cairo_surface_t *surface); | ||||
| cairo_surface_t *fiv_io_deserialize(GBytes *bytes); | ||||
| enum { FIV_IO_SERIALIZE_LOW_QUALITY = 1 << 0 }; | ||||
| 
 | ||||
| void fiv_io_serialize_to_stdout(cairo_surface_t *surface, guint64 user_data); | ||||
| cairo_surface_t *fiv_io_deserialize(GBytes *bytes, guint64 *user_data); | ||||
| 
 | ||||
| // --- Filesystem --------------------------------------------------------------
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										159
									
								
								fiv-thumbnail.c
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								fiv-thumbnail.c
									
									
									
									
									
								
							| @ -30,6 +30,10 @@ | ||||
| #include "fiv-thumbnail.h" | ||||
| #include "xdg.h" | ||||
| 
 | ||||
| #ifdef HAVE_LIBRAW | ||||
| #include <libraw.h> | ||||
| #endif  // HAVE_LIBRAW
 | ||||
| 
 | ||||
| #ifndef __linux__ | ||||
| #define st_mtim st_mtimespec | ||||
| #endif  // ! __linux__
 | ||||
| @ -104,6 +108,28 @@ fiv_thumbnail_get_root(void) | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static cairo_surface_t * | ||||
| render(GFile *target, GBytes *data, gboolean *color_managed, GError **error) | ||||
| { | ||||
| 	FivIoOpenContext ctx = { | ||||
| 		.uri = g_file_get_uri(target), | ||||
| 		.screen_profile = fiv_io_profile_new_sRGB(), | ||||
| 		.screen_dpi = 96, | ||||
| 		.first_frame_only = TRUE, | ||||
| 		// Only using this array as a redirect.
 | ||||
| 		.warnings = g_ptr_array_new_with_free_func(g_free), | ||||
| 	}; | ||||
| 
 | ||||
| 	cairo_surface_t *surface = 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; | ||||
| } | ||||
| 
 | ||||
| // In principle similar to rescale_thumbnail() from fiv-browser.c.
 | ||||
| static cairo_surface_t * | ||||
| adjust_thumbnail(cairo_surface_t *thumbnail, double row_height) | ||||
| @ -175,6 +201,76 @@ adjust_thumbnail(cairo_surface_t *thumbnail, double row_height) | ||||
| 	return scaled; | ||||
| } | ||||
| 
 | ||||
| cairo_surface_t * | ||||
| fiv_thumbnail_extract(GFile *target, FivThumbnailSize max_size, GError **error) | ||||
| { | ||||
| 	const char *path = g_file_peek_path(target); | ||||
| 	if (!path) { | ||||
| 		set_error(error, "thumbnails will only be extracted from local files"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	GMappedFile *mf = g_mapped_file_new(path, FALSE, error); | ||||
| 	if (!mf) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	cairo_surface_t *surface = NULL; | ||||
| #ifndef HAVE_LIBRAW | ||||
| 	// TODO(p): Implement our own thumbnail extractors.
 | ||||
| 	set_error(error, "unsupported file"); | ||||
| #else  // HAVE_LIBRAW
 | ||||
| 	libraw_data_t *iprc = libraw_init( | ||||
| 		LIBRAW_OPIONS_NO_MEMERR_CALLBACK | LIBRAW_OPIONS_NO_DATAERR_CALLBACK); | ||||
| 	if (!iprc) { | ||||
| 		set_error(error, "failed to obtain a LibRaw handle"); | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	int err = 0; | ||||
| 	if ((err = libraw_open_buffer(iprc, (void *) g_mapped_file_get_contents(mf), | ||||
| 			 g_mapped_file_get_length(mf))) || | ||||
| 		(err = libraw_unpack_thumb(iprc))) { | ||||
| 		set_error(error, libraw_strerror(err)); | ||||
| 		goto fail_libraw; | ||||
| 	} | ||||
| 
 | ||||
| 	libraw_processed_image_t *image = libraw_dcraw_make_mem_thumb(iprc, &err); | ||||
| 	if (!image) { | ||||
| 		set_error(error, libraw_strerror(err)); | ||||
| 		goto fail_libraw; | ||||
| 	} | ||||
| 
 | ||||
| 	gboolean dummy = FALSE; | ||||
| 	switch (image->type) { | ||||
| 	case LIBRAW_IMAGE_JPEG: | ||||
| 		surface = render( | ||||
| 			target, g_bytes_new(image->data, image->data_size), &dummy, error); | ||||
| 		break; | ||||
| 	case LIBRAW_IMAGE_BITMAP: | ||||
| 		// TODO(p): Implement this one as well.
 | ||||
| 	default: | ||||
| 		set_error(error, "unsupported embedded thumbnail"); | ||||
| 	} | ||||
| 
 | ||||
| 	libraw_dcraw_clear_mem(image); | ||||
| fail_libraw: | ||||
| 	libraw_close(iprc); | ||||
| #endif  // HAVE_LIBRAW
 | ||||
| 
 | ||||
| fail: | ||||
| 	g_mapped_file_unref(mf); | ||||
| 	if (!surface || max_size < FIV_THUMBNAIL_SIZE_MIN || | ||||
| 		max_size > FIV_THUMBNAIL_SIZE_MAX) | ||||
| 		return surface; | ||||
| 
 | ||||
| 	cairo_surface_t *result = | ||||
| 		adjust_thumbnail(surface, fiv_thumbnail_sizes[max_size].size); | ||||
| 	cairo_surface_destroy(surface); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static WebPData | ||||
| encode_thumbnail(cairo_surface_t *surface) | ||||
| { | ||||
| @ -238,30 +334,7 @@ save_thumbnail(cairo_surface_t *thumbnail, const char *path, GString *thum) | ||||
| } | ||||
| 
 | ||||
| static cairo_surface_t * | ||||
| render(GFile *target, GBytes *data, gboolean *color_managed, GError **error) | ||||
| { | ||||
| 	FivIoOpenContext ctx = { | ||||
| 		.uri = g_file_get_uri(target), | ||||
| 		.screen_profile = fiv_io_profile_new_sRGB(), | ||||
| 		.screen_dpi = 96, | ||||
| 		.first_frame_only = TRUE, | ||||
| 		// Only using this array as a redirect.
 | ||||
| 		.warnings = g_ptr_array_new_with_free_func(g_free), | ||||
| 	}; | ||||
| 
 | ||||
| 	cairo_surface_t *surface = 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; | ||||
| } | ||||
| 
 | ||||
| static gboolean | ||||
| produce_fallback(GFile *target, FivThumbnailSize size, | ||||
| 	cairo_surface_t **surface, GError **error) | ||||
| produce_fallback(GFile *target, FivThumbnailSize size, GError **error) | ||||
| { | ||||
| 	goffset filesize = 0; | ||||
| 	GFileInfo *info = g_file_query_info(target, | ||||
| @ -276,40 +349,39 @@ produce_fallback(GFile *target, FivThumbnailSize size, | ||||
| 	// For example, we can employ magic checks.
 | ||||
| 	if (filesize > 10 << 20) { | ||||
| 		set_error(error, "oversize, not thumbnailing"); | ||||
| 		return FALSE; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	GBytes *data = g_file_load_bytes(target, NULL, NULL, error); | ||||
| 	if (!data) | ||||
| 		return FALSE; | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	gboolean color_managed = FALSE; | ||||
| 	cairo_surface_t *result = render(target, data, &color_managed, error); | ||||
| 	if (!result) | ||||
| 		return FALSE; | ||||
| 	cairo_surface_t *surface = render(target, data, &color_managed, error); | ||||
| 	if (!surface) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (!*surface) | ||||
| 		*surface = adjust_thumbnail(result, fiv_thumbnail_sizes[size].size); | ||||
| 	cairo_surface_destroy(result); | ||||
| 	return TRUE; | ||||
| 	cairo_surface_t *result = | ||||
| 		adjust_thumbnail(surface, fiv_thumbnail_sizes[size].size); | ||||
| 	cairo_surface_destroy(surface); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| gboolean | ||||
| fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, | ||||
| 	cairo_surface_t **max_size_surface, GError **error) | ||||
| cairo_surface_t * | ||||
| fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error) | ||||
| { | ||||
| 	g_return_val_if_fail(max_size >= FIV_THUMBNAIL_SIZE_MIN && | ||||
| 		max_size <= FIV_THUMBNAIL_SIZE_MAX, FALSE); | ||||
| 
 | ||||
| 	const gchar *path = g_file_peek_path(target); | ||||
| 	if (!path || !g_file_is_native(target) /* Don't save sftp://. */) | ||||
| 		return produce_fallback(target, max_size, max_size_surface, error); | ||||
| 		return produce_fallback(target, max_size, error); | ||||
| 
 | ||||
| 	// Make the TOCTTOU issue favour unnecessary reloading.
 | ||||
| 	GStatBuf st = {}; | ||||
| 	if (g_stat(path, &st)) { | ||||
| 		set_error(error, g_strerror(errno)); | ||||
| 		return FALSE; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	GError *e = NULL; | ||||
| @ -317,7 +389,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, | ||||
| 	if (!mf) { | ||||
| 		g_debug("%s: %s", path, e->message); | ||||
| 		g_error_free(e); | ||||
| 		return produce_fallback(target, max_size, max_size_surface, error); | ||||
| 		return produce_fallback(target, max_size, error); | ||||
| 	} | ||||
| 
 | ||||
| 	gsize filesize = g_mapped_file_get_length(mf); | ||||
| @ -326,7 +398,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, | ||||
| 		render(target, g_mapped_file_get_bytes(mf), &color_managed, error); | ||||
| 	g_mapped_file_unref(mf); | ||||
| 	if (!surface) | ||||
| 		return FALSE; | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	// Boilerplate copied from fiv_thumbnail_lookup().
 | ||||
| 	gchar *uri = g_file_get_uri(target); | ||||
| @ -354,6 +426,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, | ||||
| 			thum, "%s%c%s%c", THUMB_COLORSPACE, 0, THUMB_COLORSPACE_SRGB, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	cairo_surface_t *max_size_surface = NULL; | ||||
| 	for (int use = max_size; use >= FIV_THUMBNAIL_SIZE_MIN; use--) { | ||||
| 		cairo_surface_t *scaled = | ||||
| 			adjust_thumbnail(surface, fiv_thumbnail_sizes[use].size); | ||||
| @ -362,8 +435,8 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, | ||||
| 		save_thumbnail(scaled, path, thum); | ||||
| 		g_free(path); | ||||
| 
 | ||||
| 		if (!*max_size_surface) | ||||
| 			*max_size_surface = scaled; | ||||
| 		if (!max_size_surface) | ||||
| 			max_size_surface = scaled; | ||||
| 		else | ||||
| 			cairo_surface_destroy(scaled); | ||||
| 	} | ||||
| @ -374,7 +447,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, | ||||
| 	g_free(sum); | ||||
| 	g_free(uri); | ||||
| 	cairo_surface_destroy(surface); | ||||
| 	return TRUE; | ||||
| 	return max_size_surface; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
|  | ||||
| @ -45,20 +45,25 @@ typedef struct _FivThumbnailSizeInfo { | ||||
| 	const char *thumbnail_spec_name;    ///< thumbnail-spec directory name
 | ||||
| } FivThumbnailSizeInfo; | ||||
| 
 | ||||
| extern FivThumbnailSizeInfo fiv_thumbnail_sizes[FIV_THUMBNAIL_SIZE_COUNT]; | ||||
| 
 | ||||
| enum { FIV_THUMBNAIL_WIDE_COEFFICIENT = 2 }; | ||||
| 
 | ||||
| extern FivThumbnailSizeInfo fiv_thumbnail_sizes[FIV_THUMBNAIL_SIZE_COUNT]; | ||||
| 
 | ||||
| /// If non-NULL, indicates a thumbnail of insufficient quality.
 | ||||
| extern cairo_user_data_key_t fiv_thumbnail_key_lq; | ||||
| 
 | ||||
| /// Returns this user's root thumbnail directory.
 | ||||
| gchar *fiv_thumbnail_get_root(void); | ||||
| 
 | ||||
| /// Attempts to extract any low-quality thumbnail from fast targets.
 | ||||
| /// If `max_size` is a valid value, the image will be downscaled as appropriate.
 | ||||
| cairo_surface_t *fiv_thumbnail_extract( | ||||
| 	GFile *target, FivThumbnailSize max_size, GError **error); | ||||
| 
 | ||||
| /// Generates wide thumbnails of up to the specified size, saves them in cache.
 | ||||
| /// Returns the surface used for the maximum size (if the pointer was NULL).
 | ||||
| gboolean fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, | ||||
| 	cairo_surface_t **max_size_surface, GError **error); | ||||
| /// Returns the surface used for the maximum size, or an error.
 | ||||
| cairo_surface_t *fiv_thumbnail_produce( | ||||
| 	GFile *target, FivThumbnailSize max_size, GError **error); | ||||
| 
 | ||||
| /// Retrieves a thumbnail of the most appropriate quality and resolution
 | ||||
| /// for the target file.
 | ||||
|  | ||||
							
								
								
									
										66
									
								
								fiv.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								fiv.c
									
									
									
									
									
								
							| @ -1767,11 +1767,47 @@ static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \ | ||||
| 	} \ | ||||
| 	.fiv-information label { padding: 0 4px; }"; | ||||
| 
 | ||||
| static void | ||||
| output_thumbnail(const char *path_arg, gboolean extract, const char *size_arg) | ||||
| { | ||||
| 	if (!path_arg) | ||||
| 		exit_fatal("no path given"); | ||||
| 
 | ||||
| 	FivThumbnailSize size = FIV_THUMBNAIL_SIZE_COUNT; | ||||
| 	if (size_arg) { | ||||
| 		for (size = 0; size < FIV_THUMBNAIL_SIZE_COUNT; size++) { | ||||
| 			if (!strcmp( | ||||
| 					fiv_thumbnail_sizes[size].thumbnail_spec_name, size_arg)) | ||||
| 				break; | ||||
| 		} | ||||
| 		if (size >= FIV_THUMBNAIL_SIZE_COUNT) | ||||
| 			exit_fatal("unknown thumbnail size: %s", size_arg); | ||||
| 	} | ||||
| 
 | ||||
| 	GError *error = NULL; | ||||
| 	GFile *file = g_file_new_for_commandline_arg(path_arg); | ||||
| 	cairo_surface_t *surface = NULL; | ||||
| 	if (extract && (surface = fiv_thumbnail_extract(file, size, &error))) | ||||
| 		fiv_io_serialize_to_stdout(surface, FIV_IO_SERIALIZE_LOW_QUALITY); | ||||
| 	else if (size_arg && | ||||
| 		(g_clear_error(&error), | ||||
| 			(surface = fiv_thumbnail_produce(file, size, &error)))) | ||||
| 		fiv_io_serialize_to_stdout(surface, 0); | ||||
| 	else | ||||
| 		g_assert(error != NULL); | ||||
| 
 | ||||
| 	g_object_unref(file); | ||||
| 	if (error) | ||||
| 		exit_fatal("%s", error->message); | ||||
| 
 | ||||
| 	cairo_surface_destroy(surface); | ||||
| } | ||||
| 
 | ||||
| int | ||||
| main(int argc, char *argv[]) | ||||
| { | ||||
| 	gboolean show_version = FALSE, show_supported_media_types = FALSE, | ||||
| 		invalidate_cache = FALSE, browse = FALSE; | ||||
| 		invalidate_cache = FALSE, browse = FALSE, extract_thumbnail = FALSE; | ||||
| 	gchar **path_args = NULL, *thumbnail_size = NULL; | ||||
| 	const GOptionEntry options[] = { | ||||
| 		{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &path_args, | ||||
| @ -1782,9 +1818,12 @@ main(int argc, char *argv[]) | ||||
| 		{"browse", 0, G_OPTION_FLAG_IN_MAIN, | ||||
| 			G_OPTION_ARG_NONE, &browse, | ||||
| 			"Start in filesystem browsing mode", NULL}, | ||||
| 		{"extract-thumbnail", 0, G_OPTION_FLAG_IN_MAIN, | ||||
| 			G_OPTION_ARG_NONE, &extract_thumbnail, | ||||
| 			"Output any embedded thumbnail (superseding --thumbnail)", NULL}, | ||||
| 		{"thumbnail", 0, G_OPTION_FLAG_IN_MAIN, | ||||
| 			G_OPTION_ARG_STRING, &thumbnail_size, | ||||
| 			"Generate thumbnails for an image, up to the given size", "SIZE"}, | ||||
| 			"Generate thumbnails, up to SIZE, and output that size", "SIZE"}, | ||||
| 		{"invalidate-cache", 0, G_OPTION_FLAG_IN_MAIN, | ||||
| 			G_OPTION_ARG_NONE, &invalidate_cache, | ||||
| 			"Invalidate the wide thumbnail cache", NULL}, | ||||
| @ -1820,27 +1859,8 @@ main(int argc, char *argv[]) | ||||
| 	// TODO(p): Complain to the user if there's more than one argument.
 | ||||
| 	// Best show the help message, if we can figure that out.
 | ||||
| 	const gchar *path_arg = path_args ? path_args[0] : NULL; | ||||
| 	if (thumbnail_size) { | ||||
| 		if (!path_arg) | ||||
| 			exit_fatal("no path given"); | ||||
| 
 | ||||
| 		FivThumbnailSize size = 0; | ||||
| 		for (; size < FIV_THUMBNAIL_SIZE_COUNT; size++) | ||||
| 			if (!strcmp(fiv_thumbnail_sizes[size].thumbnail_spec_name, | ||||
| 					thumbnail_size)) | ||||
| 				break; | ||||
| 		if (size >= FIV_THUMBNAIL_SIZE_COUNT) | ||||
| 			exit_fatal("unknown thumbnail size: %s", thumbnail_size); | ||||
| 
 | ||||
| 		GFile *target = g_file_new_for_commandline_arg(path_arg); | ||||
| 		cairo_surface_t *surface = NULL; | ||||
| 		if (!fiv_thumbnail_produce(target, size, &surface, &error)) | ||||
| 			exit_fatal("%s", error->message); | ||||
| 		g_object_unref(target); | ||||
| 		if (surface) { | ||||
| 			fiv_io_serialize_to_stdout(surface); | ||||
| 			cairo_surface_destroy(surface); | ||||
| 		} | ||||
| 	if (extract_thumbnail || thumbnail_size) { | ||||
| 		output_thumbnail(path_arg, extract_thumbnail, thumbnail_size); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user