gimp/app/core/gimpimagefile.c

1331 lines
37 KiB
C
Raw Normal View History

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpfileimage.c
* Thumbnail handling according to the Thumbnail Managing Standard.
* http://triq.net/~pearl/thumbnail-spec/
*
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
* Michael Natterer <mitch@gimp.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
an evil temp_hack which lets GimpContext managing the active display 2001-08-14 Michael Natterer <mitch@gimp.org> * app/gdisplay.h: an evil temp_hack which lets GimpContext managing the active display withoug including "gdisplay.h". Will go away as soon ad context properties are registered dynamically. * app/module_db.c: cleaned up the object code in preparation of moving it to core/. * app/path.c: connect to GimpImage's * app/core/gimpobject.[ch]: derive it from GObject, not from GtkObject any more (yeah :-) * app/core/*.c: #include <glib-object.h> instead of <gtk/gtk.h>, removed some remaining GtkObject-isms. (left in a few #include <gtk/gtk.h> where bigger changes are needed to get rid of the UI dependency). * app/core/core-types.h: #include <gdk-pixbuf/gdk-pixbuf.h> here temporarily. * app/core/gimp.c (gimp_create_display): unref the image after creating it's first display. * app/core/gimpbrush.[ch]: disabled the parts of the code which depend on GimpPaintTool. * app/core/gimpbrushgenerated.c * app/core/gimpbrushpipe.c: changed accordingly. * app/core/gimpcontext.[ch]: evil hack (see above) to manage the active display without including "gdisplay.h" * app/core/gimpimage-mask.[ch]: pass a context to gimage_mask_stroke() and get the current tool's PDB string from there. * app/core/gimpedit.c: changed accordingly. * app/core/gimpimage.c: use gimp_image_update() instead of gdisplays_update_full(). * app/gui/color-area.c * app/gui/colormap-dialog.c * app/gui/dialogs-constructors.c * app/gui/edit-commands.c * app/gui/image-commands.c * app/gui/toolbox.c: changed accordingly (don't use Gtk methods on GObjects). * app/gui/menus.c: fix some const warnings by explicit casting. * app/tools/*.[ch]: ported all tools to GObject, some minor cleanup while i was on it. * app/widgets/gimpdialogfactory.[ch]: ported to GObject. * app/widgets/gimplayerlistview.h: added FOO_GET_CLASS() macro. * tools/pdbgen/app.pl: added a "widgets_eek" hack like "tools_eek" which inserts #include "widgets/widgets-types.h" before ordinary includes. * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/edit.pdb * app/pdb/brush_select_cmds.c * app/pdb/edit_cmds.c: changed according to the stuff above.
2001-08-14 14:53:55 +00:00
#include <glib-object.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#define GETTEXT_PACKAGE 2001-03-28 Hans Breuer <hans@breuer.org> * config.h.win32 : #define GETTEXT_PACKAGE * makefile.msc : add theme rule * app/makefile.msc : gimp.exe depends on all the libs and general update * app/base/makefile.msc : updated * app/config/gimpconfig-serialize.c : #include <io.h> for win32 * app/config/gimpconfig-types.c : #include <string.h> * app/core/gimpcontext.c app/core/gimpcontainer.c app/core/gimptoolinfo.c : #include <string.h> * app/core/gimpdocuments.c (gimp_documents_save_func) : need to g_strescape() the filename to not make backslashes vanish during de-serialization * app/core/gimpimagefile.c : #define S_ISREG for G_OS_WIN32 * app/core/makefile.msc : add -DGIMP_COMPILATION required for cpercep.c build * app/display/gimpdisplayshell.c : #include <string.h> * app/display/makefile.msc : -FImsvc_recommended_pragmas.h, G_LOG_DOMAIN definition and object file update * app/file/makefile.msc : -FImsvc_recommended_pragmas.h, G_LOG_DOMAIN definition * app/file/file-open.c (file_open_with_proc_and_display) : use absolute filename for gimp_documents_add() * app/gui/channel-commands.c app/gui/colormap-editor-commands.c app/gui/edit-commands.c app/gui/vectors-commands.c : #include <string.h> * app/gui/makefile.msc : updated * app/gui/menus.c : use g_file_test() instead of access() to avoid inclusion <unistd.h> * app/paint/makefile.msc : updated * app/plug-in/plug-in-params.c : #include <string.h> * app/plug-in/makefile.msc : updated * app/plug-in/plug-in-def.h : #include <time.h> for time_t * app/plug-in/plug-in.c : remove definition of S_IFREG * app/plug-in/gap/gap_arr_dialog.c : include <config.h> before including libgimp/libgimp-intl.h * app/tools/makefile.msc : updated * app/vectors/makefile.msc : new file * app/widgets/makefile.msc : updated * libgimp/gimp.def : updated externals * libgimpwidgets/gimpwidgets.def : updated externals * modules/makefile.msc : updated and disabled colorsel_gtk. * plug-in/makefile.msc : don't define GETTEXT_PACKAGE * themes/Default/images/makefile.msc : moved makefile.msc from .. and adapted pathes to images
2002-03-28 00:10:56 +00:00
#ifdef G_OS_WIN32
add appconfig.lib. Statically link libgimptool/gimptool.lib. 2001-05-11 Hans Breuer <hans@breuer.org> * app/makefile.msc : add appconfig.lib. Statically link libgimptool/gimptool.lib. * app/main.c : use gimp_locale_directory() * app/config/gimpconfig-utils.c : <string.h> * app/config/makefile.msc : add gimpscanner * app/core/gimpimagefile.c : some G_OS_WIN32 mess to get mkdir() and chmod() * app/display/gimpdisplayshell.c app/plug-in/plug-in-progrss.c app/tool/gimpcolorpickertool.c app/tool/gimpcroptool.c app/tool/gimpmeasuretool.c app/tool/gimpperspectivetool.c app/tool/gimprotatetool.c app/tool/gimpscaletool.c app/tool/gimpsheartool.c app/tool/gimptransformtool.c app/widgets/gimpcolormapeditor.c app/widgets/gimpcolorpanel.c app/widgets/gimptoolbox-color-area.c add #ifdef __GNUC__ to avoid breaking on non standard pragma #warning * app/tools/makefile.msc : add gimptoolcontrol remove tools-enum * app/tools/tool_manager.c : need to include libgimptool/gimptoolcontrol.h after core includes otherwise we would compile without prototypes or break miserably * app/gui/plug-in-menus.c : replace LOCALEDIR with gimp_locale_directory () * app/gui/preferences-dialog.c (prefs_notebook_append_page) : only try to gdk_pixbuf_new_from_file() with a valid filename. It should simply return NULL otherwise, but fails if the filename is an empty string. * app/paint-funcs/makefile.msc : add -FImsvc_recommended_pragmas.h * app/widgets/gimpcolormapeditor.c : the 'row' allocated needs to be 'xn * cellsize * 2' (to avoid accessing unowned memory) not only width, which has become allocation.width by someone commenting out the correct size calculation * app/widgets/gimpdialogfactory.c : varargs to macros are GCCism or at least non standard. #define DEBUG to g_print or nothing - without arguments - does fix it somewhat dirty as the compiler needs to tolerate the '(blah, foo, bar);' statement than * app/widgets/makefile.msc : updated * app/xcf/makefile.msc : add -FImsvc_recommended_pragmas.h * etc/gimprc.win32 : use ';' to separate theme-path * libgimpbase/gimpenv.c : #include <stdio.h> for sprintf() * app/widgets/gimpdnd.c (gimp_dnd_set_file_data) : the passed in vals chunk is not always null-terminated (at least not on win32). Use the length parameter too to avoid reading junk filenames. * libgimp/gimp.def : export gimp_image_get_name() * libgimpbase/gimpbase.def : export gimp_locale_directory() * libgimpbase/gimpenv.[ch] : added gimp_locale_directory () * libgimpbase/makefile.msc : define DATADIR and SYSCONFDIR to empty string to let gimp find its files in the common place (win32: relative to the top level gimp dir) * plug-ins/common/pixelize.c : <string.h> * plug-ins/flame/cmap.c : #include <glib.h> for g_random_int() * plug-ins/makefile.msc : -FImsvc_recommended_pragams.h and a little hack to give imagemap the prototypes it desires without changing the lexed source * themes/Default/images/makefile.msc : now added (see below) * themes/Default/images/stock-button-reset.png : made it binary
2002-05-10 23:30:09 +00:00
# include <direct.h>
# define mkdir(n,a) _mkdir(n)
# include <io.h>
# define chmod(n,f) _chmod(n,f)
#define GETTEXT_PACKAGE 2001-03-28 Hans Breuer <hans@breuer.org> * config.h.win32 : #define GETTEXT_PACKAGE * makefile.msc : add theme rule * app/makefile.msc : gimp.exe depends on all the libs and general update * app/base/makefile.msc : updated * app/config/gimpconfig-serialize.c : #include <io.h> for win32 * app/config/gimpconfig-types.c : #include <string.h> * app/core/gimpcontext.c app/core/gimpcontainer.c app/core/gimptoolinfo.c : #include <string.h> * app/core/gimpdocuments.c (gimp_documents_save_func) : need to g_strescape() the filename to not make backslashes vanish during de-serialization * app/core/gimpimagefile.c : #define S_ISREG for G_OS_WIN32 * app/core/makefile.msc : add -DGIMP_COMPILATION required for cpercep.c build * app/display/gimpdisplayshell.c : #include <string.h> * app/display/makefile.msc : -FImsvc_recommended_pragmas.h, G_LOG_DOMAIN definition and object file update * app/file/makefile.msc : -FImsvc_recommended_pragmas.h, G_LOG_DOMAIN definition * app/file/file-open.c (file_open_with_proc_and_display) : use absolute filename for gimp_documents_add() * app/gui/channel-commands.c app/gui/colormap-editor-commands.c app/gui/edit-commands.c app/gui/vectors-commands.c : #include <string.h> * app/gui/makefile.msc : updated * app/gui/menus.c : use g_file_test() instead of access() to avoid inclusion <unistd.h> * app/paint/makefile.msc : updated * app/plug-in/plug-in-params.c : #include <string.h> * app/plug-in/makefile.msc : updated * app/plug-in/plug-in-def.h : #include <time.h> for time_t * app/plug-in/plug-in.c : remove definition of S_IFREG * app/plug-in/gap/gap_arr_dialog.c : include <config.h> before including libgimp/libgimp-intl.h * app/tools/makefile.msc : updated * app/vectors/makefile.msc : new file * app/widgets/makefile.msc : updated * libgimp/gimp.def : updated externals * libgimpwidgets/gimpwidgets.def : updated externals * modules/makefile.msc : updated and disabled colorsel_gtk. * plug-in/makefile.msc : don't define GETTEXT_PACKAGE * themes/Default/images/makefile.msc : moved makefile.msc from .. and adapted pathes to images
2002-03-28 00:10:56 +00:00
# ifndef S_ISREG
# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
# endif
#endif
#include "libgimpbase/gimpversion.h"
#include "core-types.h"
#include "base/temp-buf.h"
Finally landed the new GimpConfig based gimprc parser. It's not finished 2002-11-18 Sven Neumann <sven@gimp.org> Finally landed the new GimpConfig based gimprc parser. It's not finished yet but we need to start somewhere. This release removes the old gimprc.[ch] files. The gimprc format changes slightly, but the changes are minimal. The Preferences dialog is temporarily disabled since it still needs to be ported. If you are are afraid, stay away from CVS for a few days ;-) * app/Makefile.am * app/gimprc.[ch]: removed the old gimprc system. * app/base/Makefile.am * app/base/base-config.[ch]: removed these files in favor of config/gimpbaseconfig.[ch]. * app/core/Makefile.am * app/core/gimpcoreconfig.[ch]: removed these files in favor of config/gimpcoreconfig.[ch]. * app/config/Makefile.am * app/config/config-types.h: moved typedefs into this new file. * app/config/gimpbaseconfig.[ch] * app/config/gimpcoreconfig.[ch] * app/config/gimpdisplayconfig.[ch] * app/config/gimpguiconfig.[ch] * app/config/gimprc.[ch] * app/config/test-config.c: brought into shape for real use. * app/base/base-types.h: include config/config-types.h here. Added a global GimpBaseConfig *base_config variable to ease migration. * app/gui/Makefile.am: temporarily disabled the preferences dialog. * app/app_procs.c * app/undo.c * app/undo_history.c * app/base/base.[ch] * app/base/gimphistogram.c * app/base/pixel-processor.c * app/base/temp-buf.c * app/base/tile-cache.c * app/core/core-types.h * app/core/gimp-documents.c * app/core/gimp.c * app/core/gimpbrush.c * app/core/gimpbrushgenerated.c * app/core/gimpcontext.c * app/core/gimpdrawable-transform.c * app/core/gimpimage-new.c * app/core/gimpimage.c * app/core/gimpimagefile.c * app/core/gimpmodules.c * app/core/gimppattern.c * app/display/Makefile.am * app/display/gimpdisplay-handlers.c * app/display/gimpdisplay.[ch] * app/display/gimpdisplayshell-callbacks.c * app/display/gimpdisplayshell-handlers.c * app/display/gimpdisplayshell-layer-select.c * app/display/gimpdisplayshell-render.c * app/display/gimpdisplayshell-scale.c * app/display/gimpdisplayshell-scroll.c * app/display/gimpdisplayshell-selection.c * app/display/gimpdisplayshell.[ch] * app/display/gimpnavigationview.c * app/file/file-save.c * app/gui/device-status-dialog.c * app/gui/dialogs-constructors.c * app/gui/file-commands.c * app/gui/file-new-dialog.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/gui.c * app/gui/menus.c * app/gui/paths-dialog.c * app/gui/resize-dialog.c * app/gui/session.c * app/gui/test-commands.c * app/gui/tips-dialog.c * app/gui/tips-dialog.h * app/gui/user-install-dialog.c * app/gui/view-commands.c * app/paint/gimppaintcore.c * app/plug-in/plug-in.c * app/plug-in/plug-ins.c * app/tools/gimpbezierselecttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpcolorpickertool.c * app/tools/gimpcroptool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimpinktool.c * app/tools/gimpmagnifytool.c * app/tools/gimpmeasuretool.c * app/tools/gimppainttool.c * app/tools/gimppathtool.c * app/tools/gimptexttool.[ch] * app/tools/selection_options.c * app/tools/tools.c * app/tools/transform_options.c * app/widgets/gimphelp.c * app/widgets/gimpitemfactory.c * app/widgets/gimpselectioneditor.c * app/xcf/xcf-load.c * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gimprc.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/layer.pdb * tools/pdbgen/pdb/transform_tools.pdb: use the new config system instead of the old gimprc stuff. * etc/gimprc.in * etc/gimprc_user.in: adapted to the new gimprc format. Will update the man-page later... * app/pdb/fileops_cmds.c * app/pdb/gimprc_cmds.c * app/pdb/image_cmds.c * app/pdb/layer_cmds.c * app/pdb/transform_tools_cmds.c * libgimp/gimpgimprc_pdb.c: regenerated.
2002-11-18 20:50:31 +00:00
#include "config/gimpcoreconfig.h"
#include "gimp.h"
#include "gimpcontainer.h"
#include "gimpimage.h"
#include "gimpimagefile.h"
#include "gimpmarshal.h"
#include "file/file-open.h"
#include "gimp-intl.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_SIZE "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
{
INFO_CHANGED,
LAST_SIGNAL
};
typedef struct
{
const gchar *dirname;
gint size;
} ThumbnailSize;
static void gimp_imagefile_class_init (GimpImagefileClass *klass);
static void gimp_imagefile_init (GimpImagefile *imagefile);
static void gimp_imagefile_finalize (GObject *object);
static void gimp_imagefile_name_changed (GimpObject *object);
static void gimp_imagefile_set_info (GimpImagefile *imagefile,
gboolean emit_always,
gint width,
gint height,
GimpImageType type,
gint n_layers);
static TempBuf * gimp_imagefile_get_new_preview (GimpViewable *viewable,
gint width,
gint height);
static TempBuf * gimp_imagefile_read_png_thumb (GimpImagefile *imagefile,
gint thumb_size);
static gboolean gimp_imagefile_save_png_thumb (GimpImagefile *imagefile,
GimpImage *gimage,
const gchar *thumb_name,
gint thumb_size,
time_t image_mtime,
off_t image_size);
static void gimp_imagefile_save_fail_thumb (GimpImagefile *imagefile,
time_t image_mtime,
off_t image_size);
static const gchar * gimp_imagefile_png_thumb_name (const gchar *uri);
static gchar * gimp_imagefile_png_thumb_path (const gchar *uri,
gint *thumb_size);
static gchar * gimp_imagefile_find_png_thumb (const gchar *uri,
gint *thumb_size);
static TempBuf * gimp_imagefile_read_xv_thumb (GimpImagefile *imagefile);
static guchar * readXVThumb (const gchar *filename,
gint *width,
gint *height,
gchar **imginfo);
static gboolean gimp_imagefile_test (const gchar *filename,
time_t *mtime,
off_t *size);
#define THUMB_SIZE_FAIL -1
static const ThumbnailSize thumb_sizes[] =
{
{ "fail", THUMB_SIZE_FAIL },
{ "normal", 128 },
{ "large", 256 }
};
static gchar *thumb_dir = NULL;
static gchar *thumb_subdirs[G_N_ELEMENTS (thumb_sizes)] = { 0 };
static gchar *thumb_fail_subdir = NULL;
static guint gimp_imagefile_signals[LAST_SIGNAL] = { 0 };
static GimpViewableClass *parent_class = NULL;
GType
gimp_imagefile_get_type (void)
{
static GType imagefile_type = 0;
if (!imagefile_type)
{
static const GTypeInfo imagefile_info =
{
sizeof (GimpImagefileClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gimp_imagefile_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GimpImagefile),
0, /* n_preallocs */
(GInstanceInitFunc) gimp_imagefile_init,
};
imagefile_type = g_type_register_static (GIMP_TYPE_VIEWABLE,
"GimpImagefile",
&imagefile_info, 0);
}
return imagefile_type;
}
static void
gimp_imagefile_class_init (GimpImagefileClass *klass)
{
GObjectClass *object_class;
GimpObjectClass *gimp_object_class;
GimpViewableClass *viewable_class;
gint i;
parent_class = g_type_class_peek_parent (klass);
object_class = G_OBJECT_CLASS (klass);
gimp_object_class = GIMP_OBJECT_CLASS (klass);
viewable_class = GIMP_VIEWABLE_CLASS (klass);
gimp_imagefile_signals[INFO_CHANGED] =
g_signal_new ("info_changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpImagefileClass, info_changed),
NULL, NULL,
gimp_marshal_VOID__VOID,
G_TYPE_NONE, 0);
object_class->finalize = gimp_imagefile_finalize;
gimp_object_class->name_changed = gimp_imagefile_name_changed;
viewable_class->name_changed_signal = "info_changed";
viewable_class->get_new_preview = gimp_imagefile_get_new_preview;
g_type_class_ref (GIMP_TYPE_IMAGE_TYPE);
thumb_dir = g_build_filename (g_get_home_dir(), ".thumbnails", NULL);
for (i = 0; i < G_N_ELEMENTS (thumb_sizes); i++)
thumb_subdirs[i] = g_build_filename (g_get_home_dir(), ".thumbnails",
thumb_sizes[i].dirname, NULL);
thumb_fail_subdir = thumb_subdirs[0];
thumb_subdirs[0] = g_strdup_printf ("%s%cgimp-%d.%d",
thumb_fail_subdir, G_DIR_SEPARATOR,
GIMP_MAJOR_VERSION, GIMP_MINOR_VERSION);
}
static void
gimp_imagefile_init (GimpImagefile *imagefile)
{
imagefile->gimp = NULL;
imagefile->state = GIMP_IMAGEFILE_STATE_UNKNOWN;
imagefile->image_mtime = 0;
imagefile->image_size = -1;
imagefile->width = -1;
imagefile->height = -1;
imagefile->type = -1;
imagefile->n_layers = -1;
imagefile->description = NULL;
}
static void
gimp_imagefile_finalize (GObject *object)
{
GimpImagefile *imagefile = (GimpImagefile *) object;
if (imagefile->description && ! imagefile->static_desc)
g_free (imagefile->description);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
GimpImagefile *
gimp_imagefile_new (Gimp *gimp,
const gchar *uri)
{
GimpImagefile *imagefile;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
imagefile = g_object_new (GIMP_TYPE_IMAGEFILE, NULL);
if (uri)
gimp_object_set_name (GIMP_OBJECT (imagefile), uri);
imagefile->gimp = gimp;
return imagefile;
}
void
gimp_imagefile_update (GimpImagefile *imagefile,
gint thumb_size)
{
const gchar *uri;
g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
g_return_if_fail (thumb_size <= 256);
if (thumb_size < 1)
return;
uri = gimp_object_get_name (GIMP_OBJECT (imagefile));
if (uri)
{
gchar *filename = NULL;
gchar *thumbname = NULL;
off_t image_size;
filename = g_filename_from_uri (uri, NULL, NULL);
imagefile->image_mtime = 0;
imagefile->image_size = -1;
if (! filename)
{
/* no thumbnails of remote images :-( */
imagefile->state = GIMP_IMAGEFILE_STATE_REMOTE;
goto cleanup;
}
if (! gimp_imagefile_test (filename,
&imagefile->image_mtime,
&image_size))
{
imagefile->state = GIMP_IMAGEFILE_STATE_NOT_FOUND;
goto cleanup;
}
/* found the image, now look for the thumbnail */
imagefile->image_size = image_size;
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_NOT_FOUND;
thumbname = gimp_imagefile_find_png_thumb (uri, &thumb_size);
if (thumbname)
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_EXISTS;
cleanup:
g_free (filename);
g_free (thumbname);
gimp_imagefile_set_info (imagefile, TRUE, -1, -1, -1, -1);
}
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile));
if (uri)
{
GimpImagefile *documents_imagefile;
documents_imagefile = (GimpImagefile *)
gimp_container_get_child_by_name (imagefile->gimp->documents, uri);
if (GIMP_IS_IMAGEFILE (documents_imagefile) &&
(documents_imagefile != imagefile))
{
gimp_imagefile_update (GIMP_IMAGEFILE (documents_imagefile),
thumb_size);
}
}
}
void
gimp_imagefile_create_thumbnail (GimpImagefile *imagefile,
gint thumb_size)
{
const gchar *uri;
g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
g_return_if_fail (thumb_size <= 256);
if (! imagefile->gimp->config->layer_previews)
return;
if (thumb_size < 1)
return;
uri = gimp_object_get_name (GIMP_OBJECT (imagefile));
if (strstr (uri, "/.thumbnails/"))
return;
if (uri)
{
gchar *filename;
gboolean file_exists;
time_t image_mtime;
off_t image_size;
GimpImage *gimage;
GimpPDBStatusType dummy;
filename = g_filename_from_uri (uri, NULL, NULL);
/* no thumbnails of remote images :-( */
if (! filename)
return;
file_exists = gimp_imagefile_test (filename, &image_mtime, &image_size);
g_free (filename);
if (! file_exists)
return;
gimage = file_open_image (imagefile->gimp,
uri,
uri,
NULL,
GIMP_RUN_NONINTERACTIVE,
&dummy,
NULL);
if (gimage)
{
gchar *thumb_name;
thumb_size = MIN (thumb_size, MAX (gimage->width, gimage->height));
thumb_name = gimp_imagefile_png_thumb_path (uri, &thumb_size);
if (thumb_name)
{
gimp_imagefile_save_png_thumb (imagefile,
gimage,
thumb_name,
thumb_size,
image_mtime,
image_size);
g_free (thumb_name);
}
g_object_unref (gimage);
}
else
{
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_FAILED;
gimp_imagefile_set_info (imagefile, TRUE, -1, -1, -1, -1);
gimp_imagefile_save_fail_thumb (imagefile, image_mtime, image_size);
}
}
}
static void
gimp_imagefile_save_fail_thumb (GimpImagefile *imagefile,
time_t image_mtime,
off_t image_size)
{
GdkPixbuf *pixbuf;
const gchar *uri;
gint thumb_size = THUMB_SIZE_FAIL;
gchar *thumb_name;
gchar *desc;
gchar *t_str;
gchar *s_str;
GError *error = NULL;
uri = gimp_object_get_name (GIMP_OBJECT (imagefile));
thumb_name = gimp_imagefile_png_thumb_path (uri, &thumb_size);
if (!thumb_name)
return;
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
desc = g_strdup_printf ("Thumbnail failure for %s", uri);
t_str = g_strdup_printf ("%ld", image_mtime);
s_str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) image_size);
if (! gdk_pixbuf_save (pixbuf, thumb_name, "png", &error,
TAG_DESCRIPTION, desc,
TAG_SOFTWARE, ("The GIMP "
GIMP_VERSION),
TAG_THUMB_URI, uri,
TAG_THUMB_MTIME, t_str,
TAG_THUMB_SIZE, s_str,
NULL))
{
g_message (_("Failed to write thumbnail for '%s' as '%s': %s"),
uri, thumb_name, error->message);
g_error_free (error);
}
else if (chmod (thumb_name, 0600))
{
g_message (_("Failed to set permissions of thumbnail '%s': %s"),
thumb_name, g_strerror (errno));
}
g_object_unref (pixbuf);
g_free (desc);
g_free (t_str);
g_free (s_str);
g_free (thumb_name);
}
gboolean
gimp_imagefile_save_thumbnail (GimpImagefile *imagefile,
GimpImage *gimage)
{
const gchar *uri;
const gchar *image_uri;
gchar *filename;
gint thumb_size;
gchar *thumb_name;
time_t image_mtime;
off_t image_size;
gboolean success = FALSE;
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE);
thumb_size = imagefile->gimp->config->thumbnail_size;
g_return_val_if_fail (thumb_size <= 256, FALSE);
if (! imagefile->gimp->config->layer_previews)
return TRUE;
if (thumb_size < 1)
return TRUE;
uri = gimp_object_get_name (GIMP_OBJECT (imagefile));
image_uri = gimp_object_get_name (GIMP_OBJECT (gimage));
g_return_val_if_fail (uri && image_uri && ! strcmp (uri, image_uri), FALSE);
if (strstr (uri, "/.thumbnails/"))
return FALSE;
filename = g_filename_from_uri (uri, NULL, NULL);
/* no thumbnails of remote images :-( */
if (! filename)
return FALSE;
thumb_size = MIN (thumb_size, MAX (gimage->width, gimage->height));
thumb_name = gimp_imagefile_png_thumb_path (uri, &thumb_size);
/* the thumbnail directory exists or could be created */
if (thumb_name)
{
if (gimp_imagefile_test (filename, &image_mtime, &image_size))
{
success = gimp_imagefile_save_png_thumb (imagefile,
gimage,
thumb_name,
thumb_size,
image_mtime,
image_size);
}
g_free (thumb_name);
}
g_free (filename);
return success;
}
static void
gimp_imagefile_name_changed (GimpObject *object)
bumped version number to 1.3.1. Require Glib/GTK+-1.3.11 and Pango-0.22. 2001-11-23 Sven Neumann <sven@gimp.org> * configure.in: bumped version number to 1.3.1. Require Glib/GTK+-1.3.11 and Pango-0.22. Removed GDK_DISABLE_COMPAT_H and GTK_DISABLE_COMPAT_H from our default CFLAGS since they don't exist any longer. * RELEASE-TO-CVS.patch: removed since the glib/gtk+ API is supposed to be frozen now. * HACKING: removed reference to RELEASE-TO-CVS.patch * app/gui/menus.c * app/tools/gimptexttool.c: applied RELEASE-TO-CVS.patch to conform to the new GTK+/Pango API. * app/core/Makefile.am: generate marshallers with gimp_marshal prefix. * app/core/gimpmarshal.list: added all marshallers we use. * app/core/gimpmarshal.[ch]: regenerated. * app/[lots of .c files]: use gimp_marshal_* for all marshallers. * data/images/ * app/app_procs.c * app/gui/splash.c: * libgimpbase/Makefile.am * libgimpbase/gimpbase.h * libgimpbase/gimputils.[ch]: removed since they are no longer needed. * app/gimprc.c * plug-ins/common/ps.c * plug-ins/gdyntext/gdyntext.c * plug-ins/gdyntext/gdyntextcompat.c * plug-ins/gfig/gfig.c * plug-ins/gflare/gflare.c * plug-ins/script-fu/script-fu-scripts.c: use glib functions instead of gimp_strescape() and gimpstrcompress(). * cleaned up all header files: use G_BEGIN_DECLS/G_END_DECLS, declared all _get_type function as G_GNUC_CONST. * tools/pdbgen/enumcode.pl * tools/pdbgen/lib.pl: make them generate header files using G_BEGIN_DECLS/G_END_DECLS. * pixmaps/Makefile.am * pixmaps/wilber3.xpm: removed ... * data/images/tips_wilber.png: ... and added here as PNG * app/gui/tips-dialog.c: load the Wilber on demand using GdkPixbuf. * data/images/gimp_splash.ppm: removed ... * data/images/gimp_splash.png: ... and added as PNG * app/app_procs.c * app/gui/splash.[ch]: load the splash image using GdkPixbuf. * app/gui/about-dialog.c: sink the GtkPreview.
2001-11-22 23:46:13 +00:00
{
GimpImagefile *imagefile;
imagefile = GIMP_IMAGEFILE (object);
if (GIMP_OBJECT_CLASS (parent_class)->name_changed)
GIMP_OBJECT_CLASS (parent_class)->name_changed (object);
imagefile->state = GIMP_IMAGEFILE_STATE_UNKNOWN;
imagefile->image_mtime = 0;
imagefile->image_size = -1;
gimp_imagefile_set_info (imagefile, TRUE, -1, -1, -1, -1);
bumped version number to 1.3.1. Require Glib/GTK+-1.3.11 and Pango-0.22. 2001-11-23 Sven Neumann <sven@gimp.org> * configure.in: bumped version number to 1.3.1. Require Glib/GTK+-1.3.11 and Pango-0.22. Removed GDK_DISABLE_COMPAT_H and GTK_DISABLE_COMPAT_H from our default CFLAGS since they don't exist any longer. * RELEASE-TO-CVS.patch: removed since the glib/gtk+ API is supposed to be frozen now. * HACKING: removed reference to RELEASE-TO-CVS.patch * app/gui/menus.c * app/tools/gimptexttool.c: applied RELEASE-TO-CVS.patch to conform to the new GTK+/Pango API. * app/core/Makefile.am: generate marshallers with gimp_marshal prefix. * app/core/gimpmarshal.list: added all marshallers we use. * app/core/gimpmarshal.[ch]: regenerated. * app/[lots of .c files]: use gimp_marshal_* for all marshallers. * data/images/ * app/app_procs.c * app/gui/splash.c: * libgimpbase/Makefile.am * libgimpbase/gimpbase.h * libgimpbase/gimputils.[ch]: removed since they are no longer needed. * app/gimprc.c * plug-ins/common/ps.c * plug-ins/gdyntext/gdyntext.c * plug-ins/gdyntext/gdyntextcompat.c * plug-ins/gfig/gfig.c * plug-ins/gflare/gflare.c * plug-ins/script-fu/script-fu-scripts.c: use glib functions instead of gimp_strescape() and gimpstrcompress(). * cleaned up all header files: use G_BEGIN_DECLS/G_END_DECLS, declared all _get_type function as G_GNUC_CONST. * tools/pdbgen/enumcode.pl * tools/pdbgen/lib.pl: make them generate header files using G_BEGIN_DECLS/G_END_DECLS. * pixmaps/Makefile.am * pixmaps/wilber3.xpm: removed ... * data/images/tips_wilber.png: ... and added here as PNG * app/gui/tips-dialog.c: load the Wilber on demand using GdkPixbuf. * data/images/gimp_splash.ppm: removed ... * data/images/gimp_splash.png: ... and added as PNG * app/app_procs.c * app/gui/splash.[ch]: load the splash image using GdkPixbuf. * app/gui/about-dialog.c: sink the GtkPreview.
2001-11-22 23:46:13 +00:00
}
static void
gimp_imagefile_set_info (GimpImagefile *imagefile,
gboolean emit_always,
gint width,
gint height,
GimpImageType type,
gint n_layers)
{
gboolean changed;
changed = (imagefile->width != width ||
imagefile->height != height ||
imagefile->type != type ||
imagefile->n_layers != n_layers);
imagefile->width = width;
imagefile->height = height;
imagefile->type = type;
imagefile->n_layers = n_layers;
if (imagefile->description)
{
if (!imagefile->static_desc)
g_free (imagefile->description);
imagefile->description = NULL;
}
if (changed || emit_always)
g_signal_emit (imagefile, gimp_imagefile_signals[INFO_CHANGED], 0);
}
static void
gimp_imagefile_set_info_from_pixbuf (GimpImagefile *imagefile,
gboolean emit_always,
GdkPixbuf *pixbuf)
{
const gchar *option;
gint img_width = -1;
gint img_height = -1;
GimpImageType img_type = -1;
gint img_layers = -1;
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_WIDTH);
if (!option || sscanf (option, "%d", &img_width) != 1)
img_width = -1;
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_HEIGHT);
if (!option || sscanf (option, "%d", &img_height) != 1)
img_height = -1;
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_TYPE);
if (option)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
enum_class = g_type_class_peek (GIMP_TYPE_IMAGE_TYPE);
enum_value = g_enum_get_value_by_nick (enum_class, option);
if (enum_value)
img_type = enum_value->value;
}
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_LAYERS);
if (!option || sscanf (option, "%d", &img_layers) != 1)
img_layers = -1;
gimp_imagefile_set_info (imagefile, emit_always,
img_width, img_height,
img_type, img_layers);
}
static TempBuf *
gimp_imagefile_get_new_preview (GimpViewable *viewable,
gint width,
gint height)
{
GimpImagefile *imagefile;
TempBuf *temp_buf;
imagefile = GIMP_IMAGEFILE (viewable);
if (! GIMP_OBJECT (imagefile)->name)
return NULL;
temp_buf = gimp_imagefile_read_png_thumb (imagefile, MAX (width, height));
added "gchar *stock_id" to the GimpViewable struct. It is used by the GUI 2003-02-25 Michael Natterer <mitch@gimp.org> * app/core/gimpviewable.[ch]: added "gchar *stock_id" to the GimpViewable struct. It is used by the GUI if the get_preview() functions return NULL. Default to GTK_STOCK_DIALOG_QUESTION. * app/core/gimptoolinfo.[ch]: set the tool's stock_id. Removed the cached GdkPixbuf. Don't implement any preview function so the GUI uses the stock_id. * app/tools/tool_manager.c: removed GdkPixbuf creation, removed the #warning about the buggy way we created the pixbuf. * app/gui/dialogs-constructors.c * app/gui/image-menu.c * app/tools/gimpcroptool.c * app/tools/gimphistogramtool.c * app/tools/gimpimagemaptool.c * app/tools/gimpmeasuretool.c * app/tools/gimptransformtool.c * app/widgets/gimptoolbox.c: use viewable->stock_id instead of tool_info->stock_id. * app/core/gimpbrush.c * app/core/gimpgradient.c * app/core/gimpimagefile.c * app/core/gimpundo.c: simplified get_preview() implementations: - never scale previews up, only down. - don't render white or checks backgrounds but simply return TempBufs with alpha and let the preview system do its job. - don't add padding but simply return previews smaller than requested. * app/display/gimpdisplayshell-render.[ch]: added "render_blend_white", a 2d lookup table for blending on white, just as the check lookup tables. Added "render_white_buf". * app/widgets/gimppreview.[ch]: changed a lot: - don't render the preview's border into the buffer. - added "GdkGC *border_gc" and draw the preview's border in expose() using gdk_draw_rectangle(). - added "GdkPixbuf *no_preview_pixbuf" and create it in gimp_preview_real_render() if gimp_viewable_get_preview() returned NULL. - factored the actual preview rendering out to gimp_preview_render_to_buffer(). Added configurable background rendering for the preview itself and it's padding area (the area the preview is larger than the buffer returned by gimp_viewable_get_preview()). - changed gimp_preview_render_and_flush() to gimp_preview_render_preview() and added "inside_bg" and "outside_bg" parameters. - use the new render buffers for blending on white. * app/widgets/gimpbrushpreview.c * app/widgets/gimpbufferpreview.c * app/widgets/gimpdrawablepreview.c * app/widgets/gimpgradientpreview.c * app/widgets/gimpimagepreview.c * app/widgets/gimppalettepreview.c * app/widgets/gimppatternpreview.c: don't create large white TempBufs to center the previews in but simply set the TempBuf's offsets to get them centered. Simplified & cleaned up many preview render functions. Pass the correct GimpPreviewBG modes to gimp_preview_render_preview(). * app/widgets/gimpcellrendererviewable.[ch]: new GtkCellRenderer class derived from GtkCellRendererPixbuf which knows how to use gimp_viewable_get_preview_size() and renders the viewable's stock item if no preview can be created. * app/widgets/gimpcontainertreeview.c: added a GtkTreeCellDataFunc which creates the preview pixbuf if needed so we don't create it unconditionally upon item insertion. Fixed preview size assertion to use GIMP_PREVIEW_MAX_SIZE, not "64". Block "selection_changed" while reordering the selected item. * app/widgets/gimpcontainerview.c: cosmetic. * app/widgets/gimpimagefilepreview.[ch] * app/widgets/gimptoolinfopreview.[ch] * app/widgets/gimpundopreview.[ch]: removed because the default implementation is good enough. * app/widgets/Makefile.am * app/widgets/widgets-types.h * app/widgets/gimppreview-utils.c: changed accordingly. * app/gui/dialogs-constructors.[ch] * app/gui/dialogs-menu.c * app/gui/dialogs.c * app/gui/image-menu.c * app/gui/toolbox-menu.c: register grid and tree view variants of the document history. Unrelated: * app/gui/gui.c (gui_exit_finish_callback): disconnect from signals earlier. * app/gui/user-install-dialog.c: create the "tool-options" subdir of the user's ~/.gimp-1.3 directory.
2003-02-26 16:15:50 +00:00
if (! temp_buf)
temp_buf = gimp_imagefile_read_xv_thumb (imagefile);
added "gchar *stock_id" to the GimpViewable struct. It is used by the GUI 2003-02-25 Michael Natterer <mitch@gimp.org> * app/core/gimpviewable.[ch]: added "gchar *stock_id" to the GimpViewable struct. It is used by the GUI if the get_preview() functions return NULL. Default to GTK_STOCK_DIALOG_QUESTION. * app/core/gimptoolinfo.[ch]: set the tool's stock_id. Removed the cached GdkPixbuf. Don't implement any preview function so the GUI uses the stock_id. * app/tools/tool_manager.c: removed GdkPixbuf creation, removed the #warning about the buggy way we created the pixbuf. * app/gui/dialogs-constructors.c * app/gui/image-menu.c * app/tools/gimpcroptool.c * app/tools/gimphistogramtool.c * app/tools/gimpimagemaptool.c * app/tools/gimpmeasuretool.c * app/tools/gimptransformtool.c * app/widgets/gimptoolbox.c: use viewable->stock_id instead of tool_info->stock_id. * app/core/gimpbrush.c * app/core/gimpgradient.c * app/core/gimpimagefile.c * app/core/gimpundo.c: simplified get_preview() implementations: - never scale previews up, only down. - don't render white or checks backgrounds but simply return TempBufs with alpha and let the preview system do its job. - don't add padding but simply return previews smaller than requested. * app/display/gimpdisplayshell-render.[ch]: added "render_blend_white", a 2d lookup table for blending on white, just as the check lookup tables. Added "render_white_buf". * app/widgets/gimppreview.[ch]: changed a lot: - don't render the preview's border into the buffer. - added "GdkGC *border_gc" and draw the preview's border in expose() using gdk_draw_rectangle(). - added "GdkPixbuf *no_preview_pixbuf" and create it in gimp_preview_real_render() if gimp_viewable_get_preview() returned NULL. - factored the actual preview rendering out to gimp_preview_render_to_buffer(). Added configurable background rendering for the preview itself and it's padding area (the area the preview is larger than the buffer returned by gimp_viewable_get_preview()). - changed gimp_preview_render_and_flush() to gimp_preview_render_preview() and added "inside_bg" and "outside_bg" parameters. - use the new render buffers for blending on white. * app/widgets/gimpbrushpreview.c * app/widgets/gimpbufferpreview.c * app/widgets/gimpdrawablepreview.c * app/widgets/gimpgradientpreview.c * app/widgets/gimpimagepreview.c * app/widgets/gimppalettepreview.c * app/widgets/gimppatternpreview.c: don't create large white TempBufs to center the previews in but simply set the TempBuf's offsets to get them centered. Simplified & cleaned up many preview render functions. Pass the correct GimpPreviewBG modes to gimp_preview_render_preview(). * app/widgets/gimpcellrendererviewable.[ch]: new GtkCellRenderer class derived from GtkCellRendererPixbuf which knows how to use gimp_viewable_get_preview_size() and renders the viewable's stock item if no preview can be created. * app/widgets/gimpcontainertreeview.c: added a GtkTreeCellDataFunc which creates the preview pixbuf if needed so we don't create it unconditionally upon item insertion. Fixed preview size assertion to use GIMP_PREVIEW_MAX_SIZE, not "64". Block "selection_changed" while reordering the selected item. * app/widgets/gimpcontainerview.c: cosmetic. * app/widgets/gimpimagefilepreview.[ch] * app/widgets/gimptoolinfopreview.[ch] * app/widgets/gimpundopreview.[ch]: removed because the default implementation is good enough. * app/widgets/Makefile.am * app/widgets/widgets-types.h * app/widgets/gimppreview-utils.c: changed accordingly. * app/gui/dialogs-constructors.[ch] * app/gui/dialogs-menu.c * app/gui/dialogs.c * app/gui/image-menu.c * app/gui/toolbox-menu.c: register grid and tree view variants of the document history. Unrelated: * app/gui/gui.c (gui_exit_finish_callback): disconnect from signals earlier. * app/gui/user-install-dialog.c: create the "tool-options" subdir of the user's ~/.gimp-1.3 directory.
2003-02-26 16:15:50 +00:00
if (temp_buf)
{
gint preview_width;
gint preview_height;
gimp_viewable_calc_preview_size (viewable,
temp_buf->width,
temp_buf->height,
width,
height,
TRUE, 1.0, 1.0,
&preview_width,
&preview_height,
NULL);
if (preview_width < temp_buf->width &&
preview_height < temp_buf->height)
{
TempBuf *scaled_buf;
scaled_buf = temp_buf_scale (temp_buf,
preview_width, preview_height);
temp_buf_free (temp_buf);
return scaled_buf;
}
}
return temp_buf;
}
const gchar *
gimp_imagefile_get_description (GimpImagefile *imagefile)
{
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
if (imagefile->description)
return (const gchar *) imagefile->description;
switch (imagefile->state)
{
case GIMP_IMAGEFILE_STATE_UNKNOWN:
imagefile->description = NULL;
imagefile->static_desc = TRUE;
break;
case GIMP_IMAGEFILE_STATE_REMOTE:
imagefile->description = _("Remote image");
imagefile->static_desc = TRUE;
break;
case GIMP_IMAGEFILE_STATE_NOT_FOUND:
imagefile->description = _("Failed to open");
imagefile->static_desc = TRUE;
break;
default:
{
GString *str;
str = g_string_new (NULL);
if (imagefile->image_size > -1)
{
gchar *size;
size = gimp_image_new_get_memsize_string (imagefile->image_size);
g_string_append (str, size);
g_free (size);
g_string_append_c (str, '\n');
}
switch (imagefile->state)
{
case GIMP_IMAGEFILE_STATE_THUMBNAIL_NOT_FOUND:
g_string_append (str, _("No preview available"));
break;
case GIMP_IMAGEFILE_STATE_THUMBNAIL_EXISTS:
g_string_append (str, _("Loading preview ..."));
break;
case GIMP_IMAGEFILE_STATE_THUMBNAIL_OLD:
g_string_append (str, _("Preview is out of date"));
break;
case GIMP_IMAGEFILE_STATE_THUMBNAIL_FAILED:
g_string_append (str, _("Cannot create preview"));
break;
case GIMP_IMAGEFILE_STATE_THUMBNAIL_OK:
{
GEnumClass *enum_class;
GEnumValue *enum_value;
if (imagefile->width > -1 && imagefile->height > -1)
{
g_string_append_printf (str, _("%d x %d pixels"),
imagefile->width,
imagefile->height);
g_string_append_c (str, '\n');
}
enum_class = g_type_class_peek (GIMP_TYPE_IMAGE_TYPE);
enum_value = g_enum_get_value (enum_class, imagefile->type);
if (enum_value)
g_string_append (str, gettext (enum_value->value_name));
if (imagefile->n_layers > -1)
{
if (enum_value)
g_string_append_len (str, ", ", 2);
if (imagefile->n_layers == 1)
g_string_append (str, _("1 Layer"));
else
g_string_append_printf (str, _("%d Layers"),
imagefile->n_layers);
}
}
break;
default:
break;
}
imagefile->description = g_string_free (str, FALSE);
imagefile->static_desc = FALSE;
}
}
return (const gchar *) imagefile->description;
}
static gboolean
gimp_imagefile_test (const gchar *filename,
time_t *mtime,
off_t *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;
}
/* PNG thumbnail handling routines according to the
Thumbnail Managing Standard http://triq.net/~pearl/thumbnail-spec/ */
static TempBuf *
gimp_imagefile_read_png_thumb (GimpImagefile *imagefile,
gint thumb_size)
{
GimpImagefileState old_state;
TempBuf *temp_buf = NULL;
GdkPixbuf *pixbuf = NULL;
gchar *thumbname = NULL;
const gchar *option;
gint width;
gint height;
gint bytes;
time_t thumb_image_mtime;
gint64 thumb_image_size;
guchar *src;
guchar *dest;
gint y;
GError *error = NULL;
if (imagefile->state < GIMP_IMAGEFILE_STATE_EXISTS)
return NULL;
old_state = imagefile->state;
/* try to locate a thumbnail for this image */
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_NOT_FOUND;
thumbname = gimp_imagefile_find_png_thumb (GIMP_OBJECT (imagefile)->name,
&thumb_size);
if (!thumbname)
goto cleanup;
pixbuf = gdk_pixbuf_new_from_file (thumbname, &error);
if (!pixbuf)
{
g_message (_("Failed to open thumbnail file '%s': %s"),
thumbname, error->message);
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, GIMP_OBJECT (imagefile)->name))
goto cleanup;
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_OLD;
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MTIME);
if (!option || sscanf (option, "%ld", &thumb_image_mtime) != 1)
goto cleanup;
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_SIZE);
if (option && sscanf (option, "%" G_GINT64_FORMAT, &thumb_image_size) != 1)
goto cleanup;
/* TAG_THUMB_SIZE is optional but must match if present */
if (thumb_image_mtime == imagefile->image_mtime &&
(option == NULL || thumb_image_size == (gint64) imagefile->image_size))
{
if (thumb_size == THUMB_SIZE_FAIL)
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_FAILED;
else
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_OK;
}
if (thumb_size == THUMB_SIZE_FAIL)
{
gimp_imagefile_set_info (imagefile, TRUE, -1, -1, -1, -1);
goto cleanup;
}
/* now convert the pixbuf to a tempbuf */
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
bytes = gdk_pixbuf_get_n_channels (pixbuf);
temp_buf = temp_buf_new (width, height, bytes, 0, 0, NULL);
dest = temp_buf_data (temp_buf);
src = gdk_pixbuf_get_pixels (pixbuf);
for (y = 0; y < height; y++)
{
memcpy (dest, src, width * bytes);
dest += width * bytes;
src += gdk_pixbuf_get_rowstride (pixbuf);
}
/* extract into about the original file from the pixbuf */
gimp_imagefile_set_info_from_pixbuf (imagefile,
imagefile->state != old_state,
pixbuf);
cleanup:
g_free (thumbname);
if (pixbuf)
g_object_unref (pixbuf);
if (error)
g_error_free (error);
return temp_buf;
}
static gboolean
gimp_imagefile_save_png_thumb (GimpImagefile *imagefile,
GimpImage *gimage,
const gchar *thumb_name,
gint thumb_size,
time_t image_mtime,
off_t image_size)
{
const gchar *uri;
gchar *temp_name = NULL;
GdkPixbuf *pixbuf;
gint width, height;
gboolean success = FALSE;
uri = gimp_object_get_name (GIMP_OBJECT (imagefile));
if (gimage->width <= thumb_size && gimage->height <= thumb_size)
{
width = gimage->width;
height = gimage->height;
thumb_size = MIN (thumb_size, MAX (width, height));
}
else
{
if (gimage->width < gimage->height)
{
height = thumb_size;
width = MAX (1, (thumb_size * gimage->width) / gimage->height);
}
else
{
width = thumb_size;
height = MAX (1, (thumb_size * gimage->height) / gimage->width);
}
}
pixbuf = gimp_viewable_get_new_preview_pixbuf (GIMP_VIEWABLE (gimage),
width, height);
{
GEnumClass *enum_class;
GimpImageType type;
gchar *type_str;
gchar *desc;
gchar *t_str;
gchar *w_str;
gchar *h_str;
gchar *s_str;
gchar *l_str;
GError *error = NULL;
type = GIMP_IMAGE_TYPE_FROM_BASE_TYPE (gimp_image_base_type (gimage));
if (gimp_image_has_alpha (gimage))
type = GIMP_IMAGE_TYPE_WITH_ALPHA (type);
enum_class = g_type_class_peek (GIMP_TYPE_IMAGE_TYPE);
type_str = g_enum_get_value (enum_class, type)->value_nick;
desc = g_strdup_printf ("Thumbnail of %s", uri);
t_str = g_strdup_printf ("%ld", image_mtime);
w_str = g_strdup_printf ("%d", gimage->width);
h_str = g_strdup_printf ("%d", gimage->height);
s_str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) image_size);
l_str = g_strdup_printf ("%d", gimage->layers->num_children);
success = gdk_pixbuf_save (pixbuf, thumb_name, "png", &error,
TAG_DESCRIPTION, desc,
TAG_SOFTWARE, ("The GIMP "
GIMP_VERSION),
TAG_THUMB_URI, uri,
TAG_THUMB_MTIME, t_str,
TAG_THUMB_SIZE, s_str,
TAG_THUMB_IMAGE_WIDTH, w_str,
TAG_THUMB_IMAGE_HEIGHT, h_str,
TAG_THUMB_GIMP_TYPE, type_str,
TAG_THUMB_GIMP_LAYERS, l_str,
NULL);
if (! success)
{
g_message (_("Failed to write thumbnail for '%s' as '%s': %s"),
uri, thumb_name, error->message);
g_error_free (error);
}
else if (chmod (thumb_name, 0600))
{
g_message (_("Failed to set permissions of thumbnail '%s': %s"),
thumb_name, g_strerror (errno));
}
g_free (desc);
g_free (t_str);
g_free (w_str);
g_free (h_str);
g_free (s_str);
g_free (l_str);
}
g_object_unref (pixbuf);
g_free (temp_name);
gimp_imagefile_update (imagefile, thumb_size);
return success;
}
static const gchar *
gimp_imagefile_png_thumb_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;
}
static gchar *
gimp_imagefile_png_thumb_path (const gchar *uri,
gint *thumb_size)
{
const gchar *name;
gchar *thumb_name = NULL;
gint i;
if (strstr (uri, "/.thumbnails/"))
return NULL;
name = gimp_imagefile_png_thumb_name (uri);
if (*thumb_size == THUMB_SIZE_FAIL)
{
i = 0;
}
else
{
for (i = 1;
i < G_N_ELEMENTS (thumb_sizes) && thumb_sizes[i].size < *thumb_size;
i++)
/* nothing */;
if (i == G_N_ELEMENTS (thumb_sizes))
i--;
}
*thumb_size = thumb_sizes[i].size;
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;
}
static gchar *
gimp_imagefile_find_png_thumb (const gchar *uri,
gint *thumb_size)
{
const gchar *name;
gchar *thumb_name;
gint i, n;
name = gimp_imagefile_png_thumb_name (uri);
n = G_N_ELEMENTS (thumb_sizes);
for (i = 1; i < n && thumb_sizes[i].size < *thumb_size; i++)
/* nothing */;
n = i;
for (; i < G_N_ELEMENTS (thumb_sizes); i++)
{
thumb_name = g_build_filename (thumb_subdirs[i], name, NULL);
if (gimp_imagefile_test (thumb_name, NULL, NULL))
{
*thumb_size = thumb_sizes[i].size;
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_imagefile_test (thumb_name, NULL, NULL))
{
*thumb_size = thumb_sizes[i].size;
return thumb_name;
}
g_free (thumb_name);
}
return NULL;
}
/* xvpics thumbnail reading routines for backward compatibility */
static TempBuf *
gimp_imagefile_read_xv_thumb (GimpImagefile *imagefile)
{
gchar *filename;
gchar *basename;
gchar *dirname;
gchar *thumbname;
time_t file_mtime;
time_t thumb_mtime;
gint width;
gint height;
gint x, y;
TempBuf *temp_buf;
guchar *src;
guchar *dest;
guchar *raw_thumb = NULL;
gchar *image_info = NULL;
filename = g_filename_from_uri (GIMP_OBJECT (imagefile)->name, NULL, NULL);
/* can't load thumbnails of non "file:" URIs */
if (! filename)
return NULL;
/* check if the image file exists at all */
if (!gimp_imagefile_test (filename, &file_mtime, NULL))
{
g_free (filename);
return NULL;
}
dirname = g_path_get_dirname (filename);
basename = g_path_get_basename (filename);
g_free (filename);
thumbname = g_build_filename (dirname, ".xvpics", basename, NULL);
g_free (dirname);
added -DG_DISABLE_DEPRECATED and -DGDK_DISABLE_COMPAT_H. 2001-08-29 Michael Natterer <mitch@gimp.org> * configure.in: added -DG_DISABLE_DEPRECATED and -DGDK_DISABLE_COMPAT_H. * app/batch.c * app/file-utils.c * app/gdisplay.c * app/gdisplay_ops.c * app/gimprc.[ch] * app/module_db.c * app/nav_window.c * app/undo_history.c * app/core/gimpgradient.c * app/core/gimpimagefile.c * app/core/gimppalette.c * app/gui/color-notebook.c * app/gui/convert-dialog.c * app/gui/error-console-dialog.c * app/gui/file-commands.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/gradient-editor.c * app/gui/info-window.c * app/gui/menus.c * app/gui/palette-import-dialog.c * app/tools/gimpbycolorselecttool.c * app/widgets/gimpcontainerview-utils.c * app/widgets/gimpdatafactoryview.c * libgimp/gimpmenu.c * plug-ins/common/bz2.c * plug-ins/common/compose.c * plug-ins/common/csource.c * plug-ins/common/decompose.c * plug-ins/common/gz.c * plug-ins/common/uniteditor.c * plug-ins/common/wmf.c * plug-ins/common/xbm.c * plug-ins/rcm/rcm_dialog.c * plug-ins/script-fu/interp_slib.c * plug-ins/script-fu/script-fu-console.c * plug-ins/script-fu/script-fu-scripts.c * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gimprc.pdb * app/pdb/fileops_cmds.c * app/pdb/gimprc_cmds.c: removed deprecated stuff like g_basename(), g_dirname(), g_strup() and friends. Added some "const gchar *" declarations while I was on it. Added some G_N_ELEMENTS() macros instead of declaring a useless variable for the number of items. * app/widgets/gtkhwrapbox.[ch] * app/widgets/gtkvwrapbox.[ch] * app/widgets/gtkwrapbox.[ch]: replaced with the latest versions from GLE, ported by the master himself. * app/gui/toolbox.c: changed accordingly. * app/plug_in.c * libgimp/gimp.c * libgimpbase/gimpwire.[ch]: use evil hacks to get binary mode from the new GIOChannel implementation (upstream bugreport already posted).
2001-08-29 17:48:28 +00:00
g_free (basename);
if (gimp_imagefile_test (thumbname, &thumb_mtime, NULL) &&
thumb_mtime >= file_mtime)
{
raw_thumb = readXVThumb (thumbname, &width, &height, &image_info);
}
g_free (thumbname);
if (!raw_thumb)
return NULL;
imagefile->state = GIMP_IMAGEFILE_STATE_THUMBNAIL_OLD;
if (image_info)
{
gint img_width;
gint img_height;
if (sscanf (image_info, "%dx%d", &img_width, &img_height) != 2)
{
img_width = 0;
img_height = 0;
}
gimp_imagefile_set_info (imagefile, FALSE,
img_width, img_height, -1, -1);
g_free (image_info);
}
temp_buf = temp_buf_new (width, height, 3, 0, 0, NULL);
src = raw_thumb;
dest = temp_buf_data (temp_buf);
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
dest[x*3] = ((src[x]>>5)*255)/7;
dest[x*3+1] = (((src[x]>>2)&7)*255)/7;
dest[x*3+2] = ((src[x]&3)*255)/3;
}
src += width;
dest += width * 3;
}
g_free (raw_thumb);
return temp_buf;
}
/* The readXVThumb function source may be re-used under
the XFree86-style license. <adam@gimp.org> */
static guchar *
readXVThumb (const gchar *fnam,
gint *w,
gint *h,
gchar **imginfo)
{
FILE *fp;
const gchar *P7_332 = "P7 332";
gchar P7_buf[7];
gchar linebuf[200];
guchar *buf;
gint twofivefive;
void *ptr;
*imginfo = NULL;
fp = fopen (fnam, "rb");
if (!fp)
return NULL;
fread (P7_buf, 6, 1, fp);
if (strncmp (P7_buf, P7_332, 6)!=0)
{
g_warning ("Thumbnail does not have the 'P7 332' header.");
fclose (fp);
return NULL;
}
/*newline*/
fread (P7_buf, 1, 1, fp);
do
{
ptr = fgets (linebuf, 199, fp);
if ((strncmp (linebuf, "#IMGINFO:", 9) == 0) &&
(linebuf[9] != '\0') &&
(linebuf[9] != '\n'))
{
if (linebuf[strlen(linebuf)-1] == '\n')
linebuf[strlen(linebuf)-1] = '\0';
if (linebuf[9] != '\0')
{
if (*imginfo)
g_free (*imginfo);
*imginfo = g_strdup (&linebuf[9]);
}
}
}
while (ptr && linebuf[0]=='#'); /* keep throwing away comment lines */
if (!ptr)
{
/* g_warning("Thumbnail ended - not an image?"); */
fclose (fp);
return NULL;
}
sscanf (linebuf, "%d %d %d\n", w, h, &twofivefive);
if (twofivefive!=255)
{
g_warning ("Thumbnail depth is incorrect.");
fclose (fp);
return NULL;
}
if ((*w)<1 || (*h)<1 || (*w)>80 || (*h)>60)
{
g_warning ("Thumbnail size is bad. Corrupted?");
fclose (fp);
return NULL;
}
buf = g_malloc ((*w) * (*h));
fread (buf, (*w) * (*h), 1, fp);
fclose (fp);
return buf;
}