diff --git a/.cvsignore b/.cvsignore index 8d1f432839..c9b47b86a4 100644 --- a/.cvsignore +++ b/.cvsignore @@ -15,6 +15,7 @@ gimp.spec gimptool-1.3 gimptool-win32.c gimp-1.3.pc +gimpthumb-1.3.pc gimpui-1.3.pc gimp-zip intltool-extract diff --git a/ChangeLog b/ChangeLog index 19fd0133a4..7d729446a0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2003-11-26 Sven Neumann + + * Makefile.am: + * configure.in + * gimpthumb-1.3.pc.in + * libgimpthumb/Makefile.am + * libgimpthumb/gimpthumb-enums.h + * libgimpthumb/gimpthumb-types.h + * libgimpthumb/gimpthumb.def + * libgimpthumb/gimpthumb.h + * libgimpthumb/gimpthumb-utils.[ch] + * libgimpthumb/gimpthumbnail.[ch]: added a first draft of + libgimpthumb. The API isn't final and the implementation is + incomplete. It compiles but don't attempt to use it yet. + 2003-11-25 Sven Neumann * libgimpbase/gimplimits.h: added GIMP_MAX_MEMSIZE, an arbitrary diff --git a/Makefile.am b/Makefile.am index 4971e282b0..d94580539d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,10 @@ SUBDIRS = \ bin_SCRIPTS = gimptool-$(LT_RELEASE) @GIMPINSTALL@ pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = gimp-$(LT_RELEASE).pc gimpui-$(LT_RELEASE).pc +pkgconfig_DATA = \ + gimp-$(LT_RELEASE).pc \ + gimpthumb-$(LT_RELEASE).pc \ + gimpui-$(LT_RELEASE).pc EXTRA_SCRIPTS = gimpinstall-$(LT_RELEASE) diff --git a/configure.in b/configure.in index 4527579db4..81479c010b 100644 --- a/configure.in +++ b/configure.in @@ -1376,6 +1376,7 @@ libgimpbase/Makefile libgimpcolor/Makefile libgimpmath/Makefile libgimpmodule/Makefile +libgimpthumb/Makefile libgimpwidgets/Makefile app/Makefile app/base/Makefile @@ -1481,6 +1482,7 @@ po-plug-ins/Makefile.in po-script-fu/Makefile.in gimptool-1.3 gimp-1.3.pc +gimpthumb-1.3.pc gimpui-1.3.pc gimp.spec gimp-zip diff --git a/gimpthumb-1.3.pc.in b/gimpthumb-1.3.pc.in new file mode 100644 index 0000000000..3d6ab3c8e3 --- /dev/null +++ b/gimpthumb-1.3.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: GIMP Thumb +Description: GIMP Thumbnail Library +Version: @VERSION@ +Requires: gdk-pixbuf-2.0 +Libs: -L${libdir} -lgimpmath-1.3 -lgimpthumb-1.3 +Cflags: -I${includedir}/gimp-1.3 diff --git a/gimpthumb-2.0.pc.in b/gimpthumb-2.0.pc.in new file mode 100644 index 0000000000..3d6ab3c8e3 --- /dev/null +++ b/gimpthumb-2.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: GIMP Thumb +Description: GIMP Thumbnail Library +Version: @VERSION@ +Requires: gdk-pixbuf-2.0 +Libs: -L${libdir} -lgimpmath-1.3 -lgimpthumb-1.3 +Cflags: -I${includedir}/gimp-1.3 diff --git a/gimpthumb.pc.in b/gimpthumb.pc.in new file mode 100644 index 0000000000..3d6ab3c8e3 --- /dev/null +++ b/gimpthumb.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: GIMP Thumb +Description: GIMP Thumbnail Library +Version: @VERSION@ +Requires: gdk-pixbuf-2.0 +Libs: -L${libdir} -lgimpmath-1.3 -lgimpthumb-1.3 +Cflags: -I${includedir}/gimp-1.3 diff --git a/libgimpthumb/.cvsignore b/libgimpthumb/.cvsignore new file mode 100644 index 0000000000..80e2966c02 --- /dev/null +++ b/libgimpthumb/.cvsignore @@ -0,0 +1,11 @@ +Makefile +Makefile.in +makefile.mingw +gimpthumb-enums.c +*.lo +_libs +.libs +.deps +*.la +*.lib +*.exp diff --git a/libgimpthumb/Makefile.am b/libgimpthumb/Makefile.am new file mode 100644 index 0000000000..8805e91725 --- /dev/null +++ b/libgimpthumb/Makefile.am @@ -0,0 +1,102 @@ +## Process this file with automake to produce Makefile.in + +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(LT_RELEASE).la + +if PLATFORM_WIN32 +no_undefined = -no-undefined +endif + +if OS_WIN32 +gimpthumb_def = gimpthumb.def +libgimpthumb_export_symbols = -export-symbols gimpthumb.def + +install-libtool-import-lib: + $(INSTALL) .libs/libgimpthumb-$(LT_RELEASE).dll.a $(DESTDIR)$(libdir) + +uninstall-libtool-import-lib: + -rm $(DESTDIR)$(libdir)/libgimpthumb-$(LT_RELEASE).dll.a +else +install-libtool-import-lib: +uninstall-libtool-import-lib: +endif + +if MS_LIB_AVAILABLE +noinst_DATA = gimpthumb-$(LT_RELEASE).lib + +install-ms-lib: + $(INSTALL) gimpthumb-$(LT_RELEASE).lib $(DESTDIR)$(libdir) + +uninstall-ms-lib: + -rm $(DESTDIR)$(libdir)/gimpthumb-$(LT_RELEASE).lib + +gimpthumb-@LT_RELEASE@.lib: gimpthumb.def + lib -name:libgimpthumb-$(LT_RELEASE)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpthumb.def -out:$@ + +else +install-ms-lib: +uninstall-ms-lib: +endif + +libgimpthumbincludedir = $(includedir)/gimp-$(LT_RELEASE)/libgimpthumb + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"LibGimpThumb\" \ + @GIMP_THREAD_FLAGS@ + +INCLUDES = \ + -I$(top_srcdir) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +EXTRA_DIST = \ + makefile.msc \ + gimpthumb.def + +lib_LTLIBRARIES = libgimpthumb-1.3.la + +libgimpthumb_1_3_la_SOURCES = \ + gimpthumb.h \ + gimpthumb-enums.c \ + gimpthumb-enums.h \ + gimpthumb-types.h \ + gimpthumb-utils.c \ + gimpthumb-utils.h \ + gimpthumbnail.c \ + gimpthumbnail.h + +libgimpthumbinclude_HEADERS = \ + gimpthumb.h \ + gimpthumb-enums.h \ + gimpthumb-types.h \ + gimpthumbnail.h + +libgimpthumb_1_3_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + $(no_undefined) \ + $(libgimpthumb_export_symbols) + +libgimpthumb_1_3_la_LIBADD = $(libgimpmath) $(GDK_PIXBUF_LIBS) + + +install-data-local: install-ms-lib install-libtool-import-lib + +uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib + + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-tec +CLEANFILES = $(gen_sources) + +$(srcdir)/gimpthumb-enums.c: $(srcdir)/gimpthumb-enums.h $(GIMP_MKENUMS) + $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"gimpthumb-enums.h\"" \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --vhead "\nstatic const GEnumValue @enum_name@_enum_values[] =\n{" \ + --vprod " { @VALUENAME@, @valuedesc@, \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n};\n\nGType\n@enum_name@_get_type (void)\n{\n static GType enum_type = 0;\n\n if (!enum_type)\n enum_type = g_enum_register_static (\"@EnumName@\", @enum_name@_enum_values);\n\n return enum_type;\n}\n" \ + $(srcdir)/gimpthumb-enums.h > xgen-tec \ + && cp xgen-tec $(@F) \ + && rm -f xgen-tec diff --git a/libgimpthumb/gimpthumb-enums.h b/libgimpthumb/gimpthumb-enums.h new file mode 100644 index 0000000000..6bbc189401 --- /dev/null +++ b/libgimpthumb/gimpthumb-enums.h @@ -0,0 +1,60 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * http://triq.net/~pearl/thumbnail-spec/ + * + * Copyright (C) 2001-2003 Sven Neumann + * Michael Natterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIMP_THUMB_ENUMS_H__ +#define __GIMP_THUMB_ENUMS_H__ + + +#define GIMP_TYPE_THUMB_SIZE (gimp_thumb_size_get_type ()) + +GType gimp_thumb_size_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_THUMB_SIZE_FAIL = 0, + GIMP_THUMB_SIZE_NORMAL = 128, + GIMP_THUMB_SIZE_LARGE = 256 +} GimpThumbSize; + + +#define GIMP_TYPE_THUMB_STATE (gimp_thumb_state_get_type ()) + +GType gimp_thumb_state_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_THUMB_STATE_UNKNOWN, + GIMP_THUMB_STATE_REMOTE, + GIMP_THUMB_STATE_NOT_FOUND, + GIMP_THUMB_STATE_EXISTS, + GIMP_THUMB_STATE_THUMBNAIL_NOT_FOUND, + GIMP_THUMB_STATE_THUMBNAIL_EXISTS, + GIMP_THUMB_STATE_THUMBNAIL_OLD, + GIMP_THUMB_STATE_THUMBNAIL_FAILED, + GIMP_THUMB_STATE_THUMBNAIL_OK +} GimpThumbState; + + +#endif /* __GIMP_THUMB_ENUMS_H__ */ diff --git a/libgimpthumb/gimpthumb-types.h b/libgimpthumb/gimpthumb-types.h new file mode 100644 index 0000000000..5fc51cd780 --- /dev/null +++ b/libgimpthumb/gimpthumb-types.h @@ -0,0 +1,39 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * http://triq.net/~pearl/thumbnail-spec/ + * + * Copyright (C) 2001-2003 Sven Neumann + * Michael Natterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIMP_THUMB_TYPES_H__ +#define __GIMP_THUMB_TYPES_H__ + + +#include + +G_BEGIN_DECLS + +typedef struct _GimpThumbnail GimpThumbnail; + +G_END_DECLS + + +#endif /* __GIMP_THUMB_TYPES_H__ */ diff --git a/libgimpthumb/gimpthumb-utils.c b/libgimpthumb/gimpthumb-utils.c new file mode 100644 index 0000000000..cceab1dd2b --- /dev/null +++ b/libgimpthumb/gimpthumb-utils.c @@ -0,0 +1,260 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * http://triq.net/~pearl/thumbnail-spec/ + * + * Copyright (C) 2001-2003 Sven Neumann + * Michael Natterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#include +#endif + +#include "libgimpmath/gimpmath.h" + +#include "gimpthumb-types.h" +#include "gimpthumb-utils.h" + + +static const gchar * gimp_thumb_png_name (const gchar *uri); +static void gimp_thumb_exit (void); + + + +static gboolean gimp_thumb_initialized = FALSE; +static gint thumb_num_sizes = 0; +static gint *thumb_sizes = NULL; +static gchar *thumb_dir = NULL; +static gchar **thumb_subdirs = NULL; +static gchar *thumb_fail_subdir = NULL; + + + +gboolean +gimp_thumb_init (const gchar *creator, + const gchar *thumb_basedir) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + gint i; + + g_return_val_if_fail (creator != NULL, FALSE); + + if (gimp_thumb_initialized) + gimp_thumb_exit (); + + thumb_dir = (thumb_basedir ? + g_strdup (thumb_basedir) : + g_build_filename (g_get_home_dir(), ".thumbnails", NULL)); + + enum_class = g_type_class_ref (GIMP_TYPE_THUMB_SIZE); + + thumb_num_sizes = enum_class->n_values; + thumb_subdirs = g_new (gchar *, thumb_num_sizes); + + for (i = 0, enum_value = enum_class->values; + i < enum_class->n_values; + i++, enum_value++) + { + thumb_sizes[i] = enum_value->value; + thumb_subdirs[i] = g_build_filename (thumb_dir, + enum_value->value_nick, NULL); + } + + thumb_fail_subdir = thumb_subdirs[0]; + thumb_subdirs[0] = g_build_filename (thumb_fail_subdir, creator, NULL); + + gimp_thumb_initialized = TRUE; + + return gimp_thumb_initialized; +} + +gchar * +gimp_thumb_png_thumb_path (const gchar *uri, + GimpThumbSize *size) +{ + const gchar *name; + gchar *thumb_name = NULL; + gint i = 0; + + g_return_val_if_fail (gimp_thumb_initialized, NULL); + + if (strstr (uri, "/.thumbnails/")) + return NULL; + + name = gimp_thumb_png_name (uri); + + if (*size != GIMP_THUMB_SIZE_FAIL) + { + for (i = 1; + i < thumb_num_sizes && thumb_sizes[i] < *size; + i++) + /* nothing */; + + if (i == thumb_num_sizes) + i--; + } + + *size = thumb_sizes[i]; + + if (! g_file_test (thumb_subdirs[i], G_FILE_TEST_IS_DIR)) + { + if (g_file_test (thumb_dir, G_FILE_TEST_IS_DIR) || + (mkdir (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0)) + { + if (i == 0) + mkdir (thumb_fail_subdir, S_IRUSR | S_IWUSR | S_IXUSR); + + mkdir (thumb_subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR); + } + + if (! g_file_test (thumb_subdirs[i], G_FILE_TEST_IS_DIR)) + { + g_message ("Failed to create thumbnail folder '%s'.", + thumb_subdirs[i]); + return NULL; + } + } + + thumb_name = g_build_filename (thumb_subdirs[i], name, NULL); + + return thumb_name; +} + +gchar * +gimp_thumbnail_find_png_thumb (const gchar *uri, + GimpThumbSize *size) +{ + const gchar *name; + gchar *thumb_name; + gint i, n; + + g_return_val_if_fail (gimp_thumb_initialized, NULL); + + name = gimp_thumb_png_name (uri); + + for (i = 1; i < thumb_num_sizes && thumb_sizes[i] < *size; i++) + /* do nothing */; + + n = i; + + for (; i < thumb_num_sizes; i++) + { + thumb_name = g_build_filename (thumb_subdirs[i], name, NULL); + + if (gimp_thumb_file_test (thumb_name, NULL, NULL)) + { + *size = thumb_sizes[i]; + return thumb_name; + } + + g_free (thumb_name); + } + + for (i = n - 1; i >= 0; i--) + { + thumb_name = g_build_filename (thumb_subdirs[i], name, NULL); + + if (gimp_thumb_file_test (thumb_name, NULL, NULL)) + { + *size = thumb_sizes[i]; + return thumb_name; + } + + g_free (thumb_name); + } + + return NULL; +} + +gboolean +gimp_thumb_file_test (const gchar *filename, + gint64 *mtime, + gint64 *size) +{ + struct stat s; + + if (stat (filename, &s) == 0 && (S_ISREG (s.st_mode))) + { + if (mtime) + *mtime = s.st_mtime; + if (size) + *size = s.st_size; + + return TRUE; + } + + return FALSE; +} + +static void +gimp_thumb_exit (void) +{ + gint i; + + g_free (thumb_dir); + g_free (thumb_sizes); + for (i = 0; i < thumb_num_sizes; i++) + g_free (thumb_subdirs[i]); + g_free (thumb_subdirs); + g_free (thumb_fail_subdir); + + thumb_num_sizes = 0; + thumb_sizes = NULL; + thumb_dir = NULL; + thumb_subdirs = NULL; + thumb_fail_subdir = NULL; + gimp_thumb_initialized = FALSE; +} + +static const gchar * +gimp_thumb_png_name (const gchar *uri) +{ + static gchar name[40]; + guchar digest[16]; + guchar n; + gint i; + + gimp_md5_get_digest (uri, -1, digest); + + for (i = 0; i < 16; i++) + { + n = (digest[i] >> 4) & 0xF; + name[i * 2] = (n > 9) ? 'a' + n - 10 : '0' + n; + + n = digest[i] & 0xF; + name[i * 2 + 1] = (n > 9) ? 'a' + n - 10 : '0' + n; + } + + strncpy (name + 32, ".png", 5); + + return (const gchar *) name; +} diff --git a/libgimpthumb/gimpthumb-utils.h b/libgimpthumb/gimpthumb-utils.h new file mode 100644 index 0000000000..a426cca249 --- /dev/null +++ b/libgimpthumb/gimpthumb-utils.h @@ -0,0 +1,45 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * http://triq.net/~pearl/thumbnail-spec/ + * + * Copyright (C) 2001-2003 Sven Neumann + * Michael Natterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIMP_THUMB_UTILS_H__ +#define __GIMP_THUMB_UTILS_H__ + +G_BEGIN_DECLS + +gboolean gimp_thumb_init (const gchar *creator, + const gchar *thumb_basedir); + +gchar * gimp_thumb_png_thumb_path (const gchar *uri, + GimpThumbSize *size); +gchar * gimp_thumbnail_find_png_thumb (const gchar *uri, + GimpThumbSize *size); + +gboolean gimp_thumb_file_test (const gchar *filename, + gint64 *mtime, + gint64 *size); + +G_END_DECLS + +#endif /* __GIMP_THUMB_UTILS_H__ */ diff --git a/libgimpthumb/gimpthumb.def b/libgimpthumb/gimpthumb.def new file mode 100644 index 0000000000..aabd2247c0 --- /dev/null +++ b/libgimpthumb/gimpthumb.def @@ -0,0 +1 @@ +EXPORTS diff --git a/libgimpthumb/gimpthumb.h b/libgimpthumb/gimpthumb.h new file mode 100644 index 0000000000..76b4caafdd --- /dev/null +++ b/libgimpthumb/gimpthumb.h @@ -0,0 +1,35 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * http://triq.net/~pearl/thumbnail-spec/ + * + * Copyright (C) 2001-2003 Sven Neumann + * Michael Natterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIMP_THUMB_H__ +#define __GIMP_THUMB_H__ + + +#include +#include +#include + + +#endif /* __GIMP_THUMB_H__ */ diff --git a/libgimpthumb/gimpthumbnail.c b/libgimpthumb/gimpthumbnail.c new file mode 100644 index 0000000000..f23ba42a8d --- /dev/null +++ b/libgimpthumb/gimpthumbnail.c @@ -0,0 +1,614 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * http://triq.net/~pearl/thumbnail-spec/ + * + * Copyright (C) 2001-2003 Sven Neumann + * Michael Natterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include + +#include "gimpthumb-types.h" +#include "gimpthumb-utils.h" +#include "gimpthumbnail.h" + + +#define TAG_DESCRIPTION "tEXt::Description" +#define TAG_SOFTWARE "tEXt::Software" +#define TAG_THUMB_URI "tEXt::Thumb::URI" +#define TAG_THUMB_MTIME "tEXt::Thumb::MTime" +#define TAG_THUMB_FILESIZE "tEXt::Thumb::Size" +#define TAG_THUMB_IMAGE_WIDTH "tEXt::Thumb::Image::Width" +#define TAG_THUMB_IMAGE_HEIGHT "tEXt::Thumb::Image::Height" +#define TAG_THUMB_GIMP_TYPE "tEXt::Thumb::X-GIMP::Type" +#define TAG_THUMB_GIMP_LAYERS "tEXt::Thumb::X-GIMP::Layers" + + +enum +{ + PROP_0, + PROP_URI, + PROP_STATE, + PROP_MTIME, + PROP_FILESIZE, + PROP_WIDTH, + PROP_HEIGHT, + PROP_TYPE, + PROP_NUM_LAYERS +}; + + +static void gimp_thumbnail_class_init (GimpThumbnailClass *klass); +static void gimp_thumbnail_init (GimpThumbnail *thumbnail); +static void gimp_thumbnail_finalize (GObject *object); +static void gimp_thumbnail_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_thumbnail_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_thumbnail_reset_info (GimpThumbnail *thumbnail); + +static GdkPixbuf * gimp_thumbnail_read_png_thumb (GimpThumbnail *thumbnail, + GimpThumbSize thumb_size, + GError **error); + + +static GObjectClass *parent_class = NULL; + + +GType +gimp_thumbnail_get_type (void) +{ + static GType thumbnail_type = 0; + + if (!thumbnail_type) + { + static const GTypeInfo thumbnail_info = + { + sizeof (GimpThumbnailClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gimp_thumbnail_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GimpThumbnail), + 0, /* n_preallocs */ + (GInstanceInitFunc) gimp_thumbnail_init, + }; + + thumbnail_type = g_type_register_static (G_TYPE_OBJECT, + "GimpThumbnail", + &thumbnail_info, 0); + } + + return thumbnail_type; +} + +static void +gimp_thumbnail_class_init (GimpThumbnailClass *klass) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (klass); + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gimp_thumbnail_finalize; + object_class->set_property = gimp_thumbnail_set_property; + object_class->get_property = gimp_thumbnail_get_property; + + g_object_class_install_property (object_class, + PROP_URI, + g_param_spec_string ("uri", NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_STATE, + g_param_spec_enum ("state", NULL, + NULL, + GIMP_TYPE_THUMB_STATE, + GIMP_THUMB_STATE_UNKNOWN, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_MTIME, + g_param_spec_int64 ("image-mtime", NULL, + NULL, + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_FILESIZE, + g_param_spec_int64 ("image-filesize", NULL, + NULL, + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_WIDTH, + g_param_spec_int ("image-width", NULL, + NULL, + 0, G_MAXINT, 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_HEIGHT, + g_param_spec_int ("image-height", NULL, + NULL, + 0, G_MAXINT, 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_TYPE, + g_param_spec_string ("image-type", NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_TYPE, + g_param_spec_int ("image-num-layers", NULL, + NULL, + 0, G_MAXINT, 0, + G_PARAM_READWRITE)); +} + +static void +gimp_thumbnail_init (GimpThumbnail *thumbnail) +{ + thumbnail->uri = NULL; + thumbnail->state = GIMP_THUMB_STATE_UNKNOWN; + + thumbnail->thumb_filename = NULL; + thumbnail->thumb_mtime = 0; + thumbnail->thumb_filesize = 0; + + thumbnail->image_filename = NULL; + thumbnail->image_mtime = 0; + thumbnail->image_filesize = 0; + + thumbnail->image_width = 0; + thumbnail->image_height = 0; + thumbnail->image_type = 0; + thumbnail->image_num_layers = 0; +} + +static void +gimp_thumbnail_finalize (GObject *object) +{ + GimpThumbnail *thumbnail = GIMP_THUMBNAIL (object); + + if (thumbnail->uri) + { + g_free (thumbnail->uri); + thumbnail->uri = NULL; + } + if (thumbnail->thumb_filename) + { + g_free (thumbnail->thumb_filename); + thumbnail->thumb_filename = NULL; + } + if (thumbnail->image_filename) + { + g_free (thumbnail->image_filename); + thumbnail->image_filename = NULL; + } + if (thumbnail->image_type) + { + g_free (thumbnail->image_type); + thumbnail->image_type = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_thumbnail_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpThumbnail *thumbnail = GIMP_THUMBNAIL (object); + + switch (property_id) + { + case PROP_URI: + gimp_thumbnail_set_uri (GIMP_THUMBNAIL (object), + g_value_get_string (value)); + break; + case PROP_MTIME: + thumbnail->image_mtime = g_value_get_int64 (value); + break; + case PROP_FILESIZE: + thumbnail->image_filesize = g_value_get_int64 (value); + break; + case PROP_WIDTH: + thumbnail->image_width = g_value_get_int (value); + break; + case PROP_HEIGHT: + thumbnail->image_height = g_value_get_int (value); + break; + case PROP_TYPE: + g_free (thumbnail->image_type); + thumbnail->image_type = g_value_dup_string (value); + break; + case PROP_NUM_LAYERS: + thumbnail->image_num_layers = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_thumbnail_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpThumbnail *thumbnail = GIMP_THUMBNAIL (object); + + switch (property_id) + { + case PROP_URI: + g_value_set_string (value, thumbnail->uri); + break; + case PROP_STATE: + g_value_set_enum (value, thumbnail->state); + break; + case PROP_MTIME: + g_value_set_int64 (value, thumbnail->image_mtime); + break; + case PROP_FILESIZE: + g_value_set_int64 (value, thumbnail->image_filesize); + break; + case PROP_WIDTH: + g_value_set_int (value, thumbnail->image_width); + break; + case PROP_HEIGHT: + g_value_set_int (value, thumbnail->image_height); + break; + case PROP_TYPE: + g_value_set_string (value, thumbnail->image_type); + break; + case PROP_NUM_LAYERS: + g_value_set_int (value, thumbnail->image_height); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_thumbnail_reset_info (GimpThumbnail *thumbnail) +{ + g_object_set (thumbnail, + "image-width", 0, + "image-height", 0, + "image-type", NULL, + "image-num-layers", 0, + NULL); +} + +GimpThumbnail * +gimp_thumbnail_new (void) +{ + return g_object_new (GIMP_TYPE_THUMBNAIL, NULL); +} + +void +gimp_thumbnail_set_uri (GimpThumbnail *thumbnail, + const gchar *uri) +{ + g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail)); + + if (thumbnail->thumb_filename) + { + g_free (thumbnail->thumb_filename); + thumbnail->thumb_filename = NULL; + } + + thumbnail->thumb_filesize = 0; + thumbnail->thumb_mtime = 0; + + g_object_set (thumbnail, + "state", GIMP_THUMB_STATE_UNKNOWN, + "image-filesize", 0, + "image-mtime", 0, + "image-width", 0, + "image-height", 0, + "image-type", NULL, + "image-num-layers", 0, + NULL); +} + +void +gimp_thumbnail_update (GimpThumbnail *thumbnail) +{ + GimpThumbState state = thumbnail->state; + gchar *thumbname = NULL; + gint64 image_filesize = 0; + gint64 image_mtime = 0; + + if (! thumbnail->uri) + return; + + /* do we know the image filename already ? */ + switch (state) + { + case GIMP_THUMB_STATE_UNKNOWN: + g_return_if_fail (thumbnail->image_filename == NULL); + + thumbnail->image_filename = g_filename_from_uri (thumbnail->uri, + NULL, NULL); + + if (! thumbnail->image_filename) + state = GIMP_THUMB_STATE_REMOTE; + + break; + + case GIMP_THUMB_STATE_REMOTE: + break; + + default: + g_return_if_fail (thumbnail->image_filename != NULL); + break; + } + + /* check if the imagefile changed */ + switch (state) + { + case GIMP_THUMB_STATE_REMOTE: + break; + + default: + if (gimp_thumb_file_test (thumbnail->image_filename, + &image_mtime, &image_filesize)) + { + if (image_mtime != thumbnail->image_mtime || + image_filesize != thumbnail->image_filesize) + { + state = GIMP_THUMB_STATE_EXISTS; + } + } + else + { + state = GIMP_THUMB_STATE_NOT_FOUND; + } + break; + } + + /* do we know the thumbnail filename ? */ + switch (state) + { + case GIMP_THUMB_STATE_EXISTS: + thumbname = gimp_thumbnail_find_png_thumb (thumbnail->uri, NULL); + break; + +#if 0 + thumbname = gimp_thumbnail_find_png_thumb (thumbnail->uri, NULL); + + thumbnail->state = GIMP_THUMB_STATE_THUMBNAIL_NOT_FOUND; + thumbnail->mtime = thumb_mtime; + thumbnail->filesize = thumb_filesize; + + thumbname = gimp_thumbnail_find_png_thumb (uri, NULL); + + if (thumbname) + thumbnail->state = GIMP_THUMB_STATE_THUMBNAIL_EXISTS; + + cleanup: + g_free (filename); + g_free (thumbname); +#endif + + + case GIMP_THUMB_STATE_UNKNOWN: + case GIMP_THUMB_STATE_REMOTE: + case GIMP_THUMB_STATE_NOT_FOUND: + case GIMP_THUMB_STATE_THUMBNAIL_NOT_FOUND: + case GIMP_THUMB_STATE_THUMBNAIL_EXISTS: + case GIMP_THUMB_STATE_THUMBNAIL_OLD: + case GIMP_THUMB_STATE_THUMBNAIL_FAILED: + case GIMP_THUMB_STATE_THUMBNAIL_OK: + break; + } +} + +gboolean +gimp_thumbnail_set_filename (GimpThumbnail *thumbnail, + const gchar *filename, + GError **error) +{ + gchar *uri = NULL; + + g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (filename) + uri = g_filename_to_uri (filename, NULL, error); + + gimp_thumbnail_set_uri (thumbnail, uri); + + return (!filename || uri); +} + + +GimpThumbState +gimp_thumbnail_get_state (GimpThumbnail *thumbnail) +{ + g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), + GIMP_THUMB_STATE_UNKNOWN); + + return thumbnail->state; +} + +static void +gimp_thumbnail_set_info_from_pixbuf (GimpThumbnail *thumbnail, + GdkPixbuf *pixbuf) +{ + const gchar *option; + gint n; + + g_object_freeze_notify (G_OBJECT (thumbnail)); + + gimp_thumbnail_reset_info (thumbnail); + + option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_WIDTH); + if (option && sscanf (option, "%d", &n) == 1) + thumbnail->image_width = n; + + option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_HEIGHT); + if (option && sscanf (option, "%d", &n) == 1) + thumbnail->image_height = n; + + thumbnail->image_type = + g_strdup (gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_TYPE)); + + option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_LAYERS); + if (option && sscanf (option, "%d", &n) == 1) + thumbnail->image_num_layers = 0; +} + +GdkPixbuf * +gimp_thumbnail_get_pixbuf (GimpThumbnail *thumbnail, + GimpThumbSize size, + GError **error) +{ + GdkPixbuf *pixbuf; + + if (! thumbnail->uri) + return NULL; + + pixbuf = gimp_thumbnail_read_png_thumb (thumbnail, size, error); + + if (pixbuf) + { + gint width = gdk_pixbuf_get_width (pixbuf); + gint height = gdk_pixbuf_get_height (pixbuf); + + if (width > size || height > size) + { + GdkPixbuf *scaled; + + /* FIXME: aspect ratio */ + scaled = gdk_pixbuf_scale_simple (pixbuf, + size, size, + GDK_INTERP_BILINEAR); + + g_object_unref (pixbuf); + pixbuf = scaled; + } + } + + return pixbuf; +} + +/* PNG thumbnail handling routines according to the + Thumbnail Managing Standard http://triq.net/~pearl/thumbnail-spec/ */ + +static GdkPixbuf * +gimp_thumbnail_read_png_thumb (GimpThumbnail *thumbnail, + GimpThumbSize thumb_size, + GError **error) +{ + GimpThumbState old_state; + GdkPixbuf *pixbuf = NULL; + gchar *thumbname = NULL; + const gchar *option; + gint64 image_mtime; + gint64 image_size; + + if (thumbnail->state < GIMP_THUMB_STATE_EXISTS) + return NULL; + + old_state = thumbnail->state; + + /* try to locate a thumbnail for this image */ + thumbnail->state = GIMP_THUMB_STATE_THUMBNAIL_NOT_FOUND; + + thumbname = gimp_thumbnail_find_png_thumb (thumbnail->uri, &thumb_size); + + if (!thumbname) + goto cleanup; + + pixbuf = gdk_pixbuf_new_from_file (thumbname, error); + + if (! pixbuf) + goto cleanup; + + g_free (thumbname); + thumbname = NULL; + + /* URI and mtime from the thumbnail need to match our file */ + + option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_URI); + if (!option || strcmp (option, thumbnail->uri)) + goto cleanup; + + thumbnail->state = GIMP_THUMB_STATE_THUMBNAIL_OLD; + + option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MTIME); + if (!option || sscanf (option, "%" G_GINT64_FORMAT, &image_mtime) != 1) + goto cleanup; + + option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_FILESIZE); + if (option && sscanf (option, "%" G_GINT64_FORMAT, &image_size) != 1) + goto cleanup; + + /* TAG_THUMB_FILESIZE is optional but must match if present */ + if (image_mtime == thumbnail->image_mtime && + (option == NULL || image_size == thumbnail->image_filesize)) + { + if (! thumb_size) + thumbnail->state = GIMP_THUMB_STATE_THUMBNAIL_FAILED; + else + thumbnail->state = GIMP_THUMB_STATE_THUMBNAIL_OK; + } + + if (! thumb_size) + { + gimp_thumbnail_reset_info (thumbnail); + goto cleanup; + } + + gimp_thumbnail_set_info_from_pixbuf (thumbnail, pixbuf); + + cleanup: + if (thumbnail->state != old_state) + g_object_notify (G_OBJECT (thumbnail), "state"); + + g_free (thumbname); + + if (thumbnail->state != GIMP_THUMB_STATE_THUMBNAIL_OK) + { + g_object_unref (pixbuf); + return NULL; + } + + return pixbuf; +} diff --git a/libgimpthumb/gimpthumbnail.h b/libgimpthumb/gimpthumbnail.h new file mode 100644 index 0000000000..4d311a77aa --- /dev/null +++ b/libgimpthumb/gimpthumbnail.h @@ -0,0 +1,91 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * http://triq.net/~pearl/thumbnail-spec/ + * + * Copyright (C) 2001-2003 Sven Neumann + * Michael Natterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIMP_THUMBNAIL_H__ +#define __GIMP_THUMBNAIL_H__ + +G_BEGIN_DECLS + + +#define GIMP_TYPE_THUMBNAIL (gimp_thumbnail_get_type ()) +#define GIMP_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_THUMBNAIL, GimpThumbnail)) +#define GIMP_THUMBNAIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_THUMBNAIL, GimpThumbnailClass)) +#define GIMP_IS_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_THUMBNAIL)) +#define GIMP_IS_THUMBNAIL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_THUMBNAIL)) +#define GIMP_THUMBNAIL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_THUMBNAIL, GimpThumbnailClass)) + + +typedef struct _GimpThumbnailClass GimpThumbnailClass; + +struct _GimpThumbnail +{ + GObject parent_instance; + + gchar *uri; + GimpThumbState state; + + gchar *thumb_filename; + gint64 thumb_filesize; + gint64 thumb_mtime; + + gchar *image_filename; + gint64 image_filesize; + gint64 image_mtime; + + gint image_width; + gint image_height; + gchar *image_type; + gint image_num_layers; +}; + +struct _GimpThumbnailClass +{ + GObjectClass parent_class; +}; + + +GType gimp_thumbnail_get_type (void) G_GNUC_CONST; + +GimpThumbnail * gimp_thumbnail_new (void); + +void gimp_thumbnail_set_uri (GimpThumbnail *thumbnail, + const gchar *uri); +gboolean gimp_thumbnail_set_filename (GimpThumbnail *thumbnail, + const gchar *filename, + GError **error); + +GimpThumbState gimp_thumbnail_get_state (GimpThumbnail *thumnail); +GdkPixbuf * gimp_thumbnail_get_pixbuf (GimpThumbnail *thumbnail, + GimpThumbSize size, + GError **error); + +gboolean gimp_thumbnail_save_pixbuf (GimpThumbnail *thumbnail, + GdkPixbuf *pixbuf, + GError **error); + + +G_END_DECLS + +#endif /* __GIMP_THUMBNAIL_H__ */