|
Packit |
a4058c |
/*
|
|
Packit |
a4058c |
* Copyright (C) 2013 Bastien Nocera <hadess@hadess.net>
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Authors: Bastien Nocera <hadess@hadess.net>
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
a4058c |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
a4058c |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
a4058c |
* (at your option) any later version.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
a4058c |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4058c |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
a4058c |
* GNU General Public License for more details.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* You should have received a copy of the GNU General Public License
|
|
Packit |
a4058c |
* along with this program; if not, write to the Free Software
|
|
Packit |
a4058c |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
*/
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include <string.h>
|
|
Packit |
a4058c |
#include <glib.h>
|
|
Packit |
a4058c |
#include <gio/gio.h>
|
|
Packit |
a4058c |
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include <math.h>
|
|
Packit |
a4058c |
#include <stdlib.h>
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include "gnome-thumbnailer-skeleton.h"
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#ifndef THUMBNAILER_USAGE
|
|
Packit |
a4058c |
#error "THUMBNAILER_USAGE must be set"
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static int output_size = 256;
|
|
Packit |
a4058c |
static gboolean g_fatal_warnings = FALSE;
|
|
Packit |
a4058c |
static char **filenames = NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#if !GDK_PIXBUF_CHECK_VERSION(2,36,5)
|
|
Packit |
a4058c |
/**
|
|
Packit |
a4058c |
* gnome_desktop_thumbnail_scale_down_pixbuf:
|
|
Packit |
a4058c |
* @pixbuf: a #GdkPixbuf
|
|
Packit |
a4058c |
* @dest_width: the desired new width
|
|
Packit |
a4058c |
* @dest_height: the desired new height
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Scales the pixbuf to the desired size. This function
|
|
Packit |
a4058c |
* is a lot faster than gdk-pixbuf when scaling down by
|
|
Packit |
a4058c |
* large amounts.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Return value: (transfer full): a scaled pixbuf
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Since: 2.2
|
|
Packit |
a4058c |
**/
|
|
Packit |
a4058c |
GdkPixbuf *
|
|
Packit |
a4058c |
gnome_desktop_thumbnail_scale_down_pixbuf (GdkPixbuf *pixbuf,
|
|
Packit |
a4058c |
int dest_width,
|
|
Packit |
a4058c |
int dest_height)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
int source_width, source_height;
|
|
Packit |
a4058c |
int s_x1, s_y1, s_x2, s_y2;
|
|
Packit |
a4058c |
int s_xfrac, s_yfrac;
|
|
Packit |
a4058c |
int dx, dx_frac, dy, dy_frac;
|
|
Packit |
a4058c |
div_t ddx, ddy;
|
|
Packit |
a4058c |
int x, y;
|
|
Packit |
a4058c |
int r, g, b, a;
|
|
Packit |
a4058c |
int n_pixels;
|
|
Packit |
a4058c |
gboolean has_alpha;
|
|
Packit |
a4058c |
guchar *dest, *src, *xsrc, *src_pixels;
|
|
Packit |
a4058c |
GdkPixbuf *dest_pixbuf;
|
|
Packit |
a4058c |
int pixel_stride;
|
|
Packit |
a4058c |
int source_rowstride, dest_rowstride;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (dest_width == 0 || dest_height == 0) {
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
source_width = gdk_pixbuf_get_width (pixbuf);
|
|
Packit |
a4058c |
source_height = gdk_pixbuf_get_height (pixbuf);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_assert (source_width >= dest_width);
|
|
Packit |
a4058c |
g_assert (source_height >= dest_height);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
ddx = div (source_width, dest_width);
|
|
Packit |
a4058c |
dx = ddx.quot;
|
|
Packit |
a4058c |
dx_frac = ddx.rem;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
ddy = div (source_height, dest_height);
|
|
Packit |
a4058c |
dy = ddy.quot;
|
|
Packit |
a4058c |
dy_frac = ddy.rem;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_assert (dx >= 1);
|
|
Packit |
a4058c |
g_assert (dy >= 1);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
|
|
Packit |
a4058c |
source_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
Packit |
a4058c |
src_pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8,
|
|
Packit |
a4058c |
dest_width, dest_height);
|
|
Packit |
a4058c |
dest = gdk_pixbuf_get_pixels (dest_pixbuf);
|
|
Packit |
a4058c |
dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixel_stride = (has_alpha)?4:3;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
s_y1 = 0;
|
|
Packit |
a4058c |
s_yfrac = -dest_height/2;
|
|
Packit |
a4058c |
while (s_y1 < source_height) {
|
|
Packit |
a4058c |
s_y2 = s_y1 + dy;
|
|
Packit |
a4058c |
s_yfrac += dy_frac;
|
|
Packit |
a4058c |
if (s_yfrac > 0) {
|
|
Packit |
a4058c |
s_y2++;
|
|
Packit |
a4058c |
s_yfrac -= dest_height;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
s_x1 = 0;
|
|
Packit |
a4058c |
s_xfrac = -dest_width/2;
|
|
Packit |
a4058c |
while (s_x1 < source_width) {
|
|
Packit |
a4058c |
s_x2 = s_x1 + dx;
|
|
Packit |
a4058c |
s_xfrac += dx_frac;
|
|
Packit |
a4058c |
if (s_xfrac > 0) {
|
|
Packit |
a4058c |
s_x2++;
|
|
Packit |
a4058c |
s_xfrac -= dest_width;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Average block of [x1,x2[ x [y1,y2[ and store in dest */
|
|
Packit |
a4058c |
r = g = b = a = 0;
|
|
Packit |
a4058c |
n_pixels = 0;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
src = src_pixels + s_y1 * source_rowstride + s_x1 * pixel_stride;
|
|
Packit |
a4058c |
for (y = s_y1; y < s_y2; y++) {
|
|
Packit |
a4058c |
xsrc = src;
|
|
Packit |
a4058c |
if (has_alpha) {
|
|
Packit |
a4058c |
for (x = 0; x < s_x2-s_x1; x++) {
|
|
Packit |
a4058c |
n_pixels++;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
r += xsrc[3] * xsrc[0];
|
|
Packit |
a4058c |
g += xsrc[3] * xsrc[1];
|
|
Packit |
a4058c |
b += xsrc[3] * xsrc[2];
|
|
Packit |
a4058c |
a += xsrc[3];
|
|
Packit |
a4058c |
xsrc += 4;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
} else {
|
|
Packit |
a4058c |
for (x = 0; x < s_x2-s_x1; x++) {
|
|
Packit |
a4058c |
n_pixels++;
|
|
Packit |
a4058c |
r += *xsrc++;
|
|
Packit |
a4058c |
g += *xsrc++;
|
|
Packit |
a4058c |
b += *xsrc++;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
src += source_rowstride;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_assert (n_pixels > 0);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (has_alpha) {
|
|
Packit |
a4058c |
if (a != 0) {
|
|
Packit |
a4058c |
*dest++ = r / a;
|
|
Packit |
a4058c |
*dest++ = g / a;
|
|
Packit |
a4058c |
*dest++ = b / a;
|
|
Packit |
a4058c |
*dest++ = a / n_pixels;
|
|
Packit |
a4058c |
} else {
|
|
Packit |
a4058c |
*dest++ = 0;
|
|
Packit |
a4058c |
*dest++ = 0;
|
|
Packit |
a4058c |
*dest++ = 0;
|
|
Packit |
a4058c |
*dest++ = 0;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
} else {
|
|
Packit |
a4058c |
*dest++ = r / n_pixels;
|
|
Packit |
a4058c |
*dest++ = g / n_pixels;
|
|
Packit |
a4058c |
*dest++ = b / n_pixels;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
s_x1 = s_x2;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
s_y1 = s_y2;
|
|
Packit |
a4058c |
dest += dest_rowstride - dest_width * pixel_stride;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return dest_pixbuf;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static char *
|
|
Packit |
a4058c |
get_target_uri (GFile *file)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GFileInfo *info;
|
|
Packit |
a4058c |
char *target;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
|
Packit |
a4058c |
if (info == NULL)
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
target = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI));
|
|
Packit |
a4058c |
g_object_unref (info);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return target;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static char *
|
|
Packit |
a4058c |
get_target_path (GFile *input)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
if (g_file_has_uri_scheme (input, "trash") != FALSE ||
|
|
Packit |
a4058c |
g_file_has_uri_scheme (input, "recent") != FALSE) {
|
|
Packit |
a4058c |
GFile *file;
|
|
Packit |
a4058c |
char *input_uri;
|
|
Packit |
a4058c |
char *input_path;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
input_uri = get_target_uri (input);
|
|
Packit |
a4058c |
file = g_file_new_for_uri (input_uri);
|
|
Packit |
a4058c |
g_free (input_uri);
|
|
Packit |
a4058c |
input_path = g_file_get_path (file);
|
|
Packit |
a4058c |
g_object_unref (file);
|
|
Packit |
a4058c |
return input_path;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
return g_file_get_path (input);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static const GOptionEntry entries[] = {
|
|
Packit |
a4058c |
{ "size", 's', 0, G_OPTION_ARG_INT, &output_size, "Size of the thumbnail in pixels", NULL },
|
|
Packit |
a4058c |
{"g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, "Make all warnings fatal", NULL},
|
|
Packit |
a4058c |
{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, "[INPUT FILE] [OUTPUT FILE]" },
|
|
Packit |
a4058c |
{ NULL }
|
|
Packit |
a4058c |
};
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
int main (int argc, char **argv)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
char *input_filename;
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf;
|
|
Packit |
a4058c |
GError *error = NULL;
|
|
Packit |
a4058c |
GOptionContext *context;
|
|
Packit |
a4058c |
GFile *input;
|
|
Packit |
a4058c |
const char *output;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#ifdef THUMBNAILER_RETURNS_PIXBUF
|
|
Packit |
a4058c |
/* Nothing */
|
|
Packit |
a4058c |
#elif THUMBNAILER_RETURNS_DATA
|
|
Packit |
a4058c |
char *data = NULL;
|
|
Packit |
a4058c |
gsize length;
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#if !GLIB_CHECK_VERSION(2, 36, 0)
|
|
Packit |
a4058c |
g_type_init ();
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Options parsing */
|
|
Packit |
a4058c |
context = g_option_context_new (THUMBNAILER_USAGE);
|
|
Packit |
a4058c |
g_option_context_add_main_entries (context, entries, NULL);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
|
|
Packit |
a4058c |
g_warning ("Couldn't parse command-line options: %s", error->message);
|
|
Packit |
a4058c |
g_error_free (error);
|
|
Packit |
a4058c |
return 1;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Set fatal warnings if required */
|
|
Packit |
a4058c |
if (g_fatal_warnings) {
|
|
Packit |
a4058c |
GLogLevelFlags fatal_mask;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
|
|
Packit |
a4058c |
fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
|
|
Packit |
a4058c |
g_log_set_always_fatal (fatal_mask);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (filenames == NULL || g_strv_length (filenames) != 2) {
|
|
Packit |
a4058c |
g_print ("Expects an input and an output file\n");
|
|
Packit |
a4058c |
return 1;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
input = g_file_new_for_commandline_arg (filenames[0]);
|
|
Packit |
a4058c |
input_filename = get_target_path (input);
|
|
Packit |
a4058c |
g_object_unref (input);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (input_filename == NULL) {
|
|
Packit |
a4058c |
g_warning ("Could not get file path for %s", filenames[0]);
|
|
Packit |
a4058c |
return 1;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
output = filenames[1];
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#ifdef THUMBNAILER_RETURNS_PIXBUF
|
|
Packit |
a4058c |
pixbuf = file_to_pixbuf (input_filename, output_size, &error);
|
|
Packit |
a4058c |
if (pixbuf != NULL) {
|
|
Packit |
a4058c |
int width, height;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
width = gdk_pixbuf_get_width (pixbuf);
|
|
Packit |
a4058c |
height = gdk_pixbuf_get_height (pixbuf);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Handle naive thumbnailers that don't resize */
|
|
Packit |
a4058c |
if (output_size != 0 &&
|
|
Packit |
a4058c |
(height > output_size || width > output_size)) {
|
|
Packit |
a4058c |
GdkPixbuf *scaled;
|
|
Packit |
a4058c |
double scale;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
scale = (double)output_size / MAX (width, height);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#if !GDK_PIXBUF_CHECK_VERSION(2,36,5)
|
|
Packit |
a4058c |
scaled = gnome_desktop_thumbnail_scale_down_pixbuf (pixbuf,
|
|
Packit |
a4058c |
floor (width * scale + 0.5),
|
|
Packit |
a4058c |
floor (height * scale + 0.5));
|
|
Packit |
a4058c |
#else
|
|
Packit |
a4058c |
scaled = gdk_pixbuf_scale_simple (pixbuf,
|
|
Packit |
a4058c |
floor (width * scale + 0.5),
|
|
Packit |
a4058c |
floor (height * scale + 0.5),
|
|
Packit |
a4058c |
GDK_INTERP_HYPER);
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
gdk_pixbuf_copy_options (pixbuf, scaled);
|
|
Packit |
a4058c |
g_object_unref (pixbuf);
|
|
Packit |
a4058c |
pixbuf = scaled;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
#elif THUMBNAILER_RETURNS_DATA
|
|
Packit |
a4058c |
data = file_to_data (input_filename, &length, &error);
|
|
Packit |
a4058c |
if (data) {
|
|
Packit |
a4058c |
GInputStream *mem_stream;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
mem_stream = g_memory_input_stream_new_from_data (data, length, g_free);
|
|
Packit |
a4058c |
pixbuf = gdk_pixbuf_new_from_stream_at_scale (mem_stream, output_size, -1, TRUE, NULL, &error);
|
|
Packit |
a4058c |
g_object_unref (mem_stream);
|
|
Packit |
a4058c |
} else {
|
|
Packit |
a4058c |
pixbuf = NULL;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
#else
|
|
Packit |
a4058c |
#error "One of THUMBNAILER_RETURNS_PIXBUF or THUMBNAILER_RETURNS_DATA must be set"
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
g_free (input_filename);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!pixbuf) {
|
|
Packit |
a4058c |
g_warning ("Could not thumbnail '%s': %s", filenames[0],
|
|
Packit |
a4058c |
error ? error->message : "Thumbnailer failed without returning an error");
|
|
Packit |
a4058c |
g_clear_error (&error);
|
|
Packit |
a4058c |
g_strfreev (filenames);
|
|
Packit |
a4058c |
return 1;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (gdk_pixbuf_save (pixbuf, output, "png", &error, NULL) == FALSE) {
|
|
Packit |
a4058c |
g_warning ("Couldn't save the thumbnail '%s' for file '%s': %s", output, filenames[0], error->message);
|
|
Packit |
a4058c |
g_error_free (error);
|
|
Packit |
a4058c |
return 1;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_object_unref (pixbuf);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return 0;
|
|
Packit |
a4058c |
}
|