/* gap_lib.c * 1997.11.18 hof (Wolfgang Hofer) * * GAP ... Gimp Animation Plugins * * basic anim functions: * Delete, Duplicate, Exchange, Shift * Next, Prev, First, Last, Goto * * */ /* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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. */ /* revision history: * 1.2.2a; 2001/10/21 hof: bufix # 61677 (error in duplicate frames GUI) * and disable duplicate for Unsaved/untitled Images. * (creating frames from such images with a default name may cause problems * as unexpected overwriting frames or mixing animations with different sized frames) * 1.2.1a; 2001/07/07 hof: p_file_copy use binary modes in fopen (hope that fixes bug #52890 in video/duplicate) * 1.1.29a; 2000/11/23 hof: gap locking (changed to procedures and placed here) * 1.1.28a; 2000/11/05 hof: check for GIMP_PDB_SUCCESS (not for FALSE) * 1.1.20a; 2000/04/25 hof: new: p_get_video_paste_name p_vid_edit_clear * 1.1.17b; 2000/02/27 hof: bug/style fixes * 1.1.14a; 1999/12/18 hof: handle .xvpics on fileops (copy, rename and delete) * new: p_get_frame_nr, * 1.1.9a; 1999/09/14 hof: handle frame filenames with framenumbers * that are not the 4digit style. (like frame1.xcf) * 1.1.8a; 1999/08/31 hof: for AnimFrame Filtypes != XCF: * p_decide_save_as does save INTERACTIVE at 1.st time * and uses GIMP_RUN_WITH_LAST_VALS for subsequent calls * (this enables to set Fileformat specific save-Parameters * at least at the 1.st call, using the save dialog * of the selected (by gimp_file_save) file_save procedure. * in NONINTERACTIVE mode we have no access to * the Fileformat specific save-Parameters * 1999/07/22 hof: accept anim framenames without underscore '_' * (suggested by Samuel Meder) * 0.99.00; 1999/03/15 hof: prepared for win/dos filename conventions * 0.98.00; 1998/11/30 hof: started Port to GIMP 1.1: * exchange of Images (by next frame) is now handled in the * new module: gap_exchange_image.c * 0.96.02; 1998/07/30 hof: extended gap_dup (duplicate range instead of singele frame) * added gap_shift * 0.96.00 hof: - now using gap_arr_dialog.h * 0.95.00 hof: increased duplicate frames limit from 50 to 99 * 0.93.01 hof: fixup bug when frames are not in the current directory * 0.90.00; hof: 1.st (pre) release */ #include "config.h" /* SYSTEM (UNIX) includes */ #include #include #include #include #include /* for kill */ #ifdef HAVE_SYS_TIMES_H #include #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_DIRENT_H #include #endif /* GIMP includes */ #include "gtk/gtk.h" #include "libgimp/stdplugins-intl.h" #include "libgimp/gimp.h" #ifdef G_OS_WIN32 #include # ifndef S_ISDIR # define S_ISDIR(m) ((m) & _S_IFDIR) # endif # ifndef S_ISREG # define S_ISREG(m) ((m) & _S_IFREG) # endif #endif #ifdef G_OS_WIN32 #include /* For _mkdir() */ #define mkdir(path,mode) _mkdir(path) #endif #ifdef G_OS_WIN32 #include /* For _getpid() */ #endif /* GAP includes */ #include "gap_layer_copy.h" #include "gap_lib.h" #include "gap_pdb_calls.h" #include "gap_arr_dialog.h" #include "gap_exchange_image.h" extern int gap_debug; /* ==0 ... dont print debug infos */ /* ------------------------------------------ */ /* forward working procedures */ /* ------------------------------------------ */ static int p_save_old_frame(t_anim_info *ainfo_ptr); static int p_rename_frame(t_anim_info *ainfo_ptr, long from_nr, long to_nr); static int p_delete_frame(t_anim_info *ainfo_ptr, long nr); static int p_del(t_anim_info *ainfo_ptr, long cnt); static int p_decide_save_as(gint32 image_id, char *sav_name); /* ============================================================================ * p_alloc_fname_thumbnail * return the thumbnail name (in .xvpics subdir) * for the given filename * ============================================================================ */ char * p_alloc_fname_thumbnail(char *name) { int l_len; int l_len2; int l_idx; char *l_str; if(name == NULL) { return(g_strdup("\0")); } l_len = strlen(name); l_len2 = l_len + 10; l_str = g_malloc(l_len2); strcpy(l_str, name); if(l_len > 0) { for(l_idx = l_len -1; l_idx > 0; l_idx--) { if((name[l_idx] == G_DIR_SEPARATOR) || (name[l_idx] == DIR_ROOT)) { l_idx++; break; } } g_snprintf(&l_str[l_idx], l_len2 - l_idx, ".xvpics%s%s", G_DIR_SEPARATOR_S, &name[l_idx]); } if(gap_debug) printf("p_alloc_fname_thumbnail: thumbname=%s:\n", l_str ); return(l_str); } /* ============================================================================ * p_strdup_*_underscore * duplicate string and if last char is no underscore add one at end. * duplicate string and delete last char if it is the underscore * ============================================================================ */ char * p_strdup_add_underscore(char *name) { int l_len; char *l_str; if(name == NULL) { return(g_strdup("\0")); } l_len = strlen(name); l_str = g_malloc(l_len+1); strcpy(l_str, name); if(l_len > 0) { if (name[l_len-1] != '_') { l_str[l_len ] = '_'; l_str[l_len +1 ] = '\0'; } } return(l_str); } char * p_strdup_del_underscore(char *name) { int l_len; char *l_str; if(name == NULL) { return(g_strdup("\0")); } l_len = strlen(name); l_str = g_strdup(name); if(l_len > 0) { if (l_str[l_len-1] == '_') { l_str[l_len -1 ] = '\0'; } } return(l_str); } /* ============================================================================ * p_msg_win * print a message both to stderr * and to a gimp info window with OK button (only when run INTERACTIVE) * ============================================================================ */ void p_msg_win(GimpRunModeType run_mode, char *msg) { static t_but_arg l_argv[1]; int l_argc; l_argv[0].but_txt = GTK_STOCK_OK; l_argv[0].but_val = 0; l_argc = 1; if(msg) { if(*msg) { fwrite(msg, 1, strlen(msg), stderr); fputc('\n', stderr); if(run_mode == GIMP_RUN_INTERACTIVE) p_buttons_dialog (_("GAP Message"), msg, l_argc, l_argv, -1); } } } /* end p_msg_win */ /* ============================================================================ * p_file_exists * * return 0 ... file does not exist, or is not accessable by user, * or is empty, * or is not a regular file * 1 ... file exists * ============================================================================ */ int p_file_exists(char *fname) { struct stat l_stat_buf; long l_len; /* File Laenge ermitteln */ if (0 != stat(fname, &l_stat_buf)) { /* stat error (file does not exist) */ return(0); } if(!S_ISREG(l_stat_buf.st_mode)) { return(0); } l_len = (long)l_stat_buf.st_size; if(l_len < 1) { /* File is empty*/ return(0); } return(1); } /* end p_file_exists */ /* ============================================================================ * p_image_file_copy * (copy the imagefile and its thumbnail) * ============================================================================ */ int p_image_file_copy(char *fname, char *fname_copy) { char *l_from_fname_thumbnail; char *l_to_fname_thumbnail; int l_rc; l_from_fname_thumbnail = p_alloc_fname_thumbnail(fname); l_to_fname_thumbnail = p_alloc_fname_thumbnail(fname_copy); l_rc = p_file_copy(fname, fname_copy); if((l_from_fname_thumbnail) && (l_to_fname_thumbnail)) { p_file_copy(l_from_fname_thumbnail, l_to_fname_thumbnail); } if(l_from_fname_thumbnail) g_free(l_from_fname_thumbnail); if(l_to_fname_thumbnail) g_free(l_to_fname_thumbnail); return(l_rc); } /* ============================================================================ * p_file_copy * ============================================================================ */ int p_file_copy(char *fname, char *fname_copy) { FILE *l_fp; char *l_buffer; struct stat l_stat_buf; long l_len; if(gap_debug) printf("p_file_copy src:%s dst:%s\n", fname, fname_copy); /* File Laenge ermitteln */ if (0 != stat(fname, &l_stat_buf)) { fprintf (stderr, "stat error on '%s'\n", fname); return -1; } l_len = (long)l_stat_buf.st_size; /* Buffer allocate */ l_buffer=(char *)g_malloc0((size_t)l_len+1); if(l_buffer == NULL) { fprintf(stderr, "file_copy: calloc error (%ld Bytes not available)\n", l_len); return -1; } /* load File into Buffer */ l_fp = fopen(fname, "rb"); /* open read */ if(l_fp == NULL) { fprintf (stderr, "open(read) error on '%s'\n", fname); g_free(l_buffer); return -1; } fread(l_buffer, 1, (size_t)l_len, l_fp); fclose(l_fp); l_fp = fopen(fname_copy, "wb"); /* open write */ if(l_fp == NULL) { fprintf (stderr, "file_copy: open(write) error on '%s' \n", fname_copy); g_free(l_buffer); return -1; } if(l_len > 0) { fwrite(l_buffer, l_len, 1, l_fp); } fclose(l_fp); g_free(l_buffer); return 0; /* all done OK */ } /* end p_file_copy */ /* ============================================================================ * p_delete_frame * ============================================================================ */ int p_delete_frame(t_anim_info *ainfo_ptr, long nr) { char *l_fname; char *l_fname_thumbnail; int l_rc; l_fname = p_alloc_fname(ainfo_ptr->basename, nr, ainfo_ptr->extension); if(l_fname == NULL) { return(1); } l_fname_thumbnail = p_alloc_fname_thumbnail(l_fname); if(l_fname_thumbnail == NULL) { return(1); } if(gap_debug) fprintf(stderr, "\nDEBUG p_delete_frame: %s\n", l_fname); l_rc = remove(l_fname); if(gap_debug) fprintf(stderr, "\nDEBUG p_delete_frame: %s\n", l_fname_thumbnail); remove(l_fname_thumbnail); g_free(l_fname); g_free(l_fname_thumbnail); return(l_rc); } /* end p_delete_frame */ /* ============================================================================ * p_rename_frame * ============================================================================ */ int p_rename_frame(t_anim_info *ainfo_ptr, long from_nr, long to_nr) { char *l_from_fname; char *l_to_fname; char *l_from_fname_thumbnail; char *l_to_fname_thumbnail; int l_rc; l_from_fname = p_alloc_fname(ainfo_ptr->basename, from_nr, ainfo_ptr->extension); if(l_from_fname == NULL) { return(1); } l_to_fname = p_alloc_fname(ainfo_ptr->basename, to_nr, ainfo_ptr->extension); if(l_to_fname == NULL) { g_free(l_from_fname); return(1); } l_from_fname_thumbnail = p_alloc_fname_thumbnail(l_from_fname); if(l_from_fname_thumbnail == NULL) { return(1); } l_to_fname_thumbnail = p_alloc_fname_thumbnail(l_to_fname); if(l_to_fname_thumbnail == NULL) { g_free(l_from_fname_thumbnail); return(1); } if(gap_debug) fprintf(stderr, "\nDEBUG p_rename_frame: %s ..to.. %s\n", l_from_fname, l_to_fname); l_rc = rename(l_from_fname, l_to_fname); if(gap_debug) fprintf(stderr, "\nDEBUG p_rename_frame: %s ..to.. %s\n", l_from_fname_thumbnail, l_to_fname_thumbnail); rename(l_from_fname_thumbnail, l_to_fname_thumbnail); g_free(l_from_fname); g_free(l_to_fname); g_free(l_from_fname_thumbnail); g_free(l_to_fname_thumbnail); return(l_rc); } /* end p_rename_frame */ /* ============================================================================ * p_alloc_basename * * build the basename from an images name * Extension and trailing digits ("0000.xcf") are cut off. * return name or NULL (if malloc fails) * Output: number contains the integer representation of the stripped off * number String. (or 0 if there was none) * ============================================================================ */ char* p_alloc_basename(char *imagename, long *number) { char *l_fname; char *l_ptr; long l_idx; *number = 0; if(imagename == NULL) return (NULL); l_fname = (char *)g_malloc(strlen(imagename) + 1); if(l_fname == NULL) return (NULL); /* copy from imagename */ strcpy(l_fname, imagename); if(gap_debug) fprintf(stderr, "DEBUG p_alloc_basename source: '%s'\n", l_fname); /* cut off extension */ l_ptr = &l_fname[strlen(l_fname)]; while(l_ptr != l_fname) { if((*l_ptr == G_DIR_SEPARATOR) || (*l_ptr == DIR_ROOT)) { break; } /* dont run into dir part */ if(*l_ptr == '.') { *l_ptr = '\0'; break; } l_ptr--; } if(gap_debug) fprintf(stderr, "DEBUG p_alloc_basename (ext_off): '%s'\n", l_fname); /* cut off trailing digits (0000) */ l_ptr = &l_fname[strlen(l_fname)]; if(l_ptr != l_fname) l_ptr--; l_idx = 1; while(l_ptr != l_fname) { if((*l_ptr >= '0') && (*l_ptr <= '9')) { *number += ((*l_ptr - '0') * l_idx); l_idx = l_idx * 10; *l_ptr = '\0'; l_ptr--; } else { /* do not cut off underscore any longer */ /* * if(*l_ptr == '_') * { * *l_ptr = '\0'; * } */ break; } } if(gap_debug) fprintf(stderr, "DEBUG p_alloc_basename result:'%s'\n", l_fname); return(l_fname); } /* end p_alloc_basename */ /* ============================================================================ * p_alloc_extension * * input: a filename * returns: a copy of the filename extension (incl "." ) * if the filename has no extension, a pointer to * an empty string is returned ("\0") * NULL if allocate mem for extension failed. * ============================================================================ */ char* p_alloc_extension(char *imagename) { int l_exlen; char *l_ext; char *l_ptr; l_exlen = 0; l_ptr = &imagename[strlen(imagename)]; while(l_ptr != imagename) { if((*l_ptr == G_DIR_SEPARATOR) || (*l_ptr == DIR_ROOT)) { break; } /* dont run into dir part */ if(*l_ptr == '.') { l_exlen = strlen(l_ptr); break; } l_ptr--; } l_ext = g_malloc0((size_t)(l_exlen + 1)); if(l_ext == NULL) return (NULL); if(l_exlen > 0) strcpy(l_ext, l_ptr); return(l_ext); } /* ============================================================================ * p_alloc_fname * * build the name of a frame using "basename_0000.ext" * * return name or NULL (if malloc fails) * ============================================================================ */ char* p_alloc_fname(char *basename, long nr, char *extension) { gchar *l_fname; gint l_leading_zeroes; gint l_len; long l_nr_chk; if(basename == NULL) return (NULL); l_len = (strlen(basename) + strlen(extension) + 10); l_fname = (char *)g_malloc(l_len); l_leading_zeroes = TRUE; if(nr < 1000) { /* try to figure out if the frame numbers are in * 4-digit style, with leading zeroes "frame_0001.xcf" * or not "frame_1.xcf" */ l_nr_chk = nr; while(l_nr_chk >= 0) { /* check if frame is on disk with 4-digit style framenumber */ g_snprintf(l_fname, l_len, "%s%04ld%s", basename, l_nr_chk, extension); if (p_file_exists(l_fname)) { break; } /* now check for filename without leading zeroes in the framenumber */ g_snprintf(l_fname, l_len, "%s%ld%s", basename, l_nr_chk, extension); if (p_file_exists(l_fname)) { l_leading_zeroes = FALSE; break; } l_nr_chk--; /* if the frames nr and nr-1 were not found * try to check frames 1 and 0 * to limit down the loop to max 4 cycles */ if((l_nr_chk == nr -2) && (l_nr_chk > 1)) { l_nr_chk = 1; } } } else { l_leading_zeroes = FALSE; } g_free(l_fname); if(l_leading_zeroes) l_fname = g_strdup_printf("%s%04ld%s", basename, nr, extension); else l_fname = g_strdup_printf("%s%ld%s", basename, nr, extension); return(l_fname); } /* end p_alloc_fname */ /* ============================================================================ * p_alloc_ainfo * * allocate and init an ainfo structure from the given image. * ============================================================================ */ t_anim_info *p_alloc_ainfo(gint32 image_id, GimpRunModeType run_mode) { t_anim_info *l_ainfo_ptr; l_ainfo_ptr = (t_anim_info*)g_malloc(sizeof(t_anim_info)); if(l_ainfo_ptr == NULL) return(NULL); l_ainfo_ptr->basename = NULL; l_ainfo_ptr->new_filename = NULL; l_ainfo_ptr->extension = NULL; l_ainfo_ptr->image_id = image_id; /* get current gimp images name */ l_ainfo_ptr->old_filename = gimp_image_get_filename(image_id); if(l_ainfo_ptr->old_filename == NULL) { /* note: gimp versions > 1.2 have default filenames for new created images * and we'll probably never step into this place anymore */ l_ainfo_ptr->old_filename = g_strdup("frame_0001.xcf"); /* assign a defaultname */ gimp_image_set_filename (image_id, l_ainfo_ptr->old_filename); } l_ainfo_ptr->basename = p_alloc_basename(l_ainfo_ptr->old_filename, &l_ainfo_ptr->frame_nr); if(NULL == l_ainfo_ptr->basename) { p_free_ainfo(&l_ainfo_ptr); return(NULL); } l_ainfo_ptr->extension = p_alloc_extension(l_ainfo_ptr->old_filename); l_ainfo_ptr->curr_frame_nr = l_ainfo_ptr->frame_nr; l_ainfo_ptr->first_frame_nr = -1; l_ainfo_ptr->last_frame_nr = -1; l_ainfo_ptr->frame_cnt = 0; l_ainfo_ptr->run_mode = run_mode; return(l_ainfo_ptr); } /* end p_init_ainfo */ /* ============================================================================ * p_dir_ainfo * * fill in more items into the given ainfo structure. * - first_frame_nr * - last_frame_nr * - frame_cnt * * to get this information, the directory entries have to be checked * ============================================================================ */ int p_dir_ainfo(t_anim_info *ainfo_ptr) { char *l_dirname; char *l_dirname_ptr; char *l_ptr; char *l_exptr; char *l_dummy; /* int l_cmp_len; */ DIR *l_dirp; struct dirent *l_dp; long l_nr; long l_maxnr; long l_minnr; short l_dirflag; char dirname_buff[1024]; l_dirname = g_malloc(strlen(ainfo_ptr->basename) +1); if(l_dirname == NULL) return -1; ainfo_ptr->frame_cnt = 0; l_dirp = NULL; l_minnr = 99999999; l_maxnr = 0; strcpy(l_dirname, ainfo_ptr->basename); l_ptr = &l_dirname[strlen(l_dirname)]; while(l_ptr != l_dirname) { if ((*l_ptr == G_DIR_SEPARATOR) || (*l_ptr == DIR_ROOT)) { *l_ptr = '\0'; /* split the string into dirpath 0 basename */ l_ptr++; break; /* stop at char behind the slash */ } l_ptr--; } /* l_cmp_len = strlen(l_ptr); */ if(gap_debug) fprintf(stderr, "DEBUG p_dir_ainfo: BASENAME:%s\n", l_ptr); if (l_ptr == l_dirname) { l_dirname_ptr = "."; l_dirflag = 0; } else if (*l_dirname == '\0') { l_dirname_ptr = G_DIR_SEPARATOR_S ; l_dirflag = 1; } else { l_dirname_ptr = l_dirname; l_dirflag = 2; } if(gap_debug) fprintf(stderr, "DEBUG p_dir_ainfo: DIRNAME:%s\n", l_dirname_ptr); l_dirp = opendir( l_dirname_ptr ); if(!l_dirp) fprintf(stderr, "ERROR p_dir_ainfo: can't read directory %s\n", l_dirname_ptr); else { while ( (l_dp = readdir( l_dirp )) != NULL ) { /* if(gap_debug) fprintf(stderr, "DEBUG p_dir_ainfo: l_dp->d_name:%s\n", l_dp->d_name); */ /* findout extension of the directory entry name */ l_exptr = &l_dp->d_name[strlen(l_dp->d_name)]; while(l_exptr != l_dp->d_name) { if(*l_exptr == G_DIR_SEPARATOR) { break; } /* dont run into dir part */ if(*l_exptr == '.') { break; } l_exptr--; } /* l_exptr now points to the "." of the direntry (or to its begin if has no ext) */ /* now check for equal extension */ if((*l_exptr == '.') && (0 == strcmp(l_exptr, ainfo_ptr->extension))) { /* build full pathname (to check if file exists) */ switch(l_dirflag) { case 0: g_snprintf(dirname_buff, sizeof(dirname_buff), "%s", l_dp->d_name); break; case 1: g_snprintf(dirname_buff, sizeof(dirname_buff), "%c%s", G_DIR_SEPARATOR, l_dp->d_name); break; default: /* UNIX: "/dir/file" * DOS: "drv:\dir\file" */ g_snprintf(dirname_buff, sizeof(dirname_buff), "%s%c%s", l_dirname_ptr, G_DIR_SEPARATOR, l_dp->d_name); break; } if(1 == p_file_exists(dirname_buff)) /* check for regular file */ { /* get basename and frame number of the directory entry */ l_dummy = p_alloc_basename(l_dp->d_name, &l_nr); if(l_dummy != NULL) { /* check for files, with equal basename (frames) * (length must be greater than basepart+extension * because of the frame_nr part "0000") */ if((0 == strcmp(l_ptr, l_dummy)) && ( strlen(l_dp->d_name) > strlen(l_dummy) + strlen(l_exptr) )) { ainfo_ptr->frame_cnt++; if(gap_debug) fprintf(stderr, "DEBUG p_dir_ainfo: %s NR=%ld\n", l_dp->d_name, l_nr); if (l_nr > l_maxnr) l_maxnr = l_nr; if (l_nr < l_minnr) l_minnr = l_nr; } g_free(l_dummy); } } } } closedir( l_dirp ); } g_free(l_dirname); /* set first_frame_nr and last_frame_nr (found as "_0099" in diskfile namepart) */ ainfo_ptr->last_frame_nr = l_maxnr; ainfo_ptr->first_frame_nr = MIN(l_minnr, l_maxnr); return 0; /* OK */ } /* end p_dir_ainfo */ /* ============================================================================ * p_free_ainfo * * free ainfo and its allocated stuff * ============================================================================ */ void p_free_ainfo(t_anim_info **ainfo) { t_anim_info *aptr; if((aptr = *ainfo) == NULL) return; if(aptr->basename) g_free(aptr->basename); if(aptr->extension) g_free(aptr->extension); if(aptr->new_filename) g_free(aptr->new_filename); if(aptr->old_filename) g_free(aptr->old_filename); g_free(aptr); } /* ============================================================================ * p_get_frame_nr * ============================================================================ */ long p_get_frame_nr_from_name(char *fname) { long number; int len; char *basename; if(fname == NULL) return(-1); basename = p_alloc_basename(fname, &number); if(basename == NULL) return(-1); len = strlen(basename); g_free(basename); if(number > 0) return(number); if(fname[len] == '0') return(number); /* * if(fname[len] == '_') * { * if(fname[len+1] == '0') return(TRUE); * } */ return(-1); } long p_get_frame_nr(gint32 image_id) { char *fname; long number; number = -1; fname = gimp_image_get_filename(image_id); if(fname) { number = p_get_frame_nr_from_name(fname); g_free(fname); } return (number); } /* ============================================================================ * p_chk_framechange * * check if the current frame nr has changed. * useful after dialogs, because the user may have renamed the current image * (using save as) * while the gap-plugin's dialog was open. * return: 0 .. OK * -1 .. Changed (or error occured) * ============================================================================ */ int p_chk_framechange(t_anim_info *ainfo_ptr) { int l_rc; t_anim_info *l_ainfo_ptr; l_rc = -1; l_ainfo_ptr = p_alloc_ainfo(ainfo_ptr->image_id, ainfo_ptr->run_mode); if(l_ainfo_ptr != NULL) { if(ainfo_ptr->curr_frame_nr == l_ainfo_ptr->curr_frame_nr ) { l_rc = 0; } else { p_msg_win(ainfo_ptr->run_mode, _("OPERATION CANCELLED.\n" "Current frame changed while dialog was open.")); } p_free_ainfo(&l_ainfo_ptr); } return l_rc; } /* end p_chk_framechange */ /* ============================================================================ * p_chk_framerange * * check ainfo struct if there is a framerange (of at least one frame) * return: 0 .. OK * -1 .. No frames there (print error) * ============================================================================ */ int p_chk_framerange(t_anim_info *ainfo_ptr) { if(ainfo_ptr->frame_cnt == 0) { p_msg_win(ainfo_ptr->run_mode, _("OPERATION CANCELLED.\n" "GAP plug-ins only work with filenames\n" "that end in numbers like _0001.xcf.\n" "==> Rename your image, then try again.")); return -1; } return 0; } /* end p_chk_framerange */ /* ============================================================================ * p_gzip * gzip or gunzip the file to a temporary file. * zip == "zip" compress * zip == "unzip" decompress * return a pointer to the temporary created (by gzip) file. * NULL in case of errors * ============================================================================ */ char * p_gzip (char *orig_name, char *new_name, char *zip) { gchar* l_cmd; gchar* l_tmpname; gint l_rc, l_rc2; if(zip == NULL) return NULL; l_cmd = NULL; l_tmpname = new_name; /* used gzip options: * -c --stdout --to-stdout * Write output on standard output * -d --decompress --uncompress * Decompress. * -f --force * Force compression or decompression even if the file */ if(*zip == 'u') { l_cmd = g_strdup_printf("gzip -cfd <\"%s\" >\"%s\"", orig_name, l_tmpname); } else { l_cmd = g_strdup_printf("gzip -cf <\"%s\" >\"%s\"", orig_name, l_tmpname); } if(gap_debug) fprintf(stderr, "system: %s\n", l_cmd); l_rc = system(l_cmd); if(l_rc != 0) { /* Shift 8 Bits gets Retcode of the executed Program */ l_rc2 = l_rc >> 8; fprintf(stderr, "ERROR system: %s\nreturncodes %d %d", l_cmd, l_rc, l_rc2); l_tmpname = NULL; } g_free(l_cmd); return l_tmpname; } /* end p_gzip */ /* ============================================================================ * p_decide_save_as * decide what to to, when attempt to save a frame in any image format * (other than xcf) * Let the user decide if the frame is to save "as it is" or "flattened" * ("as it is" will save only the backround layer in most fileformat types) * The SAVE_AS_MODE is stored , and reused * (without displaying the dialog) on subsequent calls. * * return -1 ... CANCEL (do not save) * 0 ... save the image (may be flattened) * ============================================================================ */ int p_decide_save_as(gint32 image_id, char *sav_name) { static char *l_msg; static char l_save_as_name[80]; static t_but_arg l_argv[3]; int l_argc; int l_save_as_mode; GimpRunModeType l_run_mode; l_msg = _("You are using a file format != xcf\n" "Save Operations may result\n" "in loss of layer information."); /* check if there are SAVE_AS_MODE settings (from privious calls within one gimp session) */ l_save_as_mode = -1; /* g_snprintf(l_save_as_name, sizeof(l_save_as_name), "plug_in_gap_plugins_SAVE_AS_MODE_%d", (int)image_id);*/ g_snprintf(l_save_as_name, sizeof(l_save_as_name), "plug_in_gap_plugins_SAVE_AS_MODE"); gimp_get_data (l_save_as_name, &l_save_as_mode); if(l_save_as_mode == -1) { /* no defined value found (this is the 1.st call for this image_id) * ask what to do with a 3 Button dialog */ l_argv[0].but_txt = GTK_STOCK_CANCEL; l_argv[0].but_val = -1; l_argv[1].but_txt = _("Save Flattened"); l_argv[1].but_val = 1; l_argv[2].but_txt = _("Save As Is"); l_argv[2].but_val = 0; l_argc = 3; l_save_as_mode = p_buttons_dialog (_("GAP Question"), l_msg, l_argc, l_argv, -1); if(gap_debug) fprintf(stderr, "DEBUG: decide SAVE_AS_MODE %d\n", (int)l_save_as_mode); if(l_save_as_mode < 0) return -1; l_run_mode = GIMP_RUN_INTERACTIVE; } else { l_run_mode = GIMP_RUN_WITH_LAST_VALS; } gimp_set_data (l_save_as_name, &l_save_as_mode, sizeof(l_save_as_mode)); if(l_save_as_mode == 1) { gimp_image_flatten (image_id); } return(p_save_named_image(image_id, sav_name, l_run_mode)); } /* end p_decide_save_as */ /* ============================================================================ * p_save_named_image * ============================================================================ */ gint32 p_save_named_image(gint32 image_id, char *sav_name, GimpRunModeType run_mode) { GimpDrawable *l_drawable; gint l_nlayers; gint32 *l_layers_list; GimpParam *l_params; gint l_retvals; if(gap_debug) fprintf(stderr, "DEBUG: before p_save_named_image: '%s'\n", sav_name); l_layers_list = gimp_image_get_layers(image_id, &l_nlayers); if(l_layers_list == NULL) return -1; l_drawable = gimp_drawable_get(l_layers_list[l_nlayers -1]); /* use the background layer */ if(l_drawable == NULL) { fprintf(stderr, "ERROR: p_save_named_image gimp_drawable_get failed '%s' nlayers=%d\n", sav_name, (int)l_nlayers); g_free (l_layers_list); return -1; } l_params = gimp_run_procedure ("gimp_file_save", &l_retvals, GIMP_PDB_INT32, run_mode, GIMP_PDB_IMAGE, image_id, GIMP_PDB_DRAWABLE, l_drawable->drawable_id, GIMP_PDB_STRING, sav_name, GIMP_PDB_STRING, sav_name, /* raw name ? */ GIMP_PDB_END); if(gap_debug) fprintf(stderr, "DEBUG: after p_save_named_image: '%s' nlayers=%d image=%d drw=%d run_mode=%d\n", sav_name, (int)l_nlayers, (int)image_id, (int)l_drawable->drawable_id, (int)run_mode); p_gimp_file_save_thumbnail(image_id, sav_name); g_free (l_layers_list); g_free (l_drawable); if (l_params[0].data.d_status != GIMP_PDB_SUCCESS) { fprintf(stderr, "ERROR: p_save_named_image gimp_file_save failed '%s'\n", sav_name); g_free(l_params); return -1; } else { g_free(l_params); return image_id; } } /* end p_save_named_image */ /* ============================================================================ * p_save_named_frame * save frame into temporary image, * on success rename it to desired framename. * (this is done, to avoid corrupted frames on disk in case of * crash in one of the save procedures) * ============================================================================ */ int p_save_named_frame(gint32 image_id, char *sav_name) { GimpParam *l_params; gchar *l_ext; char *l_tmpname; gint l_retvals; int l_gzip; int l_xcf; int l_rc; l_tmpname = NULL; l_rc = -1; l_gzip = 0; /* dont zip */ l_xcf = 0; /* assume no xcf format */ /* check extension to decide if savd file will be zipped */ l_ext = p_alloc_extension(sav_name); if(l_ext == NULL) return -1; if(0 == strcmp(l_ext, ".xcf")) { l_xcf = 1; } else if(0 == strcmp(l_ext, ".xcfgz")) { l_gzip = 1; /* zip it */ l_xcf = 1; } else if(0 == strcmp(l_ext, ".gz")) { l_gzip = 1; /* zip it */ } /* find a temp name * that resides on the same filesystem as sav_name * and has the same extension as the original sav_name */ l_tmpname = g_strdup_printf("%s.gtmp%s", sav_name, l_ext); if(1 == p_file_exists(l_tmpname)) { /* FILE exists: let gimp find another temp name */ l_params = gimp_run_procedure ("gimp_temp_name", &l_retvals, GIMP_PDB_STRING, &l_ext[1], GIMP_PDB_END); if(l_params[1].data.d_string != NULL) { l_tmpname = l_params[1].data.d_string; } g_free(l_params); } g_free(l_ext); if(gap_debug) { l_ext = (gchar *) g_getenv("GAP_NO_SAVE"); if(l_ext != NULL) { fprintf(stderr, "DEBUG: GAP_NO_SAVE is set: save is skipped: '%s'\n", l_tmpname); g_free(l_tmpname); /* free if it was a temporary name */ return 0; } } if(gap_debug) fprintf(stderr, "DEBUG: before p_save_named_frame: '%s'\n", l_tmpname); if(l_xcf != 0) { /* save current frame as xcf image * xcf_save does operate on the complete image, * the drawable is ignored. (we can supply a dummy value) */ l_params = gimp_run_procedure ("gimp_xcf_save", &l_retvals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, image_id, GIMP_PDB_DRAWABLE, 0, GIMP_PDB_STRING, l_tmpname, GIMP_PDB_STRING, l_tmpname, /* raw name ? */ GIMP_PDB_END); if(gap_debug) fprintf(stderr, "DEBUG: after xcf p_save_named_frame: '%s'\n", l_tmpname); if (l_params[0].data.d_status == GIMP_PDB_SUCCESS) { l_rc = image_id; } g_free(l_params); } else { /* let gimp try to save (and detect filetype by extension) * Note: the most imagefileformats do not support multilayer * images, and extra channels * the result may not contain the whole imagedata * * To Do: Should warn the user at 1.st attempt to do this. */ l_rc = p_decide_save_as(image_id, l_tmpname); } if(l_rc < 0) { remove(l_tmpname); g_free(l_tmpname); /* free temporary name */ return l_rc; } if(l_gzip == 0) { /* remove sav_name, then rename tmpname ==> sav_name */ remove(sav_name); if (0 != rename(l_tmpname, sav_name)) { /* if tempname is located on another filesystem (errno == EXDEV) * rename will not work. * so lets try a copy ; remove sequence */ if(gap_debug) fprintf(stderr, "DEBUG: p_save_named_frame: RENAME 2nd try\n"); if(0 == p_file_copy(l_tmpname, sav_name)) { remove(l_tmpname); } else { fprintf(stderr, "ERROR in p_save_named_frame: can't rename %s to %s\n", l_tmpname, sav_name); return -1; } } } else { /* ZIP tmpname ==> sav_name */ if(NULL != p_gzip(l_tmpname, sav_name, "zip")) { /* OK zip created compressed file named sav_name * now delete the uncompressed l_tempname */ remove(l_tmpname); } } p_gimp_file_save_thumbnail(image_id, sav_name); g_free(l_tmpname); /* free temporary name */ return l_rc; } /* end p_save_named_frame */ /* ============================================================================ * p_save_old_frame * ============================================================================ */ int p_save_old_frame(t_anim_info *ainfo_ptr) { /* (here we should check if image has unsaved changes * before save) */ if(1) { return (p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename)); } else { return -1; } } /* end p_save_old_frame */ /* ============================================================================ * p_load_image * load image of any type by filename, and return its image id * (or -1 in case of errors) * ============================================================================ */ gint32 p_load_image (char *lod_name) { GimpParam* l_params; char *l_ext; char *l_tmpname; gint l_retvals; gint32 l_tmp_image_id; int l_rc; l_rc = 0; l_tmpname = NULL; l_ext = p_alloc_extension(lod_name); if(l_ext != NULL) { if((0 == strcmp(l_ext, ".xcfgz")) || (0 == strcmp(l_ext, ".gz"))) { /* find a temp name */ l_params = gimp_run_procedure ("gimp_temp_name", &l_retvals, GIMP_PDB_STRING, &l_ext[1], /* extension */ GIMP_PDB_END); if(l_params[1].data.d_string != NULL) { /* try to unzip file, before loading it */ l_tmpname = p_gzip(lod_name, l_params[1].data.d_string, "unzip"); } g_free(l_params); } else l_tmpname = lod_name; g_free(l_ext); } if(l_tmpname == NULL) { return -1; } if(gap_debug) fprintf(stderr, "DEBUG: before p_load_image: '%s'\n", l_tmpname); l_params = gimp_run_procedure ("gimp_file_load", /* "gimp_xcf_load" */ &l_retvals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_STRING, l_tmpname, GIMP_PDB_STRING, l_tmpname, /* raw name ? */ GIMP_PDB_END); l_tmp_image_id = l_params[1].data.d_int32; if(gap_debug) fprintf(stderr, "DEBUG: after p_load_image: '%s' id=%d\n\n", l_tmpname, (int)l_tmp_image_id); if(l_tmpname != lod_name) { remove(l_tmpname); g_free(l_tmpname); /* free if it was a temporary name */ } g_free(l_params); return l_tmp_image_id; } /* end p_load_image */ /* ============================================================================ * p_load_named_frame * load new frame, replacing the existing image * file must be of same type and size * ============================================================================ */ int p_load_named_frame (gint32 image_id, char *lod_name) { gint32 l_tmp_image_id; int l_rc; l_rc = 0; l_tmp_image_id = p_load_image(lod_name); if(gap_debug) fprintf(stderr, "DEBUG: after p_load_named_frame: '%s' id=%d new_id=%d\n\n", lod_name, (int)image_id, (int)l_tmp_image_id); if(l_tmp_image_id < 0) return -1; /* replace image_id with the content of l_tmp_image_id */ if(p_exchange_image (image_id, l_tmp_image_id) < 0) { /* in case of errors the image will be trashed */ image_id = -1; } /* delete the temporary image (old content of the original image) */ if(gap_debug) printf("p_load_named_frame: BEFORE gimp_image_delete %d\n", (int)l_tmp_image_id); gimp_image_delete(l_tmp_image_id); if(gap_debug) printf("p_load_named_frame: AFTER gimp_image_delete %d\n", (int)l_tmp_image_id); /* use the original lod_name */ gimp_image_set_filename (image_id, lod_name); /* dont consider image dirty after load */ gimp_image_clean_all(image_id); return image_id; } /* end p_load_named_frame */ /* ============================================================================ * p_replace_image * * make new_filename of next file to load, check if that file does exist on disk * then save current image and replace it, by loading the new_filename * ============================================================================ */ int p_replace_image(t_anim_info *ainfo_ptr) { if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename); ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->frame_nr, ainfo_ptr->extension); if(ainfo_ptr->new_filename == NULL) return -1; if(0 == p_file_exists(ainfo_ptr->new_filename )) return -1; if(p_save_old_frame(ainfo_ptr) <0) { return -1; } else { return (p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->new_filename)); } } /* end p_replace_image */ /* ============================================================================ * p_del * * delete cnt frames starting at current * all following frames are renamed (renumbered down by cnt) * ============================================================================ */ int p_del(t_anim_info *ainfo_ptr, long cnt) { long l_lo, l_hi, l_curr, l_idx; if(gap_debug) fprintf(stderr, "DEBUG p_del\n"); if(cnt < 1) return -1; l_curr = ainfo_ptr->curr_frame_nr; if((1 + ainfo_ptr->last_frame_nr - l_curr) < cnt) { /* limt cnt to last existing frame */ cnt = 1 + ainfo_ptr->frame_cnt - l_curr; } if(cnt >= ainfo_ptr->frame_cnt) { /* dont want to delete all frames * so we have to leave a rest of one frame */ cnt--; } l_idx = l_curr; while(l_idx < (l_curr + cnt)) { p_delete_frame(ainfo_ptr, l_idx); l_idx++; } /* rename (renumber) all frames with number greater than current */ l_lo = l_curr; l_hi = l_curr + cnt; while(l_hi <= ainfo_ptr->last_frame_nr) { if(0 != p_rename_frame(ainfo_ptr, l_hi, l_lo)) { gchar *tmp_errtxt; tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld") ,l_hi, l_lo); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } l_lo++; l_hi++; } /* calculate how much frames are left */ ainfo_ptr->frame_cnt -= cnt; ainfo_ptr->last_frame_nr = ainfo_ptr->first_frame_nr + ainfo_ptr->frame_cnt -1; /* set current position to previous frame (if there is one) */ if(l_curr > ainfo_ptr->first_frame_nr) { ainfo_ptr->frame_nr = l_curr -1; } else { ainfo_ptr->frame_nr = ainfo_ptr->first_frame_nr; } /* make filename, then load the new current frame */ if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename); ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->frame_nr, ainfo_ptr->extension); if(ainfo_ptr->new_filename != NULL) return (p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->new_filename)); else return -1; } /* end p_del */ /* ============================================================================ * p_dup * * all following frames are renamed (renumbered up by cnt) * current frame is duplicated (cnt) times * ============================================================================ */ int p_dup(t_anim_info *ainfo_ptr, long cnt, long range_from, long range_to) { long l_lo, l_hi; long l_cnt2; long l_step; long l_src_nr, l_src_nr_min, l_src_nr_max; char *l_dup_name; char *l_curr_name; gdouble l_percentage, l_percentage_step; if(gap_debug) fprintf(stderr, "DEBUG p_dup fr:%d to:%d cnt:%d extension:%s: basename:%s frame_cnt:%d\n", (int)range_from, (int)range_to, (int)cnt, ainfo_ptr->extension, ainfo_ptr->basename, (int)ainfo_ptr->frame_cnt); if(cnt < 1) return -1; l_curr_name = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); /* save current frame */ if(p_save_named_frame(ainfo_ptr->image_id, l_curr_name) < 0) { gchar *tmp_errtxt; tmp_errtxt = g_strdup_printf(_("Error: could not save frame %s"), l_curr_name); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } /* use a new name (0001.xcf Konvention) */ gimp_image_set_filename (ainfo_ptr->image_id, l_curr_name); g_free(l_curr_name); if((range_from <0 ) && (range_to < 0 )) { /* set range to one single current frame * (used for the old non_interactive PDB-interface without range params) */ range_from = ainfo_ptr->curr_frame_nr; range_to = ainfo_ptr->curr_frame_nr; } /* clip range */ if(range_from > ainfo_ptr->last_frame_nr) range_from = ainfo_ptr->last_frame_nr; if(range_to > ainfo_ptr->last_frame_nr) range_to = ainfo_ptr->last_frame_nr; if(range_from < ainfo_ptr->first_frame_nr) range_from = ainfo_ptr->first_frame_nr; if(range_to < ainfo_ptr->first_frame_nr) range_to = ainfo_ptr->first_frame_nr; if(range_to < range_from) { /* invers range */ l_cnt2 = ((range_from - range_to ) + 1) * cnt; l_step = -1; l_src_nr_max = range_from; l_src_nr_min = range_to; } else { l_cnt2 = ((range_to - range_from ) + 1) * cnt; l_step = 1; l_src_nr_max = range_to; l_src_nr_min = range_from; } if(gap_debug) fprintf(stderr, "DEBUG p_dup fr:%d to:%d cnt2:%d l_src_nr_max:%d\n", (int)range_from, (int)range_to, (int)l_cnt2, (int)l_src_nr_max); l_percentage = 0.0; if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE) { gimp_progress_init( _("Duplicating frames...")); } /* rename (renumber) all frames with number greater than current */ l_lo = ainfo_ptr->last_frame_nr; l_hi = l_lo + l_cnt2; while(l_lo > l_src_nr_max) { if(0 != p_rename_frame(ainfo_ptr, l_lo, l_hi)) { gchar *tmp_errtxt; tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld"), l_lo, l_hi); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } l_lo--; l_hi--; } l_percentage_step = 1.0 / ((1.0 + l_hi) - l_src_nr_max); if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE) { l_percentage += l_percentage_step; gimp_progress_update (l_percentage); } /* copy cnt duplicates */ l_src_nr = range_to; while(l_hi > l_src_nr_max) { l_curr_name = p_alloc_fname(ainfo_ptr->basename, l_src_nr, ainfo_ptr->extension); l_dup_name = p_alloc_fname(ainfo_ptr->basename, l_hi, ainfo_ptr->extension); if((l_dup_name != NULL) && (l_curr_name != NULL)) { p_image_file_copy(l_curr_name, l_dup_name); g_free(l_dup_name); g_free(l_curr_name); } if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE) { l_percentage += l_percentage_step; gimp_progress_update (l_percentage); } l_src_nr -= l_step; if(l_src_nr < l_src_nr_min) l_src_nr = l_src_nr_max; if(l_src_nr > l_src_nr_max) l_src_nr = l_src_nr_min; l_hi--; } /* restore current position */ ainfo_ptr->frame_cnt += l_cnt2; ainfo_ptr->last_frame_nr = ainfo_ptr->first_frame_nr + ainfo_ptr->frame_cnt -1; /* load from the "new" current frame */ if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename); ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); return (p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->new_filename)); } /* end p_dup */ /* ============================================================================ * p_exchg * * save current frame, exchange its name with destination frame on disk * and reload current frame (now has contents of dest. and vice versa) * ============================================================================ */ int p_exchg(t_anim_info *ainfo_ptr, long dest) { long l_tmp_nr; gchar *tmp_errtxt; l_tmp_nr = ainfo_ptr->last_frame_nr + 4; /* use a free frame_nr for temp name */ if((dest < 1) || (dest == ainfo_ptr->curr_frame_nr)) return -1; if(p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename) < 0) return -1; /* rename (renumber) frames */ if(0 != p_rename_frame(ainfo_ptr, dest, l_tmp_nr)) { tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld"), dest, l_tmp_nr); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } if(0 != p_rename_frame(ainfo_ptr, ainfo_ptr->curr_frame_nr, dest)) { tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld"), ainfo_ptr->curr_frame_nr, dest); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } if(0 != p_rename_frame(ainfo_ptr, l_tmp_nr, ainfo_ptr->curr_frame_nr)) { tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld"), l_tmp_nr, ainfo_ptr->curr_frame_nr); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } /* load from the "new" current frame */ if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename); ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); return (p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->new_filename)); } /* end p_exchg */ /* ============================================================================ * p_shift * * all frmaes in the given range are renumbered (shifted) * according to cnt: * example: cnt == 1 : range before 3, 4, 5, 6, 7 * range after 4, 5, 6, 7, 3 * ============================================================================ */ static int p_shift(t_anim_info *ainfo_ptr, long cnt, long range_from, long range_to) { long l_lo, l_hi, l_curr, l_dst; long l_upper; long l_shift; gchar *l_curr_name; gchar *tmp_errtxt; gdouble l_percentage, l_percentage_step; if(gap_debug) fprintf(stderr, "DEBUG p_shift fr:%d to:%d cnt:%d\n", (int)range_from, (int)range_to, (int)cnt); if(range_from == range_to) return -1; /* clip range */ if(range_from > ainfo_ptr->last_frame_nr) range_from = ainfo_ptr->last_frame_nr; if(range_to > ainfo_ptr->last_frame_nr) range_to = ainfo_ptr->last_frame_nr; if(range_from < ainfo_ptr->first_frame_nr) range_from = ainfo_ptr->first_frame_nr; if(range_to < ainfo_ptr->first_frame_nr) range_to = ainfo_ptr->first_frame_nr; if(range_to < range_from) { l_lo = range_to; l_hi = range_from; } else { l_lo = range_from; l_hi = range_to; } /* limit shift amount to number of frames in range */ l_shift = cnt % (l_hi - l_lo); if(gap_debug) fprintf(stderr, "DEBUG p_shift shift:%d\n", (int)l_shift); if(l_shift == 0) return -1; l_curr_name = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); /* save current frame */ p_save_named_frame(ainfo_ptr->image_id, l_curr_name); g_free(l_curr_name); l_percentage = 0.0; if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE) { gimp_progress_init( _("Renumber Framesequence...")); } /* rename (renumber) all frames (using high numbers) */ l_upper = ainfo_ptr->last_frame_nr +100; l_percentage_step = 0.5 / ((1.0 + l_lo) - l_hi); for(l_curr = l_lo; l_curr <= l_hi; l_curr++) { if(0 != p_rename_frame(ainfo_ptr, l_curr, l_curr + l_upper)) { tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld"), l_lo, l_hi); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE) { l_percentage += l_percentage_step; gimp_progress_update (l_percentage); } } /* rename (renumber) all frames (using desied destination numbers) */ l_dst = l_lo + l_shift; if (l_dst > l_hi) { l_dst -= (l_lo -1); } if (l_dst < l_lo) { l_dst += ((l_hi - l_lo) +1); } for(l_curr = l_upper + l_lo; l_curr <= l_upper + l_hi; l_curr++) { if (l_dst > l_hi) { l_dst = l_lo; } if(0 != p_rename_frame(ainfo_ptr, l_curr, l_dst)) { tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld"), l_lo, l_hi); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE) { l_percentage += l_percentage_step; gimp_progress_update (l_percentage); } l_dst ++; } /* load from the "new" current frame */ if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename); ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); return (p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->new_filename)); } /* end p_shift */ /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ /* ============================================================================ * gap_next gap_prev * * store the current Gimp Image to the current anim Frame * and load it from the next/prev anim Frame on disk. * ============================================================================ */ int gap_next(GimpRunModeType run_mode, gint32 image_id) { int rc; t_anim_info *ainfo_ptr; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { ainfo_ptr->frame_nr = ainfo_ptr->curr_frame_nr + 1; rc = p_replace_image(ainfo_ptr); p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_next */ int gap_prev(GimpRunModeType run_mode, gint32 image_id) { int rc; t_anim_info *ainfo_ptr; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { ainfo_ptr->frame_nr = ainfo_ptr->curr_frame_nr - 1; rc = p_replace_image(ainfo_ptr); p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_prev */ /* ============================================================================ * gap_first gap_last * * store the current Gimp Image to the current anim Frame * and load it from the first/last anim Frame on disk. * ============================================================================ */ int gap_first(GimpRunModeType run_mode, gint32 image_id) { int rc; t_anim_info *ainfo_ptr; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { if (0 == p_dir_ainfo(ainfo_ptr)) { ainfo_ptr->frame_nr = ainfo_ptr->first_frame_nr; rc = p_replace_image(ainfo_ptr); } p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_first */ int gap_last(GimpRunModeType run_mode, gint32 image_id) { int rc; t_anim_info *ainfo_ptr; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { if (0 == p_dir_ainfo(ainfo_ptr)) { ainfo_ptr->frame_nr = ainfo_ptr->last_frame_nr; rc = p_replace_image(ainfo_ptr); } p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_last */ /* ============================================================================ * gap_goto * * store the current Gimp Image to disk * and load it from the anim Frame on disk that has the specified frame Nr. * GIMP_RUN_INTERACTIVE: * show dialogwindow where user can enter the destination frame Nr. * ============================================================================ */ int gap_goto(GimpRunModeType run_mode, gint32 image_id, int nr) { int rc; t_anim_info *ainfo_ptr; long l_dest; gchar *l_hline; gchar *l_title; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { if (0 == p_dir_ainfo(ainfo_ptr)) { if(0 != p_chk_framerange(ainfo_ptr)) return -1; if(run_mode == GIMP_RUN_INTERACTIVE) { l_title = g_strdup_printf (_("Goto Frame (%ld/%ld)") , ainfo_ptr->curr_frame_nr , ainfo_ptr->frame_cnt); l_hline = g_strdup_printf (_("Destination Frame Number (%ld - %ld)") , ainfo_ptr->first_frame_nr , ainfo_ptr->last_frame_nr); l_dest = p_slider_dialog(l_title, l_hline, _("Number:"), NULL , ainfo_ptr->first_frame_nr , ainfo_ptr->last_frame_nr , ainfo_ptr->curr_frame_nr , TRUE); g_free (l_title); g_free (l_hline); if(l_dest < 0) { /* Cancel button: go back to current frame */ l_dest = ainfo_ptr->curr_frame_nr; } if(0 != p_chk_framechange(ainfo_ptr)) { l_dest = -1; } } else { l_dest = nr; } if(l_dest >= 0) { ainfo_ptr->frame_nr = l_dest; rc = p_replace_image(ainfo_ptr); } } p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_goto */ /* ============================================================================ * gap_del * ============================================================================ */ int gap_del(GimpRunModeType run_mode, gint32 image_id, int nr) { int rc; t_anim_info *ainfo_ptr; long l_cnt; long l_max; gchar *l_hline; gchar *l_title; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { if (0 == p_dir_ainfo(ainfo_ptr)) { if(0 != p_chk_framerange(ainfo_ptr)) return -1; if(run_mode == GIMP_RUN_INTERACTIVE) { l_title = g_strdup_printf (_("Delete Frames (%ld/%ld)") , ainfo_ptr->curr_frame_nr , ainfo_ptr->frame_cnt); l_hline = g_strdup_printf (_("Delete Frames from %ld to (number)") , ainfo_ptr->curr_frame_nr); l_max = ainfo_ptr->last_frame_nr; if(l_max == ainfo_ptr->curr_frame_nr) { /* bugfix: the slider needs a maximum > minimum * (if not an error is printed, and * a default range 0 to 100 is displayed) */ l_max++; } l_cnt = p_slider_dialog(l_title, l_hline, _("Number:"), NULL , ainfo_ptr->curr_frame_nr , l_max , ainfo_ptr->curr_frame_nr , TRUE); g_free (l_title); g_free (l_hline); if(l_cnt >= 0) { l_cnt = 1 + l_cnt - ainfo_ptr->curr_frame_nr; } if(0 != p_chk_framechange(ainfo_ptr)) { l_cnt = -1; } } else { l_cnt = nr; } if(l_cnt >= 0) { /* delete l_cnt number of frames (-1 CANCEL button) */ rc = p_del(ainfo_ptr, l_cnt); } } p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_del */ /* ============================================================================ * p_dup_dialog * * ============================================================================ */ int p_dup_dialog(t_anim_info *ainfo_ptr, long *range_from, long *range_to) { static t_arr_arg argv[3]; gchar *l_title; l_title = g_strdup_printf (_("Duplicate Frames (%ld/%ld)") , ainfo_ptr->curr_frame_nr , ainfo_ptr->frame_cnt); p_init_arr_arg(&argv[0], WGT_INT_PAIR); argv[0].label_txt = _("From:"); argv[0].constraint = TRUE; argv[0].int_min = (gint)ainfo_ptr->first_frame_nr; argv[0].int_max = (gint)ainfo_ptr->last_frame_nr; argv[0].int_ret = (gint)ainfo_ptr->curr_frame_nr; argv[0].help_txt = _("Source Range starts at this framenumber"); p_init_arr_arg(&argv[1], WGT_INT_PAIR); argv[1].label_txt = _("To:"); argv[1].constraint = TRUE; argv[1].int_min = (gint)ainfo_ptr->first_frame_nr; argv[1].int_max = (gint)ainfo_ptr->last_frame_nr; argv[1].int_ret = (gint)ainfo_ptr->curr_frame_nr; argv[1].help_txt = _("Source Range ends at this framenumber"); p_init_arr_arg(&argv[2], WGT_INT_PAIR); argv[2].label_txt = _("N times:"); argv[2].constraint = FALSE; argv[2].int_min = 1; argv[2].int_max = 99; argv[2].int_ret = 1; argv[2].umin = 1; argv[2].umax = 9999; argv[2].help_txt = _("Copy selected Range n-times \n(you may type in Values > 99)"); if(TRUE == p_array_dialog(l_title, _("Duplicate Frame Range"), 3, argv)) { g_free (l_title); *range_from = (long)(argv[0].int_ret); *range_to = (long)(argv[1].int_ret); return (int)(argv[2].int_ret); } else { g_free (l_title); return -1; } } /* end p_dup_dialog */ /* ============================================================================ * gap_dup * ============================================================================ */ int gap_dup(GimpRunModeType run_mode, gint32 image_id, int nr, long range_from, long range_to) { int rc; t_anim_info *ainfo_ptr; long l_cnt, l_from, l_to; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { if (0 == p_dir_ainfo(ainfo_ptr)) { if(run_mode == GIMP_RUN_INTERACTIVE) { if(0 != p_chk_framechange(ainfo_ptr)) { l_cnt = -1; } else { if(*ainfo_ptr->extension == '\0' && ainfo_ptr->frame_cnt == 0) { /* duplicate was called on a frame without extension and without framenumer in its name * (typical for new created images named like 'Untitled' (or 'Unbenannt' for german GUI or .. in other languages) */ p_msg_win(ainfo_ptr->run_mode, _("OPERATION CANCELLED.\n" "GAP plug-ins only work with filenames\n" "that end in numbers like _0001.xcf.\n" "==> Rename your image, then try again.")); return -1; } l_cnt = p_dup_dialog(ainfo_ptr, &l_from, &l_to); } if((0 != p_chk_framechange(ainfo_ptr)) || (l_cnt < 1)) { l_cnt = -1; } } else { l_cnt = nr; l_from = range_from; l_to = range_to; } if(l_cnt > 0) { /* make l_cnt duplicate frames (on disk) */ rc = p_dup(ainfo_ptr, l_cnt, l_from, l_to); } } p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_dup */ /* ============================================================================ * gap_exchg * ============================================================================ */ int gap_exchg(GimpRunModeType run_mode, gint32 image_id, int nr) { int rc; t_anim_info *ainfo_ptr; long l_dest; long l_initial; gchar *l_title; rc = -1; l_initial = 1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { if (0 == p_dir_ainfo(ainfo_ptr)) { if(0 != p_chk_framerange(ainfo_ptr)) return -1; if(run_mode == GIMP_RUN_INTERACTIVE) { if(ainfo_ptr->curr_frame_nr < ainfo_ptr->last_frame_nr) { l_initial = ainfo_ptr->curr_frame_nr + 1; } else { l_initial = ainfo_ptr->last_frame_nr; } l_title = g_strdup_printf (_("Exchange current Frame (%ld)") , ainfo_ptr->curr_frame_nr); l_dest = p_slider_dialog(l_title, _("With Frame (number)"), _("Number:"), NULL , ainfo_ptr->first_frame_nr , ainfo_ptr->last_frame_nr , l_initial , TRUE); g_free (l_title); if(0 != p_chk_framechange(ainfo_ptr)) { l_dest = -1; } } else { l_dest = nr; } if((l_dest >= ainfo_ptr->first_frame_nr ) && (l_dest <= ainfo_ptr->last_frame_nr )) { /* excange current frames with destination frame (on disk) */ rc = p_exchg(ainfo_ptr, l_dest); } } p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_exchg */ /* ============================================================================ * p_shift_dialog * * ============================================================================ */ int p_shift_dialog(t_anim_info *ainfo_ptr, long *range_from, long *range_to) { static t_arr_arg argv[3]; gchar *l_title; l_title = g_strdup_printf (_("Framesequence Shift (%ld/%ld)") , ainfo_ptr->curr_frame_nr , ainfo_ptr->frame_cnt); p_init_arr_arg(&argv[0], WGT_INT_PAIR); argv[0].label_txt = _("From:"); argv[0].constraint = TRUE; argv[0].int_min = (gint)ainfo_ptr->first_frame_nr; argv[0].int_max = (gint)ainfo_ptr->last_frame_nr; argv[0].int_ret = (gint)ainfo_ptr->curr_frame_nr; argv[0].help_txt = _("Affected Range starts at this framenumber"); p_init_arr_arg(&argv[1], WGT_INT_PAIR); argv[1].label_txt = _("To:"); argv[1].constraint = TRUE; argv[1].int_min = (gint)ainfo_ptr->first_frame_nr; argv[1].int_max = (gint)ainfo_ptr->last_frame_nr; argv[1].int_ret = (gint)ainfo_ptr->last_frame_nr; argv[1].help_txt = _("Affected Range ends at this framenumber"); p_init_arr_arg(&argv[2], WGT_INT_PAIR); argv[2].label_txt = _("N-Shift:"); argv[2].constraint = TRUE; argv[2].int_min = -1 * (gint)ainfo_ptr->last_frame_nr; argv[2].int_max = (gint)ainfo_ptr->last_frame_nr; argv[2].int_ret = 1; argv[2].help_txt = _("Renumber the affected framesequence \n(numbers are shifted in circle by N)"); if(TRUE == p_array_dialog(l_title, _("Framesequence shift"), 3, argv)) { g_free (l_title); *range_from = (long)(argv[0].int_ret); *range_to = (long)(argv[1].int_ret); return (int)(argv[2].int_ret); } else { g_free (l_title); return 0; } } /* end p_shift_dialog */ /* ============================================================================ * gap_shift * ============================================================================ */ int gap_shift(GimpRunModeType run_mode, gint32 image_id, int nr, long range_from, long range_to) { int rc; t_anim_info *ainfo_ptr; long l_cnt, l_from, l_to; rc = -1; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr != NULL) { if (0 == p_dir_ainfo(ainfo_ptr)) { if(run_mode == GIMP_RUN_INTERACTIVE) { l_cnt = 1; if(0 != p_chk_framechange(ainfo_ptr)) { l_cnt = 0; } else { l_cnt = p_shift_dialog(ainfo_ptr, &l_from, &l_to); } if((0 != p_chk_framechange(ainfo_ptr)) || (l_cnt == 0)) { l_cnt = 0; } } else { l_cnt = nr; l_from = range_from; l_to = range_to; } if(l_cnt != 0) { /* shift framesquence by l_cnt frames * (rename all frames in the given range on disk) */ rc = p_shift(ainfo_ptr, l_cnt, l_from, l_to); } } p_free_ainfo(&ainfo_ptr); } return(rc); } /* end gap_shift */ /* ============================================================================ * gap_video_paste Buffer procedures * ============================================================================ */ gchar * p_get_video_paste_basename(void) { gchar *l_basename; l_basename = p_gimp_gimprc_query("video-paste-basename"); if(l_basename == NULL) { l_basename = g_strdup("gap_video_pastebuffer_"); } return(l_basename); } gchar * p_get_video_paste_dir(void) { gchar *l_dir; gint l_len; l_dir = p_gimp_gimprc_query("video-paste-dir"); if(l_dir == NULL) { l_dir = g_strdup("/tmp"); } /* if dir is configured with trailing dir seprator slash * then cut it off */ l_len = strlen(l_dir); if((l_dir[l_len -1] == G_DIR_SEPARATOR) && (l_len > 1)) { l_dir[l_len -1] = '\0'; } return(l_dir); } gchar * p_get_video_paste_name(void) { gchar *l_dir; gchar *l_basename; gchar *l_video_name; gchar *l_dir_thumb; l_dir = p_get_video_paste_dir(); l_basename = p_get_video_paste_basename(); l_video_name = g_strdup_printf("%s%s%s", l_dir, G_DIR_SEPARATOR_S, l_basename); l_dir_thumb = g_strdup_printf("%s%s%s", l_dir, G_DIR_SEPARATOR_S, ".xvpics"); mkdir (l_dir_thumb, 0755); g_free(l_dir); g_free(l_basename); g_free(l_dir_thumb); if(gap_debug) printf("p_get_video_paste_name: %s\n", l_video_name); return(l_video_name); } static gint32 p_clear_or_count_video_paste(gint delete_flag) { gchar *l_dir; gchar *l_basename; gchar *l_filename; gchar *l_fname_thumbnail; gint l_len; gint32 l_framecount; DIR *l_dirp; struct dirent *l_dp; l_dir = p_get_video_paste_dir(); l_dirp = opendir(l_dir); l_framecount = 0; if(!l_dirp) { printf("ERROR p_vid_edit_clear: can't read directory %s\n", l_dir); l_framecount = -1; } else { l_basename = p_get_video_paste_basename(); l_len = strlen(l_basename); while ( (l_dp = readdir( l_dirp )) != NULL ) { if(strncmp(l_basename, l_dp->d_name, l_len) == 0) { l_filename = g_strdup_printf("%s%s%s", l_dir, G_DIR_SEPARATOR_S, l_dp->d_name); if(1 == p_file_exists(l_filename)) /* check for regular file */ { /* delete all files in the video paste directory * with names matching the basename part */ l_framecount++; if(delete_flag) { if(gap_debug) printf("p_vid_edit_clear: remove file %s\n", l_filename); remove(l_filename); /* also delete thumbnail */ l_fname_thumbnail = p_alloc_fname_thumbnail(l_filename); remove(l_fname_thumbnail); g_free(l_fname_thumbnail); } } g_free(l_filename); } } closedir( l_dirp ); g_free(l_basename); } g_free(l_dir); return(l_framecount); } gint32 p_vid_edit_clear(void) { return(p_clear_or_count_video_paste(TRUE)); /* delete frames */ } gint32 p_vid_edit_framecount() { return (p_clear_or_count_video_paste(FALSE)); /* delete_flag is off, just count frames */ } /* ============================================================================ * gap_vid_edit_copy * ============================================================================ */ gint gap_vid_edit_copy(GimpRunModeType run_mode, gint32 image_id, long range_from, long range_to) { int rc; t_anim_info *ainfo_ptr; gchar *l_curr_name; gchar *l_fname ; gchar *l_fname_copy; gchar *l_basename; gint32 l_frame_nr; gint32 l_cnt_range; gint32 l_cnt2; gint32 l_idx; gint32 l_tmp_image_id; ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr == NULL) { return (-1); } rc = 0; if((ainfo_ptr->curr_frame_nr >= MIN(range_to, range_from)) && (ainfo_ptr->curr_frame_nr <= MAX(range_to, range_from))) { /* current frame is in the affected range * so we have to save current frame to file */ l_curr_name = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); p_save_named_frame(ainfo_ptr->image_id, l_curr_name); g_free(l_curr_name); } l_basename = p_get_video_paste_name(); l_cnt2 = p_vid_edit_framecount(); /* count frames in the video paste buffer */ l_frame_nr = 1 + l_cnt2; /* start at one, or continue (append) at end +1 */ l_cnt_range = 1 + MAX(range_to, range_from) - MIN(range_to, range_from); for(l_idx = 0; l_idx < l_cnt_range; l_idx++) { if(rc < 0) { break; } l_fname = p_alloc_fname(ainfo_ptr->basename, MIN(range_to, range_from) + l_idx, ainfo_ptr->extension); l_fname_copy = g_strdup_printf("%s%04ld.xcf", l_basename, (long)l_frame_nr); if(strcmp(ainfo_ptr->extension, ".xcf") == 0) { rc = p_image_file_copy(l_fname, l_fname_copy); } else { /* convert other fileformats to xcf before saving to video paste buffer */ l_tmp_image_id = p_load_image(l_fname); rc = p_save_named_frame(l_tmp_image_id, l_fname_copy); gimp_image_delete(l_tmp_image_id); } g_free(l_fname); g_free(l_fname_copy); l_frame_nr++; } p_free_ainfo(&ainfo_ptr); return(rc); } /* end gap_vid_edit_copy */ /* ============================================================================ * p_custom_palette_file * write a gimp palette file * ============================================================================ */ static gint p_custom_palette_file(char *filename, guchar *rgb, gint count) { FILE *l_fp; l_fp= fopen(filename, "w"); if (l_fp == NULL) { return -1; } fprintf(l_fp, "GIMP Palette\n"); fprintf(l_fp, "# this file will be overwritten each time when video frames are converted to INDEXED\n"); while (count > 0) { fprintf(l_fp, "%d %d %d\tUnknown\n", rgb[0], rgb[1], rgb[2]); rgb+= 3; --count; } fclose (l_fp); return 0; } /* end p_custom_palette_file */ /* ============================================================================ * gap_vid_edit_paste * ============================================================================ */ gint gap_vid_edit_paste(GimpRunModeType run_mode, gint32 image_id, long paste_mode) { #define CUSTOM_PALETTE_NAME "gap_cmap" int rc; t_anim_info *ainfo_ptr; gchar *l_curr_name; gchar *l_fname ; gchar *l_fname_copy; gchar *l_basename; gint32 l_frame_nr; gint32 l_dst_frame_nr; gint32 l_cnt2; gint32 l_lo, l_hi; gint32 l_insert_frame_nr; gint32 l_tmp_image_id; gint l_rc; GimpParam *l_params; gint l_retvals; GimpImageBaseType l_orig_basetype; l_cnt2 = p_vid_edit_framecount(); if(gap_debug) { printf("gap_vid_edit_paste: paste_mode %d found %d frames to paste\n" , (int)paste_mode, (int)l_cnt2); } if (l_cnt2 < 1) { return(0); /* video paste buffer is empty */ } ainfo_ptr = p_alloc_ainfo(image_id, run_mode); if(ainfo_ptr == NULL) { return (-1); } if (0 != p_dir_ainfo(ainfo_ptr)) { return (-1); } rc = 0; l_insert_frame_nr = ainfo_ptr->curr_frame_nr; if(paste_mode != VID_PASTE_INSERT_AFTER) { /* we have to save current frame to file */ l_curr_name = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); p_save_named_frame(ainfo_ptr->image_id, l_curr_name); g_free(l_curr_name); } if(paste_mode != VID_PASTE_REPLACE) { if(paste_mode == VID_PASTE_INSERT_AFTER) { l_insert_frame_nr = ainfo_ptr->curr_frame_nr +1; } /* rename (renumber) all frames with number greater (or greater equal) than current */ l_lo = ainfo_ptr->last_frame_nr; l_hi = l_lo + l_cnt2; if(gap_debug) { printf("gap_vid_edit_paste: l_insert_frame_nr %d l_lo:%d l_hi:%d\n" , (int)l_insert_frame_nr, (int)l_lo, (int)l_hi); } while(l_lo >= l_insert_frame_nr) { if(0 != p_rename_frame(ainfo_ptr, l_lo, l_hi)) { gchar *tmp_errtxt; tmp_errtxt = g_strdup_printf(_("Error: could not rename frame %ld to %ld"), (long int)l_lo, (long int)l_hi); p_msg_win(ainfo_ptr->run_mode, tmp_errtxt); g_free(tmp_errtxt); return -1; } l_lo--; l_hi--; } } l_basename = p_get_video_paste_name(); l_dst_frame_nr = l_insert_frame_nr; for(l_frame_nr = 1; l_frame_nr <= l_cnt2; l_frame_nr++) { l_fname = p_alloc_fname(ainfo_ptr->basename, l_dst_frame_nr, ainfo_ptr->extension); l_fname_copy = g_strdup_printf("%s%04ld.xcf", l_basename, (long)l_frame_nr); l_tmp_image_id = p_load_image(l_fname_copy); /* check size and resize if needed */ if((gimp_image_width(l_tmp_image_id) != gimp_image_width(image_id)) || (gimp_image_height(l_tmp_image_id) != gimp_image_height(image_id))) { GimpParam *l_params; gint l_retvals; gint32 l_size_x, l_size_y; l_size_x = gimp_image_width(image_id); l_size_y = gimp_image_height(image_id); if(gap_debug) printf("DEBUG: scale to size %d %d\n", (int)l_size_x, (int)l_size_y); l_params = gimp_run_procedure ("gimp_image_scale", &l_retvals, GIMP_PDB_IMAGE, l_tmp_image_id, GIMP_PDB_INT32, l_size_x, GIMP_PDB_INT32, l_size_y, GIMP_PDB_END); } /* check basetype and convert if needed */ l_orig_basetype = gimp_image_base_type(image_id); if(gimp_image_base_type(l_tmp_image_id) != l_orig_basetype) { switch(l_orig_basetype) { gchar *l_palette_filename; gchar *l_gimp_dir; guchar *l_cmap; gint l_ncolors; /* convert tmp image to dest type */ case GIMP_INDEXED: l_cmap = gimp_image_get_cmap(image_id, &l_ncolors); if(gap_debug) printf("DEBUG: convert to INDEXED %d colors\n", (int)l_ncolors); l_params = gimp_run_procedure ("gimp_gimprc_query", &l_retvals, GIMP_PDB_STRING, "gimp_dir", GIMP_PDB_END); l_gimp_dir = g_strdup(l_params[1].data.d_string); gimp_destroy_params(l_params, l_retvals); l_palette_filename = g_strdup_printf("%s%spalettes%s%s" , l_gimp_dir , G_DIR_SEPARATOR_S , G_DIR_SEPARATOR_S , CUSTOM_PALETTE_NAME); l_rc = p_custom_palette_file(l_palette_filename, l_cmap, l_ncolors); if(l_rc == 0) { l_params = gimp_run_procedure ("gimp_palette_refresh", &l_retvals, GIMP_PDB_END); gimp_destroy_params(l_params, l_retvals); l_params = gimp_run_procedure ("gimp_convert_indexed", &l_retvals, GIMP_PDB_IMAGE, l_tmp_image_id, GIMP_PDB_INT32, 1, /* dither value 1== floyd-steinberg */ GIMP_PDB_INT32, 4, /* palette_type 4 == CUSTOM_PALETTE */ GIMP_PDB_INT32, l_ncolors, /* number of colors */ GIMP_PDB_INT32, 0, /* alpha_dither */ GIMP_PDB_INT32, 0, /* remove_unused */ GIMP_PDB_STRING, CUSTOM_PALETTE_NAME, /* name of the custom palette */ GIMP_PDB_END); gimp_destroy_params(l_params, l_retvals); } else { printf("ERROR: gap_vid_edit_paste: could not save custom palette %s\n", l_palette_filename); } g_free(l_cmap); g_free(l_palette_filename); g_free(l_gimp_dir); break; case GIMP_GRAY: if(gap_debug) printf("DEBUG: convert to GRAY'\n"); l_params = gimp_run_procedure ("gimp_convert_grayscale", &l_retvals, GIMP_PDB_IMAGE, l_tmp_image_id, GIMP_PDB_END); gimp_destroy_params(l_params, l_retvals); break; case GIMP_RGB: if(gap_debug) printf("DEBUG: convert to RGB'\n"); l_params = gimp_run_procedure ("gimp_convert_rgb", &l_retvals, GIMP_PDB_IMAGE, l_tmp_image_id, GIMP_PDB_END); gimp_destroy_params(l_params, l_retvals); break; default: printf( "DEBUG: unknown image type\n"); return -1; break; } } rc = p_save_named_frame(l_tmp_image_id, l_fname); gimp_image_delete(l_tmp_image_id); g_free(l_fname); g_free(l_fname_copy); l_dst_frame_nr++; } if((rc >= 0) && (paste_mode != VID_PASTE_INSERT_AFTER)) { /* load from the "new" current frame */ if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename); ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->curr_frame_nr, ainfo_ptr->extension); rc = p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->new_filename); } p_free_ainfo(&ainfo_ptr); return(rc); } /* end gap_vid_edit_paste */ gint32 p_getpid(void) { #ifndef G_OS_WIN32 /* for UNIX */ return ((gint32)getpid()); #else /* hof: dont know how to getpid on windows */ return 0; #endif } gint p_pid_is_alive(gint32 pid) { #ifndef G_OS_WIN32 /* for UNIX */ /* kill with signal 0 checks only if the process is alive (no signal is sent) * returns 0 if alive, 1 if no process with given pid found. */ if (0 == kill(pid, 0)) { return(TRUE); } return (FALSE); #else /* hof: dont know how to check on Windows * assume that process is always alive * (therefore on Windows locks will not be cleared * automatically after crashes of the locking process) */ return(TRUE); #endif } static void p_gap_lock_build_lockkey(t_gap_lockdata *lock, gint32 image_id) { g_snprintf(lock->key, sizeof(lock->key), "plug_in_gap_plugins_LOCK_%d", (int)image_id); } gint p_gap_lock_is_locked(gint32 image_id, GimpRunModeType run_mode) { gint32 l_pid; t_gap_lockdata l_lock; /* check for locks */ l_pid = p_getpid(); l_lock.lock = 0; p_gap_lock_build_lockkey(&l_lock, image_id); gimp_get_data (l_lock.key, &l_lock); if((l_lock.lock != 0) && (l_lock.image_id == image_id)) { if(p_pid_is_alive(l_lock.pid)) { if(run_mode == GIMP_RUN_INTERACTIVE) { gchar *l_lockmsg; l_lockmsg = g_strdup_printf(_("Can't execute more than 1 Video Function\n" "on the same AnimFrame Image at the same time\n" "LOCK ID:%s\n") , l_lock.key); gimp_message(l_lockmsg); g_free(l_lockmsg); } printf("GAP plug-in is LOCKED ID:%s PID:%d\n", l_lock.key, (int)l_lock.pid); return(TRUE); } } return(FALSE); } void p_gap_lock_set(gint32 image_id) { t_gap_lockdata l_lock; p_gap_lock_build_lockkey(&l_lock, image_id); /* set LOCK on image (for all gap_plugins) */ l_lock.lock = 1; l_lock.image_id = image_id; l_lock.pid = p_getpid(); gimp_set_data (l_lock.key, &l_lock, sizeof(l_lock)); } void p_gap_lock_remove(gint32 image_id) { t_gap_lockdata l_lock; p_gap_lock_build_lockkey(&l_lock, image_id); /* remove LOCK on this image for all gap_plugins */ l_lock.lock = 0; l_lock.image_id = -1; gimp_set_data (l_lock.key, &l_lock, sizeof(l_lock)); }