diff --git a/plug-ins/gap/.cvsignore b/plug-ins/gap/.cvsignore new file mode 100644 index 0000000000..61cea24090 --- /dev/null +++ b/plug-ins/gap/.cvsignore @@ -0,0 +1,7 @@ +Makefile.in +Makefile +.deps +_libs +.libs +gap_filter +gap_plugins diff --git a/plug-ins/gap/Makefile.am b/plug-ins/gap/Makefile.am new file mode 100644 index 0000000000..906c423f26 --- /dev/null +++ b/plug-ins/gap/Makefile.am @@ -0,0 +1,90 @@ +## Process this file with automake to produce Makefile.in + +pluginlibdir = $(gimpplugindir)/plug-ins + +EXTRA_DIST = README README_developers TESTPROT_iter_ALT + +pluginlib_PROGRAMS = gap_plugins gap_filter + +gap_plugins_SOURCES = \ + gap_main.c \ + gap_mov_exec.c \ + gap_mov_exec.h \ + gap_range_ops.c \ + gap_range_ops.h \ + gap_arr_dialog.c \ + gap_arr_dialog.h \ + gap_mov_dialog.c \ + gap_mov_dialog.h \ + gap_layer_copy.c \ + gap_layer_copy.h \ + gap_split.c \ + gap_split.h \ + gap_resi_dialog.c \ + gap_resi_dialog.h \ + gap_mpege.c \ + gap_mpege.h \ + gap_mod_layer.c \ + gap_mod_layer.h \ + gap_filter_pdb.c \ + gap_filter_pdb.h \ + gap_filter_codegen.c \ + gap_dbbrowser_utils.c \ + gap_dbbrowser_utils.h \ + gap_match.c \ + gap_match.h \ + gap_lib.c \ + gap_lib.h \ + gap_exchange_image.c \ + gap_exchange_image.h \ + gap_pdb_calls.c \ + gap_pdb_calls.h \ + resize.c + + +gap_filter_SOURCES = \ + gap_filter_main.c \ + gap_dbbrowser_utils.c \ + gap_dbbrowser_utils.h \ + gap_filter_foreach.c \ + gap_filter_iterators.c \ + gap_filter_iterators.h \ + gap_filter_pdb.c \ + gap_filter_pdb.h \ + gap_filter_codegen.c \ + gap_arr_dialog.c \ + gap_arr_dialog.h \ + gap_pdb_calls.c \ + gap_pdb_calls.h \ + gap_filter.h + + +INCLUDES = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la \ + $(GTK_LIBS) + +DEPS = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la + +gap_plugins_DEPENDENCIES = $(DEPS) + +gap_filter_DEPENDENCIES = $(DEPS) + +files: + @files=`ls $(DISTFILES) 2> /dev/null`; for p in $$files; do \ + echo $$p; \ + done + @for subdir in $(SUBDIRS); do \ + files=`cd $$subdir; $(MAKE) files | grep -v "make\[[1-9]\]"`; \ + for file in $$files; do \ + echo $$subdir/$$file; \ + done; \ + done + diff --git a/plug-ins/gap/README b/plug-ins/gap/README new file mode 100644 index 0000000000..05c21ddc55 --- /dev/null +++ b/plug-ins/gap/README @@ -0,0 +1,1030 @@ +Project gap "Gimp Animation Package" 17. Mar 1999 release 0.99.00 + +-------------------------------------------------------------------- +Introduction +-------------------------------------------------------------------- + +The GIMP (1.0.2) is a great Program for creating and manipulating +Pixelbased Images of many types. The Plugin Concept and the Procedural Database +allows Programmers to extend the Gimp's Functions in many ways. + +For now there are some Plugins that supports Animation Features, +based on Layers. Each Layer of the Image is considered as one frame +of the Animation. +GAP is a collection of Plugins that extends the GIMP's animation capabilities +by supporting the creation of more complex animations. +(Additional Informations about the GAP can be found in the GimpUserManual 1.0.0 + Chapter Advanced Animation) + +Idea: + With some little changes to the gimp-core and a few new plug-ins + gimp can operate on a series of images as if they were + a single one. + Each frame of an Animation can have multiple Layers. + The user can step from frame to frame by pressing an Accelerator Key. + + Layeranimated Images can be combined with frames (multiple images) + as I have done in "Move Path" Plugin (see below). + +Concept: + An animation consists of a series of Images of the same size & Type + (Frames). + In my Concept each Frame is stored seperate on Disk, using a filename + convention that includes the frame Number and extension. + + Example: film_0001.xcf, + film_0002.xcf + .. + film_0012.xcf + + gimp's xcf Fileformat should be used to store the frames, + so that each frame can have more Layers. + (Notes: Sound is not supported in this Concept, + Playbackrates have to be added when save as animation format) + + This requires a lot of Diskspace but offers much more flexibility + while working on the animation. + + To save diskspace you may optional use gziped xcf frames + (takes extra time to un/compress) by using the extension .xcfgz + (gzip has to be installed on your system to do that) + If you can accept lossy compression you may also use + the xjt fileformat to store your frames on jpeg based compression. + (xjt load and save plugins are available in the Plugin Registry) + + The final Product can be converted to one multilayerd Image + that can be saved as Animated-Gif. + (or, in the future in other Animation Formats + when other Load/Save Modules were added to the gimp) + + GAP provides frontend dialog interfaces for + 2 different software MPEG encoder programs. + (mpeg_encode and mpeg2encode) + + GAP also provides a way to load AnimFrames from multimedia fileformats + (avi, quicktime, ....) based on the famous xanim multimedia player. + see the file README_xanim_hack for more details. + + +-------------------------------------------------------------------- +Installation +-------------------------------------------------------------------- + +This GAP-release 0.99.00 runs stable under GIMP 1.0.2 when +some Patches are made to the GIMP-core. + +Most of the GAP 0.99.00 Functions are now changed to run +on the latest Development release of GIMP 1.1.3 / gtk 1.2 too. + + 1. copy the tar file to gimp's plugin directory /plug-ins + then: + cd /plug-ins + + 2. extract the tar file (should expand to a directory named gap + 2.a + # if you have gnu tar: + tar -xzvf gap.tgz + 2.b + # if you dont have gnu tar: + # gunzip, then extract + gzip -cfd gap.tar + tar -xvf gap.tar + + 3 # IF you have GIMP 1.1 (latest) + # No Patches to the GIMP 1.1 core are needed ! + # (goto Step 4.) + # + # IF you have GIMP 1.0.2 (or older GIMP releases) + # check if your gimp version is supported by + # the ./apply_patch.sh script + # + # to do this, read the comments within this script + + 3.1 + # check if your gimp version is supported by + # the ./apply_patch.sh script + + + 3.2 # Apply the patch to gimp core and install it + # check if your gimp version is supported by + # the ./apply_patch.sh script + # + # to do this, read the comments within this script + + cd /plug-ins/gap + ./apply_patch.sh 1.0.0 # specify your gimp version here + cd ../../app + make install + + 4. Compile GAP Plugins and install it + + cd $GIMP/plug-ins/gap + + # for GIMP 1.0.2 (and older) + cp Makefile.1.0 Makefile + + # for GIMP 1.1 + cp Makefile.1.1 Makefile + + # Edit the Makefile to fit your configuration + + make + make install + + 5. Install Script-Fu Scripts + # Just copy *.scm to /usr/local/share/gimp/scripts + # (or to $HOME/.gimp/scripts ) + cd $GIMP/plug-ins/gap + cp *.scm /usr/local/share/gimp/scripts + + + Notes: + - GAP provides frontend dialog for mpeg_encode (V1.5R2) + and mpeg2encode (V1.2) + If you like to use that stuff, you should install + mpeg_encode and mpeg_play + mpeg2encode and mpeg2decode + on your system. + + (There is no need to install the mpeg encoders to compile GAP) + + - GAP-Patch support for some older gimp releases + is available at the Registry: + + http://registry.gimp.org/detailview.phtml?plugin=gap_patches + + + - See the File README_xanim_hack for further optional Installation + + + +-------------------------------------------------------------------- +Change Log +-------------------------------------------------------------------- +- 0.99.00 - Move Path bugfix in the dialog + (update preview did'nt work with gimp 1.1.2 and gtk 1.1) + - Animated Filtercalls: + added a 2.nd Set of Iterators (iter_ALT Procedures) for + Plugins of GIMP 1.1.3 that is compiled with Makefile 1.1 + (Makefile 1.0 still uses Iterators for the old 1.0 PDB-Interfaces) + - bugfixes in iterator code generator. + - GAP now uses gimp standard procedures to copy layers and channels. + - prepared filename-handling for win/dos conventions. +- 0.98.02 - GAP_DB_BROWSER (1.1 variant) show help like the dbbrowser in gimp-1.1.1 +- 0.98.01 - GAP_DB_BROWSER variants for GIMP 1.0 and GIMP 1.1 + new e-mail adress + xanim-hack +- 0.98.00 - Sarted porting GAP to GIMP 1.1 + Changed internal interfaces to work with + both GIMP 1.0.2 and GIMP 1.1 PDB. + + MovePath: The Layermask of imported Source Layerobjects + is no longer ignored. +- 0.97.01 - Added Patches for the XJT Load/Save filters + (XJT support for gimp 1.0.0 upto gimp 1.0.2) +- 0.97.00 - Patches updated to work with gimp 1.0.2 + - New Plugin 'Frames Modify' + - Bugfix in patchcode layer_cmds.c + (get/set linked state didnt work since gimp 0.99.18) +- 0.96.04 - Patches updated to work with gimp 1.0.1 +- 0.96.03 - Extended Pitstop-Dialog in 'Filter All layers' + (now you can specify a backup file to store the + Image after each non-interactive filtercall, + futher you may skip the filtercall. + These extensions were done because the MapObject + Plugin crashes sometimes + (after 3 hours calculating time on a PII 300Mhz processor) + - Extended Parameters for non-interactive call of + plug_in_gap_range_to_multilayer and + plug_in_gap_split + (the image_id of the created image is now returned) +- 0.96.02 - Added Clip To Image Option and Tooltips in MovePath Plugin. + _ Added Plugin 'Framesequence shift' + - Exended 'Frames To Image' Plugin + (now you can set framerate and framename + for the generated layers) + - Extended 'Duplicate Frames' Plugin + (now you can use a framerange as source, + not just the current frame) +- 0.96.01 - Bugfix (refresh problems when GAP's array dialog was used twice + in one plugin -- because of double call to gtk_init) +- 0.96.00 - Added AnimFrames scale, resize and crop + - Added 'Split Image to Frames" (plug_in_gap_split) + - Added 'Frames MPEG_encode' plug_in_gap_mpeg_encode + (Requires free software 'mpeg_encode' to run) + - Added 'Frames MPEG2_encode' plug_in_gap_mpeg2encode + (Requires software 'mpeg2encode' to run) + - Most Dialogs changed (some have got tooltips) + (now based on the new gap_arr_dialog module) + - internal changes (header files for all modules) + - bugfix: memory leak in gap_layer_copy.c +- 0.95.04 - Iterator Generation: added support for iteration on PARAM_DRAWABLE type + Updated Iterator_ALT procedures for: + - BumpMap (now you can use animated bumpmaps) + - DepthMerge + - Displace + - Refract +- 0.95.03 - Patches updated to work with gimp 1.0.0 + (Removed patches for gimp 0.99.19 due to sizelimit + in the Plugin registry) +- 0.95.02 - Added Script sel-to-anim-img.scm +- 0.95.01 - Made Patches usable for GIMP release 0.99.31 + (there were no updates needed, just added some links + from 0.99.29 to 0.99.31) +- 0.95.00 - extended MovePath plugin. + Now you can specify an angle for each point of the path + how to rotate the moving object layer. +- 0.94.02 - Patches updated to work with gimp 0.99.29 +- 0.94.01 - Added new features to 'Frames to Image' Plugin: + Select of flatten_modes, and optional exclude of the BG-Layer. +- 0.94.00 - Move Path Plugin uses now 1 Point per default. + (If you want to move an Object, you'll have to) + add Point(s) explicit. + - bugfix: Move Path Plugin initial value for src_paintmode + undefined values sometimes caused 'cant get new layer' Error + and Sourcelayers were not copied to the frames + - Patches updated to work with gimp 0.99.28 + - Updated Foundation adress +- 0.93.06 - Patches updated to work with gimp 0.99.27 + implemented new versions of + gimage:gimage_lower_top_layer + gimage:gimage_lower_bot_layer + (the old versions were slow and failed + on Images with many layers) +- 0.93.05 - Patches updated to work with gimp 0.99.24 + Patches extended: + Added gimp-core functions to layers dialog: + raising/lowering a Layer to Top/Bottom of the layerstack +- 0.93.04 - Patches updated to work with gimp 0.99.22 + Window with Info Message if no Source Image was + selected in MovePath +- 0.93.03 - Bugfix of the Patches updated to work with gimp 0.99.19 + (duplicate_into should now copy channels too) +- 0.93.02 - Patches updated to work with gimp 0.99.19 + +- 0.93.01 - Bugfix + (GAP filters now should work on frames that are not + in the current directory) + +- 0.93.00 - Internal "Gen Code by name" Button + generates additional file _Iterator.c + (see README_Developers for more details) + - The Patches to the gimp core + (needed to run GAP bend, movel, adjust, implayer Plugins) + are now available for gimp releases: + 0.99.16 + 0.99.17 + 0.99.18 + 0.99.19 + the patches are stored in seperate versioned directories, + The apply_patch.sh script now expects the gimp-version + as calling argument. + +- 0.92.00 - "Filter all Layers" Dialog Window after 1.st and 2.nd + Interactive Plugin call. (to give a chance to see + the effects caused by the plugin or to cancel + before proceeding) + - New Patches to the gimp core + "gimp_layer_get_linked" + "gimp_layer_set_linked" + + (for Plugins "adjust", "bend", "movel" + these Plugins are not part of GAP, you can get them + from the Plugin Registry) + + - Internal"Gen Code by name" Button if GAP_DEBUG environment is set. + +- 0.91.01 - "Apply Varying" Button insensitive if animated call + not available. + - Convert Frames Dialog window + now hides Colors and Dither entries + if Conv to INDEXED is not selected. + + +- 0.91.00 - Patches to the gimp core were updated to work with gimp 0.99.16 + (plus: gimage_update_full was added for GAP) + (The Patches do work with gimp 0.99.17 too) + + - "plug_in_gap_layers_run_animfilter" + New Plugin allows animated calls to (more than 50) existing Plugins + +-------------------------------------------------------------------- +How to use +-------------------------------------------------------------------- + +WebTip: My GIMP-page http://pages.hotbot.com/arts/hof/index.html + contains an illustrated GAP-tutorial, some demo-animations + and other plugins. + + The chapter "Advanced Animation" in the Gimp User Manual 1.0 + (by Karin & Olof Kylander) also describes GAP with many + Illustrations. + + + +- Creating multiple frames (AnimFrames) + + - from an existing single image + Save your Image as XCF file. (File->Save as) + use a Name that ends up in _0001.xcf (or _0001.xcfgz) + + Then duplicate your image (AnimFrames->Duplicate) + You'll be asked how much Copies you need. + (Note: all copies are stored on disk immediate + without explicite save) + + - from an existing (layeranimated) multilayer image + use + AnimFrames->Split Img to frames + + This will create Frames, a series of Images on disk, + with a Name that ends up in _0001.xcf). + Optional you may use other extensions. (.xcfgz, .jpg ...) + + WARNING: The extension defines the fileformat + of the Frames. Most of the other Formats + (than GIMP's .xcf Format) can NOT save + multilayer frames or frames with alpha channels. + + - from outside the gimp + You may rename and copy existing XCF Images + according to the frame naming conventions. + _0001.xcf + _0002.xcf + ... + Then load (only one of them) into the GIMP. + +- Navigation (Goto) + It is recommanded to define some Accelerator Keys for quick + walk through the frames. + Here are my settings (excerpt from my $HOME/.gimp/menurc ) + + (menu-path "/AnimFrames/Goto First" "1") + (menu-path "/AnimFrames/Goto Prev" "1") + (menu-path "/AnimFrames/Goto Next" "2") + (menu-path "/AnimFrames/Goto Any" "3") + (menu-path "/AnimFrames/Goto Last" "2") + + +- Playback + playbak of multilayered image is not available. + (it would be very slow) + But you can convert your frames to one Multilayer Image + (AnimFrames->Frames to Image) + And then playback the newly created Image + (Filters->Animation->Playback) + +- Move Path (Make Things Move) + For this Plugin you need a series of frames + and one single image (that may have more layers). + + - The Source Image must be opened in the gimp + - The Source Image must be another Image than the destination frame + (if you really want to copy layers from destination frame + to destination frame(s) you have to duplicate it first) + - The Source Image must be from the same type (RGB, INDEXED ..) as + the destination frame. + + In other words: To run the 'Move Path' Plugin you have to open at + least 2 Images of the same type. + + + + Invoke this Plugin from one of the frames: + (Menu: AnimFrames->Move Path) + + The selected Layer(s) of the Source Image + is (are) copied into the selected range of frames. + Each handled frame recieves exactly one Copy of the selected Layer + from the Source Image. + + If you use other Stepmodes than "None", + the Layers of the SourceImage are stepped through, + and the next handled frame recieves the next + Layer from the Source_images Layerstack. + + The copies of the SourceLayer(s) were modified by transitions + with varying Parameters. + Parameters: + - Position (X/Y) + - Size (Width/Height) + - Opacity + - Rotation (angle from -360 to +360 degrees) + - SourceLayer (depends on Stepmode) + + The Parameters were changed linear from one starting point to + the next point. Per default the move Path has only only 1 Point. + (So the Src-Layers(s) are copied to all frames of the framerange + at constant Position, Size and Opacity) + If you want your Source_layers to move, grow, rotate or to fade (in or out) + you have to add one more points (limited to 256) to define a Path. + + The affected range is selected by Start Frame - End Frame. + Each affected frame recieves exactly one copy of the (current) + Sourcelayer adjusted to the current Prameters. + The Layerstack defines if the pasted copy appears + in the foreground (0 == on top) or below other layers that are + already in the frame. + With the toggle button 'Clip To Frame' the the copied layer + is clipped to the destination Frames Image Width and Height. + + With PreviewFrame you can select the frame Number to display + in the "Mov Path Preview". + You first have to Adjust the PreviewFrame + then press the "UpdPreview" Button. + + Tips: + - with the UpdPreview Button you can blend in the + selected Layer of the Source Image. + If you want to adjust position it may be useful to see + the background. + Therfore you can make the source image + transparent (modify the opacity value) or + put the sourcelayer below the background + (set the Layerstack to higher value) + Then pres UpdPreview Button again. + + - If you let your objects (source layers) rotatate or + change their size, set Handle mode to 'Center'. + (If you use another Handle mode you may get unwanted + moves of your object, caused by resizing) + + - Speed: + Move path alternates the settings linear from + point to point, so things move (or happen) in constant speed + between 2 Points. + + If you want to make accelerated moving Obcets, you'll + have to set more points with growing distances. + + Example: + + [1] [2] [3] [4] [5] [6] + +---+----+-----+------+-------+ + + The affected range has 25 frames, and you have set 6 points + with growing distances in one straight line. + That gives 5 frames (== equal time) for each part of the path, + but each part has another length. This results in different + (growing) speeds for each part of the path. + + +- Convert frames to one multilayered Image. + This can be done with + + (AnimFrames->Frames to Image) + + The selected Source Range of Frames is copied into one + new created multilayered destination Image. + Each frame results in one Layer in the destination Image. + (or nothing if the source frame has no selected layer) + + With Layer Basename you can chosse a name for the resulting + Layers in the destination Image. The string [####] is replaced + by the frame number. + Example: my_layer_[##] results in: my_layer_01, my_layer_02 .... + + Layer Mergemode: + - Extend as necessary Build a destination Layer by merging + selected Layers of one Sourceframe. + The destination Layer's size will be + the outline-rectangle of all selected + Layers. + + - Clipped to image Build a destination Layer by merging + selected Layers of one Sourceframe. + The destination Layer's size will be + the Imagesize. + + - Clipped to bottom layer Build a destination Layer by merging + selected Layers of one Sourceframe. + The destination Layer's size will be + the size of the lowest selected Layer. + + - Flattened image Build destination Layer by flattening + the source_layers copied from one Sourceframe + There will be no transparent parts + in the destination Layers. + The destination Layer's size will be + the Imagesize. + + With the 'Exclude BG-Layer' check_button pressed, + the Backgrund-Layers of all the Sourceframes are excluded + from the copy, regardless if they are selected or not. + Otherwise BG-Layers are handled like all other Layers. + + (If you are using Flatten_mode 'Flatten' the BG-Color will fill all + transparent parts of the resulting destination layer(s).) + + Layer Selection: + ---------------- + Here you can select which layer(s) of a frame is(are) used + to build the destination Layer. + + Select Layer(s): + Pattern is equal to LayerName + Pattern is Start of LayerName + Pattern is End of Layername + Pattern is a Part of LayerName + With these settings you can select Layers + by their Layername. + (All Layers with a layername matching the Select Pattern + are selected). + + Pattern is LayerstackNumber List + Layers are selected by their Layerstackposition, + where 0 is the top layer. + The Pattern is a list of layerstack numbers or + number ranges. (0-3 matches to the upper 4 layers) + Pattern is REVERSE-stack List" + Here you can specify Layerstacknumbers in REVERSE order. + (where 0 is the background Layer) + + All Visible (ignore Pattern) + All visible Layers are selected. + (The Select Pattern is ignored) + + Select Pattern: + String to identify a Layer. It can be a part of the layername + or a List of Layerstacknumbers (like this one: "0, 2-5, 9") + depending on your choice in Select Layer(s) above. + Case Sensitive: + Lowercase and UPPPERCASE letters are considered as equal + if this checkbutton is set to off. + Invert Selection: + Select all unselected Layers. + + + +- Modify Frames + + The GAP-tool 'Frames Modify' provides the feature to perform functions + on one ore more selected Layer(s) in all frames of the selected framerange. + + The Layers can be selected by (parts of) their name, or by their + layerstack numbers. + (for more information on Layer Selection see above). + + Available Functions are: + "0:set_visible, + 1:set_invisible, + 2:set_linked, + 3:set_unlinked, + 4:raise, + 5:lower, + 6:merge_expand, + 7:merge_img, + 8:merge_bg, + 9:apply_filter, + 10:duplicate, + 11:delete, + 12:rename" + + + The function 'apply_filter' brigs up a dialog window that is + similar to the PDB-Browser, where you can select any available + filter. + + If you use the PDB-Browser's button "Apply Varying", the + filterparameters will slightly change in each handled frame + from start to end. + + - If there is more than one selected layer in a frame + each of the selected layers within the same frame + will be processed with the same filterparameter values. + + (for more info se 'Animated calls of Plug-In Filters' below) + + + + It is a good idea to use the same layerstack structure in all + your frames. Another hint is that you assign useful names to + your layers. that should be done consequent for all frames. + + Example 1 (useful Layertack structure/names) + + + film_0001.xcf film_0002.xcf .... film_0010.xcf + --------------------------------------------------------------------- + + layerstack [0] mouse_01 mouse_02 mouse_10 + layerstack [1] cat_01 cat_02 cat_10 + layerstack [2] tree_01 tree_02 tree_10 + layerstack [3] background background background + + + + Example 2 (not recommanded) + + + film_0001.xcf film_0002.xcf .... film_0010.xcf + --------------------------------------------------------------------- + + layerstack [0] tree layer mouse_10 + layerstack [1] bg layer cat_10 + layerstack [2] layer background + layerstack [3] background + + + + +- Saving MPEG files: + + GAP provides only frontend dialogs to call MPEG encoding programs. + You must have installed one of them if you want to save + your finished AnimFrames in MPEG Video Fileformat. + + The MPEG-encoders are: + + 1) mpeg_encode 1.5 + freely distributed Berkeley MPEG-1 Video Encoder + ftp://mm-ftp.cs.berkeley.edu/pub/multimedia/mpeg/bmt1r1.tar.gz + + (Can read AnimFrame in the Fileformats: + .yuv + .ppm + .pnm + .jpg + ) + (can call filterprograms to convert from other fileformats) + + 2) mpeg2encode 1.2 + MPEG-2 and MPEG-1 Encoder / Decoder, Version 1.2 + (MPEG Software Simulation Group) + + Web: http://www.mpeg.org/MSSG + FTP: ftp://ftp.mpeg.org/pub/mpeg/mssg + E-mail: mssg@mpeg.org (author contact) + + (Can read AnimFrame in the Fileformats: + .yuv + .ppm + ) + + To Prepare for MPEG encoding: + + - Use 'Frames Convert' from the AnimFrames Menu + to convert your AnimFrames to .ppm + (or another Fileformat that can be read directly by + your encoder) + + - If you have a Layerstack-Animated Multilayer Image + You can use 'Split Img to Frames' from the AnimFrames Menu. + + - If you are using mpeg_encode and + height or width are not multiples of 16: + Use 'Frames Scale' or 'Frames Crop' from the AnimFrames Menu + on the newly converted/created AnimFrames. + + - Then use 'Frames MPEG2_encode' or 'Frames MPEG_encode' + from the AnimFrames Menu. + (Invoked from one of the prepared AnimFrames) + + Both encoders offer a lot of Parameter Settings for the experienced + User. The Parameters are passed to the encoder in a Parameterfile. + The frontend dialog shows only the most important Parameters, + but generates a documented Parameterfile with default values + for the other Parameters. + You can generate the Parameterfile with or without starting + the encoder. + (You may start the encoder later by executing the Startscript + from a Unixshell. The Startscript is generated by the frontend dialog) + + For more information on the Parameters please refer to the encoders + documentations. + + +- Tips: + - Convert one multilayer Image to a series of frames: + + This can be done if you create a series of empty frames. + The Number of frames should be equal to the number of layers + in your multilayered image. + (File->New, AnimFrames->Duplicate) + + Then use the "Move Path" Plugin (AnimFrames->Move Path) + to copy the layers to all your frames. + Use 2 Points with both X=0, Y=0, Opacity=100%, Width=100%, Height=100%, + select the 1.st Layer of your multilayer Image as Source, + Handle=LeftTop, Mode=Normal, Stepmode=Loop + + Finally you can delete the (empty) backround Layers + (AnimFrames->Frames LayerDel) with Layerstack Parameter >= 1 + + - Reverse the order of Layers within one multilayer Image + Convert one multilayer Image to a series of frames (see above) + then select AnimFrames->FramesToImage and use an invers Range + from N to 1. + + - Convert multiple frames from one Fileformat to another: + rename your images to fit GAP framename conventions + (Example: img_0001.tif, img_0003.tif img_0004.tif) + + then load the 1.st image into the gimp (File->Load) + and select (AnimFrames->Frames Convert) + In the following dialog window you can select + the desired destination filtype by its typical extension + (.jpg, .bmp or so on) + + If your source images have layers and/or cannels + but your destination filtype do not support layers/cannels + you may optional choose flatten (1), to collapse all layers + into one before saving destination frames. + (flatten has no affect to your source frames) + + Type conversions to RGB, GRAY and INDEXED were needed + if your desired destination filetype does not support + the type of the source image. + Example: to convert from (.jpg) to (.gif) + you have to set the destinationtype "INDEXED", + (.gif can not handle RGB, only GRAY and INDEXED) + + Convert to INDEXED reduces the number of colors + downto 256 (or less). + + +- NO UNDO + There is no Undo for the AnimFrame Plugins. + Note: If you step to the next frame (AnimFrames->Goto Next) + the current frame is saved to disk and the next frame + is loaded into the image. + All Undo steps were cleared at this time. + + Tip: "undo" for Move Path + You can Delete the Layers at desired Layerstackposition + (0 == on top) by (AnimFrames->Frames LayerDel) + So you can remove all the Layers inserted by "Move Path" plugin. + +- About Locks + All the GAP Plugins are using a lock, based on the image_id + (that is common to all frame images within one gimp session). + This lock disables to run other gap-plugins (or the same plugin twice) + from the frames Image menu + while the current GAP-plugin is working on that frame. + If you cancel a running GAP-plugin (Cancel Button in the progress window, + or kill it from a shell) the lock remains. To unlock save the current + frame image, close all views to that image, then reload the frame image. + (or quit and restart the gimp) + + WARNING: + It is not recommanded to open more than one frame of + an animation at the same time. + (File->Open "img_0001.xcf" + File->Open "img_0002.xcf") + + In this constellation both gap plugins may concurrent + in save/load to/from the same file. + Example: + call from "img_0001.xcf" AnimFrames->Delete Frames (range 0001 to 0003) + now img_0004.xcf is renamed to img_0001.xcf + img_0005.xcf is renamed to img_0002.xcf + + then + call from "img_0002.xcf" AnimFrames->Goto First + the img_0002.xcf (its old content) is saved, + overwiting wht was img_0004.xcf before. + If you make the 2nd call while the 1st one is in progress, + you may trash your frames (2 writers on one file) and/or crash + your gimp session. + +- Animated calls of Plug-In Filters: + + First of all, you need a multilayered Image. + You can use "->AnimFrames->Frames to Image" + to create one from a series of Frames, + or Duplicate the backround Layer of a single Layered Image + (Press Ctrl-C within the layers_dialog Window N-times) + + Then call from wihin the multilayer Image: + + "->Filters->Animation->Filter all Layers" + + You'll get a window similar to the PDB-Browser, that shows + all available Plug-Ins in a listbox (and informations about + the selected Plugin on the right side). + + Note: The Listbox does not show the full PDB. + (Plugins without RunMode, Image, Drawable Parameters + are never listed here, regardless to your wildcard selection) + + Select one of the listed Plugins and press one of the Buttons: + + "Apply Varying": + The selected Plugin is called 2 times in Interactive mode, + 1. for the backround Layer + 2. for the Top Layer. + + For all further Layers, the Plug-In will work non-interactive + with the Iterated Inbetween Values. + Therefore the plugin must have an _Iterator or _Iterator_ALT + procedure, to modify the "last stored values" + + (GAP provides such procedures for more than 50 existing plugins) + + If the Iterator Procedure is not available, the + "Apply Varying" Button is set insensitive on the selected procedure. + + Note: + It is possible to Iterate Values of the Type PARAM_DRAWABLE, + (such as the BumpMap in plug_in_bump_map: + You may use a layerstack-animated Image as animated Bump-map) + This will only work, if from and to Values are Layers within the same Image. + The Iteration is done on the Layerstackindex in that case. + and the inbetween values for the non-interactive filtercalls + will be the Layers between the from and to Layerstackindex. + (an animation sequence as the user might expect) + + "Apply Constant": + The selected Plugin is called 2 times in Interactive mode, + For all further Layers, the Plug-In will work non-interactive + using the last stored values. + + + The Script sel-to-anim-img.scm simplifies the creation of Animated + Images. Invoke the Script from: + + "->Script-Fu->Animators->Sel To AnimImage" + + The Script creates a new Image with n copies of the current selection. + Then it invokes the animated call of Plug-In Filters (optional) + on the generated new Image. + + Note: - Some Plugins may not work correct or crash when called + in NON_INTERACTIVE mode. (see TESTPROT_iter_ALT) + + - Some Pugin's in your PDB can have earlier or later versions + If their Interfaces were changed + you get the message:(in the shell window) + + ERROR: xxxx_Iterator stored Data missmatch in size N != M + + when using the "Apply Varying" Button. + In that case you can try to generate the needed _Iterator Procedure + for your Plugins current Interface by yourself. + (see file: README_devlopers) + + - The wr_trans plugin collection (available in the Plugin Registry) + provides plugin interfaces for animated calls of the GIMP's transformer + Tools. (Rotate, Scale, Shear, Perspective) + +- How to write (or generate) Animated Plugins: + (see file: README_devlopers) + +-------------------------------------------------------------------- +changes to the gimp-core: +-------------------------------------------------------------------- + +Please note that NO CHANGES are needed if you have GIMP 1.1 + + For my Plugins I added NEW internals + (they are not part of the official gimp 1.0.2, + in most cases the Patch just offers a PDB-Interface + for already existing GIMP-core internal Functions) + + - gimp_drawable_set_image + - gimage_update_full + - gimp_layer_get_linked + - gimp_layer_set_linked + - floating_sel_attach_proc + - floating_sel_rigor_proc + - floating_sel_relax_proc + - gimage_floating_sel_attached_to + + my private extension for the layers_dialog Window + + - gimage_lower_top_layer + - gimage_lower_bot_layer + + Affected gimp-core sources (in directory app/) + gimage.c + gimage.h + gimage_cmds.c + gimage_cmds.h + layer_cmds.c + layer_cmds.h + layers_dialog.c + internal_procs.c + floating_sel_cmds.c + floating_sel_cmds.h + +============================================= +plugin's included in release gap 0.99.00: +============================================= + + plug_in_gap_next + "This plugin exchanges current image with (next nubered) image from disk.", + "/AnimFrames/Goto Next", + + plug_in_gap_prev + "This plugin exchanges current image with (previous nubered) image from disk.", + "/AnimFrames/Goto Prev", + + plug_in_gap_first + "This plugin exchanges current image with (lowest nubered) image from disk.", + "/AnimFrames/Goto First", + + plug_in_gap_last + "This plugin exchanges current image with (highest nubered) image from disk.", + "/AnimFrames/Goto Last", + + plug_in_gap_goto + "This plugin exchanges current image with requested image (nr) from disk.", + "/AnimFrames/Goto Any", + + plug_in_gap_del + "This plugin deltes the given number of frames from disk + including the current frame.", + "/AnimFrames/Delete Frames", + + plug_in_gap_dup + "This plugin duplicates the current frames on disk n-times.", + "/AnimFrames/Duplicate Frames", + + plug_in_gap_exchg + "This plugin exchanges content of the current with destination frame.", + "/AnimFrames/Exchange Frame", + + + plug_in_gap_move + "This plugin copies layer(s) from one sourceimage to multiple frames on disk, + varying position, size and opacity.", + "/AnimFrames/Move Path", + + plug_in_gap_range_to_multilayer + "This plugin creates a new image from the given range of frame-images. + Each frame is converted to one layer in the new image, according to flatten_mode. + (the frames on disk are not changed).", + "/AnimFrames/Frames to Image", + + plug_in_gap_range_flatten + "This plugin flattens the given range of frame-images (on disk)", + "/AnimFrames/Frames Flatten", + + plug_in_gap_range_layer_del + "This plugin deletes one layer in the given range of frame-images (on disk). + exception: the last remaining layer of a frame is not deleted", + "/AnimFrames/Frames LayerDel", + + plug_in_gap_range_convert + "This plugin converts the given range of frame-images to other fileformats + (on disk) depending on extension", + "/AnimFrames/Frames Convert", + + plug_in_gap_layers_run_animfilter + "This plugin calls another plugin for each layer of an image, + varying its settings (to produce animated effects). + The called plugin must work on a single drawable + and must be able to RUN_WITH_LAST_VALS", + "/Filters/Animation/Filter all Layers" + + plug_in_gap_anim_resize + "This plugin resizes all anim_frames (images on disk) + to the given new_width/new_height", + "/AnimFrames/Frames Resize", + + plug_in_gap_anim_crop + "This plugin crops all anim_frames (images on disk) + to the given new_width/new_height", + "/AnimFrames/Frames Crop", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_resize, nreturn_vals, + args_resize, return_vals); + + plug_in_gap_anim_scale + "This plugin scales all anim_frames (images on disk) + to the given new_width/new_height", + "/AnimFrames/Frames Scale", + + plug_in_gap_split + "This plugin splits the current image to anim frames (images on disk). + Each layer is saved as one frame", + "/AnimFrames/Split Img to Frames", + + plug_in_gap_mpeg_encode + "This plugin calls mpeg_encode to convert anim frames to MPEG1, + or just generates a param file for mpeg_encode. + (mpeg_encode must be installed on your system)", + "/AnimFrames/Frames MPEG_encode", + + plug_in_gap_mpeg2encode + "This plugin calls mpeg2encode to convert anim frames to MPEG1 or MPEG2, + or just generates a param file for mpeg2encode. + (mpeg2encode must be installed on your system)", + "/AnimFrames/Frames MPEG2_encode", + + plug_in_gap_shift + "This plugin Exchanges frames numbers in the given range. + (discfile frame_0001.xcf is renamed to frame_0002.xcf, + 2->3, 3->4 ... n->1)", + "/AnimFrames/Framesequence Shift", + + plug_in_gap_modify + "This plugin performs a modifying action on each selected layer + in each selected framerange" + /AnimFrames/Frames Modify + + script-fu-selection-to-anim-image + "Create a multilayer image from current selection and + apply any PDB Filter to all layer-copies" + "->Script-Fu->Animators->Sel To AnimImage" diff --git a/plug-ins/gap/README_developers b/plug-ins/gap/README_developers new file mode 100644 index 0000000000..1692ee5927 --- /dev/null +++ b/plug-ins/gap/README_developers @@ -0,0 +1,259 @@ +This README_developers file should be read by Plug-In authors: + +How to make a GIMP Plug-In an "Animated" one: +--------------------------------------------- + +First of all: + + the plugin must be able to operate on a single Drawable, + ---------------------------- + further it must be able to run with last_values and in interactive mode. + -------------------- ----------- + + For the Animated Plugin Call we need an Iterator Procedure. + This Procedure is named like the Plugin with the extension "_Iterator". + The Iterator Procedure has to know the Plugin's internal datastructure + and has to modify Its "LastValues" step by step at each call. + + The GAP-PDB-Browser /Filters/Animation/Filter All Layers + checks for the existance of Iterator Procedures in the PDB + 1.) _Iterator + 2.) _iter_ALT + + If one of the iterator procedures was found in the PDB, the + "Apply Varying" Button is set sensitive to allow animated calls + for the selected procedure. + + + If you have gap prerelease 0.93.00 or later you can generate + the "plug_in_XXXX_Iterator" + for a Plugin as seperated Sourcefile, ready to compile. + (The Plugin must be found in the PDB at generation time) + + # this example uses the Plugin whirlpinch + + 1.a # for bourne and ksh users: + + GAP_DEBUG=y + export GAP_DEBUG + + 1.b # for csh users + setenv GAP_DEBUG y + + 2. # change to the directory, where you like to generate the sourcecode + cd /usr/local/gimp-0.99.17/plug-ins/whirlpinch + + 3. # start the Gimp + gimp + + 4. # open or create any image + File->New + + 5. # call the GAP Plugin for Animated Filter apply + # from within the image + Filters->Animation->Filter all Layers + + # if you have set GAP_DEBUG as shown above, + # the Window should contain the Button "Gen Code by name" + + # Type the name of your Plugin into the Search Inputfield + # (Click "Search by Name" Button, to check if the Plugin + # is available in the PDB) + + # Then click "Gen Code by Name" Button and then "Cancel" Button. + + # This will generate 4 files in the current directory: + gen_filter_iter_forward.c + gen_filter_iter_tab.c + plug_in_whirl_pinch_iter_ALT.inc + plug_in_whirl_pinch_iter.c + + # You can quit the gimp and delete the 3 files named "gen_filter_iter_*" + + 6. # compile and link the generated Source, plug_in_whirl_pinch_iter.c + # and install the linked executeable + # in global or private plug-in directory + + gimptool --install plug_in_whirl_pinch_iter.c + + # (if you dont have gimptool, you can use a copy of the original Plugin's Makefile + # and change the Plugins by to do that job.) + + + 7. # start the gimp again, + # and open or create an Image that has at least 3 Layers. + # Test the "Animated Filter apply" + + Filters->Animation->Filter all Layers + + # Use the "Apply Varying" Button, + # it should be sensitive now (if all went well so far). + + 8. # In case of error: + # If you get an Error Message (in the shell, where you started the gimp) + # that looks like: + + ERROR: xxxx_Iterator stored Data missmatch in size N != M + + # you have to change the generated code manually. + # (check for calls to "gimp_set_data" or "gimp_get_data" that are using + # the plugins name as key argument within the plugin's sourcecode. + # The passed datastructure has to match exactly in size with the generated one + # for the example above the generated structure is: + # plug_in_whirl_pinch_iter.c: + typedef struct t_plug_in_whirl_pinch_Vals + { + gdouble whirl; + gdouble pinch; + gdouble radius; + } t_plug_in_whirl_pinch_Vals; + + +If you are the Author of a Plugin you may decide to include the _Iterator +within the original Sources of your Plugin. In that case you have to check +the name argument in the run procedure. + + + + +Example Code: + +query() +{ + static GParamDef args_plugin[] = + { + {PARAM_INT32, "run_mode", "non-interactive"}, + {PARAM_IMAGE, "image", "the image"}, + {PARAM_DRAWABLE, "drawable", "the drawable"}, + {PARAM_INT32, "value", "my_parameter value"}, + }; + static int nargs_plugin = sizeof(args_plugin) / sizeof(args_plugin[0]); + + static GParamDef args_iter[] = + { + {PARAM_INT32, "run_mode", "non-interactive"}, + {PARAM_INT32, "total_steps", "total number of steps (# of layers-1 to apply the related plug-in)"}, + {PARAM_FLOAT, "current_step", "current (for linear iterations this is the layerstack position, otherwise some value inbetween)"}, + {PARAM_INT32, "len_struct", "length of stored data structure with id is equal to the plug_in proc_name"}, + }; + static int nargs_iter = sizeof(args_iter) / sizeof(args_iter[0]); + + static GParamDef *return_vals = NULL; + static int nreturn_vals = 0; + + gimp_install_procedure("plug_in_XXXX, + "plug_in_XXXX can do ....", + "", + "Authors Name", + "Copyright", + "Date", + "/Filters/Effects/XXXX", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_plugin, nreturn_vals, + args_plugin, return_vals); + + + gimp_install_procedure("plug_in_XXXX_Iterator, + "This extension calculates the modified values for one iterationstep for the call of plug_in_XXXX", + "", + "Authors Name", + "Copyright", + "Date", + NULL, /* do not appear in menus */ + NULL, + PROC_EXTENSION, + nargs_iter, nreturn_vals, + args_iter, return_vals); +} + + + + +static void p_delta_long(long *val, long val_from, long val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; + +} + + + +static void +run (char *name, + int n_params, + GParam *param, + int *nreturn_vals, + GParam **return_vals) +{ + static GParam values[1]; + GStatusType status = STATUS_SUCCESS; + GRunModeType run_mode; + gint32 len_struct; + gint32 total_steps; + gdouble current_step; + + long pval; /* plug_in_XXXX has one parameter of type long */ + long *pval_from, *pval_to; /* values for 1.st and last layer + * when they were processed by "plug_in_gap_layers_run_animfilter" + */ + + *nreturn_vals = 1; + *return_vals = values; + values[0].type = PARAM_STATUS; + + + run_mode = param[0].data.d_int32; + + if (strcmp (name, "plug_in_XXXX") == 0) + { + .... /* start the plugin itself */ + } + else if (strcmp (name, "plug_in_XXXX_Iterator") == 0) + { + /* Iterator procedure is usually called from + * "plug_in_gap_layers_run_animfilter" + * (always run noninteractive) + */ + if ((run_mode == RUN_NONINTERACTIVE) && (n_params == 4)) + { + total_steps = param[1].data.d_int32; + current_step = param[2].data.d_float; + len_struct = param[3].data.d_int32; + + if(len_struct == sizeof(pval)) + { + /* get _FROM and _TO data, + * This data was stored by plug_in_gap_layers_run_animfilter + */ + + gimp_get_data("plug_in_XXXX_ITER_FROM", pval_from); + gimp_get_data("plug_in_XXXX_ITER_TO", pval_to); + + p_delta_long(&pval, *pval_from, *pval_to, total_steps, current_step); + + gimp_set_data("plug_in_XXXX", &pval, sizeof(pval)); + } + else status = STATUS_CALLING_ERROR; + } + else status = STATUS_CALLING_ERROR; + } + + values[0].type = PARAM_STATUS; + values[0].type = PARAM_STATUS; + +} + + +Important for Plugin_Authors: + I have made Iterator Procedures for more than 50 existing Procedures. + (see gap_filter_iterators.c and subdirectories iter_ALT/*/*.inc) + If your Plugin is found in gap_filter_iterators.c, and You make updates + to your Plugin's interface, you should write (or generate) your own _Iterator Procedure, + to keep its Animated capabilities intact. + (You don't need to change gap sources to do that, because the Iterator + named "plug_in_XXXX_Iterator" is used rather than "plug_in_XXXX_Iterator_ALT" ) diff --git a/plug-ins/gap/TESTPROT_iter_ALT b/plug-ins/gap/TESTPROT_iter_ALT new file mode 100644 index 0000000000..9cad53fa78 --- /dev/null +++ b/plug-ins/gap/TESTPROT_iter_ALT @@ -0,0 +1,299 @@ +Here is a result List of the "Animated Filtercall" Tests + I tested more than 100 PDB-Procedures and the GAP's _iter_ALT Procedures + on a 3-Layer Testimage with Menu: + + /Filters/Animation/Filter All Layers using "Apply Varying" Button + +--------------------------------------------------- +Testresults (used in Makefile 1.1) +-------------------------------------------------- +14.03.1999 + + Test with PDB-Procedures (Plugins) that came with + gimp release 1.1.3 + and all the generated (or manually changed) _iter_ALT Procedures + +State: ++ ... Passed simple "Aplly Varying" Test OK +- ... Failed Test +. ... not tested + + REM ... Removed the generated _iter_ALT Procedure + Plugin did not work with last value interface + but it would make sense if Interface would be OK. + Del ... Deleted the generated _iter_ALT Procedure + It does not make sense to call the Plugin + with varying Values + + * ... Iter_ALT modified by Hand + + + +State Procedure_name Testnotes +--------------------------------------------------------------------------- +. * Colorify (not part of 1.1.3) +-Del perl_fu_blowinout (not tested, perl ) +-Del perl_fu_feedback (not tested, perl ) +-Del perl_fu_prep4gif (not tested, perl ) +-Del perl_fu_scratches (not tested, perl ) +-Del perl_fu_terraltext (not tested, perl ) +-Del perl_fu_tex_string_to_float (not tested, perl ) +-Del perl_fu_webify (not tested, perl ) +-Del perl_fu_windify (not tested, perl ) +-Del perl_fu_xach_blocks (not tested, perl ) +-Del perl_fu_xach_shadows (not tested, perl ) +-Del perl_fu_xachvision (not tested, perl ) +- plug_in_CML_explorer +. * plug_in_CentralReflection (not part of 1.1.3) ++ * plug_in_Twist ++ * plug_in_alienmap +.Del plug_in_align_layers +. plug_in_alpha2color (not tested, perl required) +. plug_in_anamorphose (not part of 1.1.3) +.Del plug_in_animationoptimize +.Del plug_in_animationplay +.Del plug_in_animationunoptimize +-REM plug_in_apply_canvas ERROR: no stored data found for Key plug_in_apply_canvas ++ plug_in_applylens +-Del plug_in_autocrop ERROR: no stored data found for Key plug_in_autocrop +-Del plug_in_autostretch_hsv ERROR: no stored data found for Key plug_in_autostretch_hsv ++ plug_in_blinds ++ * plug_in_blur +. * plug_in_blur2 (not part of 1.1.3) +-Del plug_in_blur_randomize ERROR: no stored data found for Key plug_in_blur_randomize ++ plug_in_borderaverage ++ plug_in_bump_map +-Del plug_in_c_astretch ERROR: no stored data found for Key plug_in_c_astretch ++ plug_in_checkerboard +-Del plug_in_color_adjust ERROR: no stored data found for Key plug_in_color_adjust ++ plug_in_color_map +-REM plug_in_colorify ERROR: no stored data found for Key plug_in_colorify +.Del plug_in_compose +- plug_in_convmatrix ERROR: p_plug_in_convmatrix_iter_ALT stored Data missmatch in size 140 != 0 ++ plug_in_cubism +.Del plug_in_decompose +.Del plug_in_deinterlace ++ * plug_in_depth_merge ++ * plug_in_despeckle ++ plug_in_destripe ++ plug_in_diffraction ++ plug_in_displace +-Del plug_in_ditherize ERROR: no stored data found for Key plug_in_ditherize (perl) ++ plug_in_edge ++ * plug_in_emboss +. * plug_in_encript (not part of 1.1.3) ++ plug_in_engrave +- * plug_in_exchange (interface changed since 1.0, CRASH: gimp_get_data missing when RUN_WITH_LAST_VALS) +.Del plug_in_export_palette +. * plug_in_figures (not part of 1.1.3) +.Del plug_in_film +-REM plug_in_filter_pack ERROR: no stored data found for Key plug_in_filter_pack +- plug_in_flame ++ plug_in_flarefx +- * plug_in_fractal_trace OK Button disappeared at 1.st press ????? ++ plug_in_gauss_iir ++ plug_in_gauss_rle +.Del plug_in_gfig ERROR: no stored data found for Key plug_in_gfig +. * plug_in_gflare (not part of 1.1.3) ++ plug_in_glasstile +.Del plug_in_gradmap ERROR: no stored data found for Key plug_in_gradmap ++ plug_in_grid +-Del plug_in_guillotine ERROR: no stored data found for Key plug_in_guillotine +. * plug_in_holes (not part of 1.1.3) +-Del plug_in_hot +-Del plug_in_ifs_compose ERROR: no stored data found for Key plug_in_ifs_compose +-Del plug_in_illusion OK Button disappeared at 1.st press ????? +.Del plug_in_image_rot270 +.Del plug_in_image_rot90 +-Del plug_in_iwarp Has No RUN_WITH_LAST_VALS support, (but can animate ++ plug_in_jigsaw +. plug_in_julia (not part of 1.1.3) +-Del plug_in_laplace ERROR: no stored data found for Key plug_in_laplace +.Del plug_in_layer_rot270 +.Del plug_in_layer_rot90 +.Del plug_in_layers_import +-Del plug_in_lic CRASH + ERROR: no stored data found for Key plug_in_lic ++ * plug_in_lighting +. * plug_in_magic_eye (not part of 1.1.3) +.Del plug_in_mail_image +-Del plug_in_make_seamless ERROR: no stored data found for Key plug_in_make_seamless +. * plug_in_mandelbrot (not part of 1.1.3) ++ * plug_in_map_object see report below: +.Del plug_in_max_rgb ++ * plug_in_maze (interface changed) ++ plug_in_mblur ++ plug_in_mosaic ++ plug_in_newsprint ++ * plug_in_nlfilt ++ plug_in_noisify +-Del plug_in_normalize ERROR: no stored data found for Key plug_in_normalize ++ * plug_in_nova ++ * plug_in_oilify (interface changed) +- * plug_in_pagecurl ERROR: no stored data found for Key plug_in_pagecurl +- plug_in_paper_tile OK Button disappeared at 1.st press ????? ++ plug_in_pixelize ++ * plug_in_plasma ++ * plug_in_polar_coords (interface changed) +-Del plug_in_qbist +. * plug_in_randomize (not part of 1.1.3) ++ plug_in_randomize_hurl ++ plug_in_randomize_pick ++ plug_in_randomize_slur +. plug_in_refract (not part of 1.1.3) ++ plug_in_ripple ++ plug_in_rotate ++ * plug_in_sample_colorize ++ plug_in_scatter_hsv +.Del plug_in_semiflatten ++ plug_in_sharpen ++ plug_in_shift ++ plug_in_sinus +- plug_in_small_tiles ERROR: no stored data found for Key plug_in_small_tiles +.Del plug_in_smooth_palette +.Del plug_in_sobel ++ * plug_in_solid_noise ++ * plug_in_sparkle ++ plug_in_spread +. plug_in_struc (not part of 1.1.3) +-Del plug_in_the_egg ERROR: no stored data found for Key plug_in_the_egg +-Del plug_in_threshold_alpha ERROR: no stored data found for Key plug_in_threshold_alpha +-Del plug_in_tile +. plug_in_tileit (not part of 1.1.3) +. plug_in_universal_filter (not part of 1.1.3) ++ plug_in_video +-Del plug_in_vinvert ERROR: no stored data found for Key plug_in_vinvert ++ plug_in_vpropagate +. * plug_in_warp (not part of 1.1.3) ++ plug_in_waves ++ plug_in_whirl_pinch ++ plug_in_wind +.Del plug_in_zealouscrop + + +Summary + + 55 Procedures + - 31 Procedures + . 51 Procedures +---------------------- + 137 + +plug_in_map_object Testreport: + + - The MapObject has an implicite feature: + If the handled layer has NO ALPHA Channel (as backgrounds often do) + and the "Transparent background" is ON + it forces the creation of a new image. + (regardless if the "Create new image" option is on or not) + + It took me hours to find out about that feature + that looks more like a bug to me. + I would prefere to add the alpha channel in such a case. + + If you want to do animated calls to MapObject ("Apply Varying" Button) + please make sure that the background layer has an Alphachannel + (Open Layers & Channels dialog, and "Add Apha Channel" + to the bg layer) + + - If you call MapObject a 2nd time in the same gimp session + gimp will crash if one of the drawables (that were used + in the 1.st call) has become invalid. + + The BUG is in the GIMP-core Procedure(s) + gimp_layer_get_image_id + gimp_drawable_image_id + + (in older GIMP releases these Procedures did return -1 + on invalid Ids, in Gimp 1.1.3 it comes to a crash + I hope that this Bug will be fixed in gimp 1.2) + +--------------------------------------------------- +older Testresults (used in Makefile 1.0) +-------------------------------------------------- +with Plugins from the gimp release 0.99.16 + +Each Plugin listed below was called with varying values. +Some Plugins did not work correct when called in +RUN_WITH_LAST_VALS mode. + + +TEST: + tested OK + * generated code did not work (changed manually) + ? not sure if useful + Del Not ueseful, deleted + + + *? Colorify_iter_ALT (crash at 1.call) + ? plug_in_CML_explorer_iter_ALT + *? plug_in_CentralReflection_iter_ALT (crash at 1.call) ++* plug_in_Twist_iter_ALT ++* plug_in_alienmap_iter_ALT +Del plug_in_align_layers_iter_ALT ++? plug_in_anamorphose_iter_ALT ++ plug_in_applylens_iter_ALT ++ plug_in_blinds_iter_ALT ++* plug_in_blur2_iter_ALT ++ plug_in_bump_map_iter_ALT ++ plug_in_checkerboard_iter_ALT +Del plug_in_coordmap_iter_ALT ++ plug_in_cubism_iter_ALT +Del plug_in_curtain_iter_ALT +Del plug_in_decompose_iter_ALT +Del plug_in_deinterlace_iter_ALT ++*? plug_in_depth_merge_iter_ALT ++*? plug_in_despeckle_iter_ALT (crash at 1.call) ++ plug_in_destripe_iter_ALT ++ plug_in_diffraction_iter_ALT ++ plug_in_displace_iter_ALT ++ plug_in_edge_iter_ALT ++* plug_in_emboss_iter_ALT + * plug_in_encript_iter_ALT (crash at noniteractive calls) ++ plug_in_engrave_iter_ALT + *? plug_in_exchange_iter_ALT ++* plug_in_figures_iter_ALT ++* plug_in_fractal_trace_iter_ALT ++ plug_in_gauss_iir_iter_ALT ++ plug_in_gauss_rle_iter_ALT + * plug_in_gflare_iter_ALT ++ plug_in_glasstile_iter_ALT ++ plug_in_grid_iter_ALT + * plug_in_holes_iter_ALT (crash at 1.call) ++ plug_in_illusion_iter_ALT ++* plug_in_julia_iter_ALT + * plug_in_magic_eye_iter_ALT (crash at 1.call) +Del plug_in_mail_image_iter_ALT ++* plug_in_mandelbrot_iter_ALT ++* plug_in_map_object_iter_ALT +Del plug_in_max_rgb_iter_ALT ++ plug_in_maze_iter_ALT ++ plug_in_mblur_iter_ALT ++ plug_in_mosaic_iter_ALT ++* plug_in_nlfilt_iter_ALT ++ plug_in_noisify_iter_ALT ++* plug_in_nova_iter_ALT ++* plug_in_oilify_iter_ALT ++ plug_in_paper_tile_iter_ALT ++ plug_in_pixelize_iter_ALT ++ plug_in_plasma_iter_ALT ++ plug_in_polar_coords_iter_ALT ++* plug_in_randomize_iter_ALT ++ plug_in_refract_iter_ALT ++ plug_in_ripple_iter_ALT +? plug_in_rotate_iter_ALT ++ plug_in_scatter_hsv_iter_ALT +? plug_in_sharpen_iter_ALT ++ plug_in_shift_iter_ALT ++* plug_in_sinus_iter_ALT +Del plug_in_sobel_iter_ALT ++ plug_in_solid_noise_iter_ALT ++* plug_in_sparkle_iter_ALT ++ plug_in_spread_iter_ALT ++ plug_in_struc_iter_ALT +? plug_in_tileit_iter_ALT +? plug_in_universal_filter_iter_ALT ++ plug_in_video_iter_ALT ++ plug_in_vpropagate_iter_ALT ++* plug_in_warp_iter_ALT ++ plug_in_waves_iter_ALT ++* plug_in_whirl_pinch_iter_ALT + +-------------------------------------------------- diff --git a/plug-ins/gap/appenv.h b/plug-ins/gap/appenv.h new file mode 100644 index 0000000000..9199fe191d --- /dev/null +++ b/plug-ins/gap/appenv.h @@ -0,0 +1,46 @@ +/* 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. + */ +#ifndef __APPENV_H__ +#define __APPENV_H__ + +#include "gdk/gdkx.h" +#include "gtk/gtk.h" + +#define DISPLAY ((Display *) GDK_DISPLAY()) + +/* important macros */ +#define BOUNDS(a,x,y) ((a < x) ? x : ((a > y) ? y : a)) +#define MINIMUM(x,y) ((x < y) ? x : y) +#define MAXIMUM(x,y) ((x > y) ? x : y) + +typedef enum { + MESSAGE_BOX, + CONSOLE +} MessageHandlerType; + +extern int no_interface; +extern int no_splash; +extern int no_splash_image; +extern int no_data; +extern int be_verbose; +extern int use_debug_handler; +extern int console_messages; + +extern MessageHandlerType message_handler; + +#endif /* APPENV_H */ diff --git a/plug-ins/gap/gap_arr_dialog.c b/plug-ins/gap/gap_arr_dialog.c new file mode 100644 index 0000000000..49a21d40c4 --- /dev/null +++ b/plug-ins/gap/gap_arr_dialog.c @@ -0,0 +1,1244 @@ +/* gap_arr_dialog.c + * 1998.June.29 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This is the common GTK Dialog for most of the GAP Functions. + * (it replaces the older gap_sld_dialog module) + * + * - p_array_dialog Dialog Window with one or more rows + * each row can contain one of the following GAP widgets: + * - float pair widget + * (horizontal slidebar combined with a float input field) + * - int pair widget + * (horizontal slidebar combined with a int input field) + * - Toggle Button widget + * - Textentry widget + * - Float entry widget + * - Int entry widget + * + * + */ +/* 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: + * version 0.96.03; 1998/08/15 hof: p_arr_gtk_init + * version 0.96.01; 1998/07/09 hof: Bugfix: gtk_init should be called only + * once in a plugin process + * version 0.96.00; 1998/07/09 hof: 1.st release + * (re-implementation of gap_sld_dialog.c) + */ + +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" +#include "libgimp/gimpui.h" + +/* private includes */ +#include "gap_arr_dialog.h" + +typedef void (*t_entry_cb_func) (GtkWidget *widget, t_arr_arg *arr_ptr); + +typedef struct { + GtkWidget *scale; + GtkWidget *entry; + GtkWidget *label; +} t_pair; + +typedef struct +{ + t_arr_arg *arr_ptr; + gint radio_index; +} t_radio_arg; + +typedef struct { + GtkWidget *dlg; + gint run; +} t_arr_interface; + + +static gint g_first_call = TRUE; + +static t_arr_interface g_arrint = +{ + NULL, /* dlg */ + FALSE /* run */ +}; + + +extern int gap_debug; /* ==0 ... dont print debug infos */ +static GtkTooltips *g_tooltips = NULL; + +/* Declare local functions. + */ + +static void arr_close_callback (GtkWidget *widget, gpointer data); +static void but_array_callback (GtkWidget *widget, gpointer data); + +static void entry_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr, + t_entry_cb_func entry_update_cb, char *init_txt); +static void label_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); +static void text_entry_update_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void text_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); +static void filesel_close_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void filesel_ok_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void filesel_open_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void filesel_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); + +static void int_entry_update_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void int_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); +static void flt_entry_update_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void flt_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); + +static void toggle_update_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void toggle_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); + +static void radio_update_cb (GtkWidget *widget, t_radio_arg *radio_ptr); +static void radio_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); +static void optionmenu_create_value (char *title, GtkTable *table, int row, t_arr_arg *arr_ptr); + +static void pair_flt_scale_update_cb (GtkAdjustment *adjustment, t_arr_arg *arr_ptr); +static void pair_flt_entry_update_cb (GtkWidget *widget, t_arr_arg *arr_ptr); +static void pair_flt_create_value (t_pair *pair, + char *title, + GtkTable *table, + int row, + t_arr_arg *arr_ptr); + + +gint +p_arr_gtk_init(gint flag) +{ + gint l_prev_value; + + l_prev_value = g_first_call; + if(flag == TRUE) g_first_call = TRUE; + else g_first_call = FALSE; + + return (l_prev_value); + +} + + +static void +arr_close_callback (GtkWidget *widget, + gpointer data) +{ + gtk_widget_destroy (GTK_WIDGET (g_arrint.dlg)); /* close & destroy dialog window */ + gtk_main_quit (); +} + +static void +but_array_callback (GtkWidget *widget, + gpointer data) +{ + g_arrint.run = *((gint *)data); /* set returnvalue according to button */ + gtk_widget_destroy (GTK_WIDGET (g_arrint.dlg)); /* close & destroy dialog window */ + gtk_main_quit (); +} + + +static void +entry_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr, + t_entry_cb_func entry_update_cb, char *init_txt) +{ + GtkWidget *entry; + GtkWidget *label; + + + label = gtk_label_new(title); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach(table, label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + entry = gtk_entry_new(); + gtk_widget_set_usize(entry, arr_ptr->entry_width, 0); + gtk_entry_set_text(GTK_ENTRY(entry), init_txt); + gtk_signal_connect(GTK_OBJECT(entry), "changed", + (GtkSignalFunc) entry_update_cb, + arr_ptr); + gtk_table_attach(GTK_TABLE(table), entry, 1, 2, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + if(arr_ptr->help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, entry, arr_ptr->help_txt,NULL); + } + gtk_widget_show(entry); + + arr_ptr->text_entry = entry; +} + + +/* -------------------------- + * LABEL + * -------------------------- + */ + +static void +label_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + GtkWidget *label; + + label = gtk_label_new(title); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach(table, label, 0, 3, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); +} + +/* -------------------------- + * FILESEL + * -------------------------- + */ +static void +filesel_close_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + if(arr_ptr->text_filesel == NULL) return; /* filesel is already open */ + + gtk_widget_destroy(GTK_WIDGET(arr_ptr->text_filesel)); + arr_ptr->text_filesel = NULL; +} +static void +filesel_ok_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + char *filename; + + if(arr_ptr->text_filesel == NULL) return; /* filesel is already open */ + + filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (arr_ptr->text_filesel)); + strncpy(arr_ptr->text_buf_ret, filename, arr_ptr->text_buf_len -1); + + gtk_entry_set_text(GTK_ENTRY(arr_ptr->text_entry), filename); + + filesel_close_cb(widget, arr_ptr); +} + +static void +filesel_open_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + GtkWidget *filesel; + + if(arr_ptr->text_filesel != NULL) return; /* filesel is already open */ + + filesel = gtk_file_selection_new (arr_ptr->label_txt); + arr_ptr->text_filesel = filesel; + + gtk_window_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE); + + gtk_signal_connect (GTK_OBJECT (filesel), "destroy", + (GtkSignalFunc) filesel_close_cb, + arr_ptr); + gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button), + "clicked", (GtkSignalFunc) filesel_ok_cb, + arr_ptr); + gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button), + "clicked", (GtkSignalFunc) filesel_close_cb, + (GtkObject *)arr_ptr); + gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), + arr_ptr->text_buf_ret); + gtk_widget_show (filesel); +} + + +static void +filesel_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + GtkWidget *button; + + entry_create_value(title, table, row, arr_ptr, text_entry_update_cb, arr_ptr->text_buf_ret); + arr_ptr->text_filesel = NULL; + + /* Button to invoke filebrowser */ + button = gtk_button_new_with_label ("File-Browser"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) filesel_open_cb, + arr_ptr); + gtk_table_attach( GTK_TABLE(table), button, 2, 3, row, row +1, + 0, 0, 0, 0 ); + gtk_widget_show (button); + +} + +/* -------------------------- + * TEXT + * -------------------------- + */ +static void +text_entry_update_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + if((arr_ptr->widget_type != WGT_TEXT) + && (arr_ptr->widget_type != WGT_FILESEL)) + { + return; + } + + strncpy(arr_ptr->text_buf_ret, + gtk_entry_get_text(GTK_ENTRY(widget)), + arr_ptr->text_buf_len -1); + arr_ptr->text_buf_ret[arr_ptr->text_buf_len -1] = '\0'; +} + +static void +text_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + entry_create_value(title, table, row, arr_ptr, text_entry_update_cb, arr_ptr->text_buf_ret); +} + + +/* -------------------------- + * INT + * -------------------------- + */ + +static void +int_entry_update_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + if(arr_ptr->widget_type != WGT_INT) return; + + arr_ptr->int_ret = atol(gtk_entry_get_text(GTK_ENTRY(widget))); +} + +static void +int_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + char buf[256]; + + sprintf(buf, arr_ptr->int_format, arr_ptr->int_ret); + entry_create_value(title, table, row, arr_ptr, int_entry_update_cb, buf); +} + +/* -------------------------- + * FLOAT + * -------------------------- + */ + +static void +flt_entry_update_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + if(arr_ptr->widget_type != WGT_FLT) return; + + arr_ptr->flt_ret = atof(gtk_entry_get_text(GTK_ENTRY(widget))); +} + +static void +flt_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + char buf[256]; + + sprintf(buf, arr_ptr->int_format, arr_ptr->int_ret); + entry_create_value(title, table, row, arr_ptr, flt_entry_update_cb, buf); +} + +/* -------------------------- + * TOGGLE + * -------------------------- + */ + +static void +toggle_update_cb (GtkWidget *widget, t_arr_arg *arr_ptr) +{ + if(arr_ptr->widget_type !=WGT_TOGGLE) return; + + if (GTK_TOGGLE_BUTTON (widget)->active) + { + arr_ptr->int_ret = 1; + } + else + { + arr_ptr->int_ret = 0; + } +} + +static void +toggle_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + GtkWidget *check_button; + GtkWidget *label; + char *l_togg_txt; + + + label = gtk_label_new(title); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach(table, label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + /* (make sure there is only 0 or 1) */ + if(arr_ptr->int_ret != 0) arr_ptr->int_ret = 1; + + if(arr_ptr->togg_label == NULL) l_togg_txt = " "; + else l_togg_txt = arr_ptr->togg_label; + /* check button */ + check_button = gtk_check_button_new_with_label (l_togg_txt); + gtk_table_attach ( GTK_TABLE (table), check_button, 1, 3, row, row+1, GTK_FILL, 0, 0, 0); + gtk_signal_connect (GTK_OBJECT (check_button), "toggled", + (GtkSignalFunc) toggle_update_cb, + arr_ptr); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (check_button), + arr_ptr->int_ret); + if(arr_ptr->help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, check_button, arr_ptr->help_txt,NULL); + } + gtk_widget_show (check_button); +} + + +/* -------------------------- + * RADIO + * -------------------------- + */ + +static void +radio_update_cb (GtkWidget *widget, t_radio_arg *radio_ptr) +{ + if(radio_ptr->arr_ptr == NULL) return; + if((radio_ptr->arr_ptr->widget_type != WGT_RADIO) + && (radio_ptr->arr_ptr->widget_type != WGT_OPTIONMENU)) + { + return; + } + + radio_ptr->arr_ptr->int_ret = radio_ptr->radio_index; + radio_ptr->arr_ptr->radio_ret = radio_ptr->radio_index; +} + +static void +radio_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + GtkWidget *label; + GtkWidget *radio_table; + GtkWidget *radio_button; + GSList *radio_group = NULL; + gint l_idy; + char *l_radio_txt; + char *l_radio_help_txt; + gint l_radio_pressed; + + t_radio_arg *radio_ptr; + + + label = gtk_label_new(title); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach( GTK_TABLE (table), label, 0, 1, row, row+1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + /* radio_table */ + radio_table = gtk_table_new (arr_ptr->radio_argc, 2, FALSE); + + + for(l_idy=0; l_idy < arr_ptr->radio_argc; l_idy++) + { + radio_ptr = calloc(1, sizeof(t_radio_arg)); + radio_ptr->arr_ptr = arr_ptr; + radio_ptr->radio_index = l_idy; + + if(arr_ptr->radio_ret == l_idy) l_radio_pressed = TRUE; + else l_radio_pressed = FALSE; + + l_radio_txt = "null"; + if (arr_ptr->radio_argv != NULL) + { + if (arr_ptr->radio_argv[l_idy] != NULL) + l_radio_txt = arr_ptr->radio_argv[l_idy]; + } + + l_radio_help_txt = arr_ptr->help_txt; + if (arr_ptr->radio_help_argv != NULL) + { + if (arr_ptr->radio_help_argv[l_idy] != NULL) + l_radio_help_txt = arr_ptr->radio_help_argv[l_idy]; + } + + if(gap_debug) printf("radio_create_value: %02d %s\n", l_idy, l_radio_txt); + + radio_button = gtk_radio_button_new_with_label ( radio_group, l_radio_txt ); + radio_group = gtk_radio_button_group ( GTK_RADIO_BUTTON (radio_button) ); + gtk_table_attach ( GTK_TABLE (radio_table), radio_button, 0, 2, l_idy, l_idy+1, GTK_FILL, 0, 0, 0); + + gtk_signal_connect ( GTK_OBJECT (radio_button), "toggled", + (GtkSignalFunc) radio_update_cb, + radio_ptr); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (radio_button), + l_radio_pressed); + if(l_radio_help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, radio_button, l_radio_help_txt, NULL); + } + gtk_widget_show (radio_button); + } + + + /* attach radio_table */ + gtk_table_attach ( GTK_TABLE (table), radio_table, 1, 3, row, row+1, GTK_FILL, 0, 0, 0); + gtk_widget_show (radio_table); +} + + +/* -------------------------- + * OPTIONMENU + * -------------------------- + */ + +/* optionmenus share callback and data structure with WGT_RADIO */ +static void +optionmenu_create_value(char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + GtkWidget *label; + GtkWidget *option_menu; + GtkWidget *menu; + GtkWidget *menu_item; + gint l_idx; + gint l_idy; + char *l_radio_txt; + gint l_radio_pressed; + + t_radio_arg *l_menu_ptr; + + /* label */ + label = gtk_label_new(title); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach( GTK_TABLE (table), label, 0, 1, row, row+1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + /* optionmenu */ + option_menu = gtk_option_menu_new(); + gtk_table_attach(GTK_TABLE(table), option_menu, 1, 2, row, row +1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show(option_menu); + + if(arr_ptr->help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, option_menu, arr_ptr->help_txt, NULL); + } + + /* menu (filled with items in loop) */ + menu = gtk_menu_new (); + + /* outer loop is done to ensure that the initial "pressed" value + * is the first entry in the optionmenu + */ + for(l_idx=0; l_idx < 2; l_idx++) + { + for(l_idy=0; l_idy < arr_ptr->radio_argc; l_idy++) + { + if(arr_ptr->radio_ret == l_idy) l_radio_pressed = TRUE; + else l_radio_pressed = FALSE; + + if( ((l_radio_pressed == TRUE) && (l_idx == 0)) + || ((l_radio_pressed == FALSE) && (l_idx == 1))) + { + l_radio_txt = "null"; + if (arr_ptr->radio_argv != NULL) + { + if (arr_ptr->radio_argv[l_idy] != NULL) + l_radio_txt = arr_ptr->radio_argv[l_idy]; + } + l_menu_ptr = calloc(1, sizeof(t_radio_arg)); + l_menu_ptr->radio_index = l_idy; + l_menu_ptr->arr_ptr = arr_ptr; + + + menu_item = gtk_menu_item_new_with_label (l_radio_txt); + gtk_container_add (GTK_CONTAINER (menu), menu_item); + + gtk_signal_connect (GTK_OBJECT (menu_item), "activate", + (GtkSignalFunc) radio_update_cb, + l_menu_ptr); + gtk_widget_show (menu_item); + } + } + } + + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + gtk_widget_show(option_menu); + +} + + +/* -------------------------- + * FLT_PAIR + * -------------------------- + */ + + +static void +pair_flt_entry_update_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + GtkAdjustment *adjustment; + gdouble new_value; + gdouble new_value_lim; + + if (arr_ptr->widget_type != WGT_FLT_PAIR) return; + + new_value = atof(gtk_entry_get_text(GTK_ENTRY(widget))); + + /* printf("pair_flt_entry_update_cb: val: %f lim: %f new_value %f\n", arr_ptr->flt_ret, arr_ptr->flt_ret_lim, new_value); + */ + + if (arr_ptr->flt_ret_lim != new_value) + { + adjustment = gtk_object_get_user_data(GTK_OBJECT(widget)); + + new_value_lim = new_value; + if (new_value < adjustment->lower) new_value_lim = adjustment->lower; + if (new_value > adjustment->upper) new_value_lim = adjustment->upper; + + /* if constraint use limit both for entry and scale */ + if(arr_ptr->constraint == TRUE) arr_ptr->flt_ret = new_value_lim; + else arr_ptr->flt_ret = new_value; + + arr_ptr->flt_ret_lim = new_value_lim; + adjustment->value = new_value_lim; + + gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed"); + } +} + +static void +pair_flt_scale_update_cb (GtkAdjustment *adjustment, t_arr_arg *arr_ptr) +{ + GtkWidget *entry; + char buf[256]; + + /* printf("pair_flt_scale_update_cb: val: %f lim: %f new_value %f\n", arr_ptr->flt_ret, arr_ptr->flt_ret_lim, (float)adjustment->value); + */ + + if (arr_ptr->widget_type != WGT_FLT_PAIR) return; + + if (arr_ptr->flt_ret_lim != adjustment->value) + { + arr_ptr->flt_ret = adjustment->value; + arr_ptr->flt_ret_lim = adjustment->value; + + entry = gtk_object_get_user_data(GTK_OBJECT(adjustment)); + sprintf(buf, arr_ptr->flt_format, arr_ptr->flt_ret); + + gtk_signal_handler_block_by_data(GTK_OBJECT(entry), arr_ptr); + gtk_entry_set_text(GTK_ENTRY(entry), buf); + gtk_signal_handler_unblock_by_data(GTK_OBJECT(entry), arr_ptr); + } +} + + +static void +pair_flt_create_value(t_pair *pair, char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + GtkObject *scale_data; + char buf[256]; + + if(arr_ptr->flt_format == NULL) + { + arr_ptr->flt_format = "%0.2f"; /* use default format */ + } + else + { + /* short check if formatstring starts with % and ends with f */ + if((*arr_ptr->flt_format != '%') + || (arr_ptr->flt_format[strlen(arr_ptr->flt_format) -1] != 'f')) + { + printf("pair_flt_create_value: Bad FloatFormat ignored %s\n", arr_ptr->flt_format); + arr_ptr->flt_format = "%0.2f"; + } + } + + /* init the limitied value */ + arr_ptr->flt_ret_lim = arr_ptr->flt_ret; + if (arr_ptr->flt_ret < arr_ptr->flt_min) arr_ptr->flt_ret_lim = arr_ptr->flt_min; + if (arr_ptr->flt_ret > arr_ptr->flt_max) arr_ptr->flt_ret_lim = arr_ptr->flt_max; + + pair->label = gtk_label_new(title); + gtk_misc_set_alignment(GTK_MISC(pair->label), 0.0, 0.5); + gtk_table_attach(table, pair->label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(pair->label); + + scale_data = gtk_adjustment_new((double)arr_ptr->flt_ret, + (double)arr_ptr->flt_min, + (double)arr_ptr->flt_max, + (double)arr_ptr->flt_step, + (double)arr_ptr->flt_step, + 0.0); + + + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc) pair_flt_scale_update_cb, + arr_ptr); + + pair->scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_widget_set_usize(pair->scale, arr_ptr->scale_width, 0); + gtk_table_attach(table, pair->scale, 2, 3, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_scale_set_draw_value(GTK_SCALE(pair->scale), FALSE); + gtk_scale_set_digits(GTK_SCALE(pair->scale), 3); + gtk_range_set_update_policy(GTK_RANGE(pair->scale), GTK_UPDATE_CONTINUOUS); + if(arr_ptr->help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, pair->scale, arr_ptr->help_txt,NULL); + } + gtk_widget_show(pair->scale); + + pair->entry = gtk_entry_new(); + gtk_object_set_user_data(GTK_OBJECT(pair->entry), scale_data); + gtk_object_set_user_data(scale_data, pair->entry); + gtk_widget_set_usize(pair->entry, arr_ptr->entry_width, 0); + sprintf(buf, arr_ptr->flt_format, arr_ptr->flt_ret); + gtk_entry_set_text(GTK_ENTRY(pair->entry), buf); + gtk_signal_connect(GTK_OBJECT(pair->entry), "changed", + (GtkSignalFunc) pair_flt_entry_update_cb, + arr_ptr); + gtk_table_attach(GTK_TABLE(table), pair->entry, 1, 2, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + if(arr_ptr->help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, pair->entry, arr_ptr->help_txt,NULL); + } + gtk_widget_show(pair->entry); +} + + +/* -------------------------- + * INT_PAIR + * -------------------------- + */ + + +static void +pair_int_entry_update_cb(GtkWidget *widget, t_arr_arg *arr_ptr) +{ + GtkAdjustment *adjustment; + gint new_value; + gint new_value_lim; + + if (arr_ptr->widget_type != WGT_INT_PAIR) return; + + new_value = atol(gtk_entry_get_text(GTK_ENTRY(widget))); + + /* printf("pair_int_entry_update_cb: val: %d lim: %d new_value %d\n", arr_ptr->int_ret, arr_ptr->int_ret_lim, new_value); + */ + + if (arr_ptr->int_ret_lim != new_value) + { + adjustment = gtk_object_get_user_data(GTK_OBJECT(widget)); + + new_value_lim = new_value; + if (new_value < (gint)adjustment->lower) new_value_lim = (gint)adjustment->lower; + if (new_value > (gint)adjustment->upper) new_value_lim = (gint)adjustment->upper; + + /* if constraint use limit both for entry and scale */ + if(arr_ptr->constraint == TRUE) + { + arr_ptr->int_ret = new_value_lim; + } + else + { + arr_ptr->int_ret = new_value; + } + + arr_ptr->int_ret_lim = new_value_lim; + adjustment->value = new_value_lim; + + gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed"); + } +} + +static void +pair_int_scale_update_cb (GtkAdjustment *adjustment, t_arr_arg *arr_ptr) +{ + GtkWidget *entry; + char buf[256]; + + /* printf("pair_int_scale_update_cb: val: %d lim: %d new_value %f\n", arr_ptr->int_ret, arr_ptr->int_ret_lim, (float)adjustment->value); + */ + + if (arr_ptr->widget_type != WGT_INT_PAIR) return; + + if (arr_ptr->int_ret_lim != (gint)adjustment->value) + { + arr_ptr->int_ret = (gint)adjustment->value; + arr_ptr->int_ret_lim = (gint)adjustment->value; + + entry = gtk_object_get_user_data(GTK_OBJECT(adjustment)); + sprintf(buf, arr_ptr->int_format, arr_ptr->int_ret); + + gtk_signal_handler_block_by_data(GTK_OBJECT(entry), arr_ptr); + gtk_entry_set_text(GTK_ENTRY(entry), buf); + gtk_signal_handler_unblock_by_data(GTK_OBJECT(entry), arr_ptr); + } +} + + +static void +pair_int_create_value(t_pair *pair, char *title, GtkTable *table, int row, t_arr_arg *arr_ptr) +{ + GtkObject *scale_data; + char buf[256]; + + if(arr_ptr->int_format == NULL) + { + arr_ptr->int_format = "%d"; /* use default format */ + } + else + { + /* short check if formatstring starts with % and ends with f */ + if((*arr_ptr->int_format != '%') + || (arr_ptr->int_format[strlen(arr_ptr->int_format) -1] != 'd')) + { + printf("pair_int_create_value: Bad IntFormat ignored %s\n", arr_ptr->int_format); + arr_ptr->int_format = "%d"; + } + } + + /* init the limitied value */ + arr_ptr->int_ret_lim = arr_ptr->int_ret; + if (arr_ptr->int_ret < arr_ptr->int_min) arr_ptr->int_ret_lim = arr_ptr->int_min; + if (arr_ptr->int_ret > arr_ptr->int_max) arr_ptr->int_ret_lim = arr_ptr->int_max; + + pair->label = gtk_label_new(title); + gtk_misc_set_alignment(GTK_MISC(pair->label), 0.0, 0.5); + gtk_table_attach(table, pair->label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(pair->label); + + scale_data = gtk_adjustment_new((double)arr_ptr->int_ret, + (double)arr_ptr->int_min, + (double)arr_ptr->int_max, + (double)arr_ptr->int_step, + (double)arr_ptr->int_step, + 0.0); + + + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc) pair_int_scale_update_cb, + arr_ptr); + + pair->scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_widget_set_usize(pair->scale, arr_ptr->scale_width, 0); + gtk_table_attach(table, pair->scale, 2, 3, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_scale_set_draw_value(GTK_SCALE(pair->scale), FALSE); + gtk_scale_set_digits(GTK_SCALE(pair->scale), 3); + gtk_range_set_update_policy(GTK_RANGE(pair->scale), GTK_UPDATE_CONTINUOUS); + if(arr_ptr->help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, pair->scale, arr_ptr->help_txt,NULL); + } + gtk_widget_show(pair->scale); + + pair->entry = gtk_entry_new(); + gtk_object_set_user_data(GTK_OBJECT(pair->entry), scale_data); + gtk_object_set_user_data(scale_data, pair->entry); + gtk_widget_set_usize(pair->entry, arr_ptr->entry_width, 0); + sprintf(buf, arr_ptr->int_format, arr_ptr->int_ret); + gtk_entry_set_text(GTK_ENTRY(pair->entry), buf); + gtk_signal_connect(GTK_OBJECT(pair->entry), "changed", + (GtkSignalFunc) pair_int_entry_update_cb, + arr_ptr); + gtk_table_attach(GTK_TABLE(table), pair->entry, 1, 2, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); + if(arr_ptr->help_txt != NULL) + { + gtk_tooltips_set_tip(g_tooltips, pair->entry, arr_ptr->help_txt,NULL); + } + gtk_widget_show(pair->entry); +} + +/* ============================================================================ + * p_array_std_dialog + * + * GTK dialog window that has argc rows. + * each row contains the widget(s) as defined in argv[row] + * + * The Dialog has an Action Area with OK and CANCEL Buttons. + * ============================================================================ + */ +gint p_array_std_dialog(char *title_txt, + char *frame_txt, + int argc, + t_arr_arg argv[], + int b_argc, + t_but_arg b_argv[], + gint b_def_val) +{ + GdkColor tips_fg, tips_bg; + GtkWidget *button; + GtkWidget *frame; + GtkWidget *table; + gchar **l_argsv; + gint l_argsc; + gint l_idx; + gint l_ok_value; + char *l_label_txt; + t_pair l_pair; + t_arr_arg *arr_ptr; + + g_arrint.run = b_def_val; /* prepare default retcode (if window is closed without button) */ + l_ok_value = 0; + table = NULL; + + if((argc > 0) && (argv == NULL)) + { + printf("p_array_std_dialog: calling error (widget array == NULL)\n"); + return (g_arrint.run); + } + if((b_argc > 0) && (b_argv == NULL)) + { + printf("p_array_std_dialog: calling error (button array == NULL)\n"); + return (g_arrint.run); + } + + /* gtk init (only once in a plugin-process) */ + if (g_first_call == TRUE) + { + l_argsc = 1; + l_argsv = g_new (gchar *, 1); + l_argsv[0] = g_strdup ("gap_std_dialog"); + gtk_init (&l_argsc, &l_argsv); + g_first_call = FALSE; + } + + /* dialog */ + g_arrint.dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (g_arrint.dlg), title_txt); + gtk_window_position (GTK_WINDOW (g_arrint.dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (g_arrint.dlg), "destroy", + (GtkSignalFunc) arr_close_callback, + NULL); + + /* tooltips */ + g_tooltips = gtk_tooltips_new(); + /* use black as foreground: */ + tips_fg.red = 0; + tips_fg.green = 0; + tips_fg.blue = 0; + /* postit yellow (khaki) as background: */ + gdk_color_alloc (gtk_widget_get_colormap (g_arrint.dlg), &tips_fg); + tips_bg.red = 61669; + tips_bg.green = 59113; + tips_bg.blue = 35979; + gdk_color_alloc (gtk_widget_get_colormap (g_arrint.dlg), &tips_bg); + gtk_tooltips_set_colors(g_tooltips, &tips_bg, &tips_fg); + + + /* Action area */ + for(l_idx = 0; l_idx < b_argc; l_idx++) + { + + if(b_argv[l_idx].but_txt == NULL) button = gtk_button_new_with_label ("OK"); + else button = gtk_button_new_with_label (b_argv[l_idx].but_txt); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) but_array_callback, + &b_argv[l_idx].but_val); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (g_arrint.dlg)->action_area), button, TRUE, TRUE, 0); + if( b_argv[l_idx].but_val == b_def_val ) gtk_widget_grab_default (button); + gtk_widget_show (button); + + } + + if(b_argc < 1) + { + /* if no buttons are specified use one CLOSE button per default */ + button = gtk_button_new_with_label ("CLOSE"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) but_array_callback, + &l_ok_value); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (g_arrint.dlg)->action_area), button, TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + } + + /* parameter settings */ + if (frame_txt == NULL) frame = gtk_frame_new ("Enter Values"); + else frame = gtk_frame_new (frame_txt); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 10); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (g_arrint.dlg)->vbox), frame, TRUE, TRUE, 0); + + if(argc > 0) + { + /* table (one row per argv) */ + table = gtk_table_new (argc +1, 3, FALSE); + gtk_container_border_width (GTK_CONTAINER (table), 10); + gtk_container_add (GTK_CONTAINER (frame), table); + + for(l_idx = 0; l_idx < argc; l_idx++) + { + arr_ptr = &argv[l_idx]; + + if(arr_ptr->label_txt == NULL) l_label_txt = "Value: "; + else l_label_txt = arr_ptr->label_txt; + + switch(arr_ptr->widget_type) + { + case WGT_FLT_PAIR: + pair_flt_create_value(&l_pair, l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_INT_PAIR: + pair_int_create_value(&l_pair, l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_TOGGLE: + toggle_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_RADIO: + radio_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_OPTIONMENU: + optionmenu_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_FILESEL: + filesel_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_TEXT: + text_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_INT: + int_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_FLT: + flt_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_LABEL: + label_create_value(l_label_txt, GTK_TABLE(table), (l_idx + 1), arr_ptr); + break; + case WGT_ACT_BUTTON: + printf("WGT_ACT_BUTTON not implemented yet, widget type ignored\n"); + break; + default: /* undefined widget type */ + printf("Unknown widget type %d ignored\n", arr_ptr->widget_type); + break; + + } /* end switch */ + } /* end for */ + } + + gtk_widget_show (frame); + if(argc > 0) { gtk_widget_show (table); } + gtk_widget_show (g_arrint.dlg); + + gtk_main (); + gdk_flush (); + + if(gap_debug) + { + /* for debugging: print results to stdout */ + for(l_idx = 0; l_idx < argc; l_idx++) + { + arr_ptr = &argv[l_idx]; + + if(arr_ptr->label_txt == NULL) l_label_txt = "Value: "; + else l_label_txt = arr_ptr->label_txt; + arr_ptr = &argv[l_idx]; + + printf("%02d ", l_idx); + + switch(arr_ptr->widget_type) + { + case WGT_FLT_PAIR: + case WGT_FLT: + printf("FLT %s : %f\n", l_label_txt, arr_ptr->flt_ret); + break; + case WGT_INT_PAIR: + case WGT_INT: + case WGT_TOGGLE: + printf("INT %s : %d\n", l_label_txt, arr_ptr->int_ret); + break; + case WGT_TEXT: + case WGT_FILESEL: + printf("TEXT %s : %s\n", l_label_txt, arr_ptr->text_buf_ret); + break; + case WGT_RADIO: + case WGT_OPTIONMENU: + printf("RADIO/OPTIONMENU %s : %d\n", l_label_txt, arr_ptr->radio_ret); + break; + default: + printf("\n"); + break; + + } + + } + } + + return (g_arrint.run); +} /* end p_array_dialog */ + + + +void p_init_arr_arg (t_arr_arg *arr_ptr, + gint widget_type) +{ + arr_ptr->label_txt = NULL; + arr_ptr->help_txt = NULL; + arr_ptr->togg_label = NULL; + arr_ptr->entry_width = 50; + arr_ptr->scale_width = 180; + arr_ptr->constraint = TRUE; + arr_ptr->has_default = FALSE; + arr_ptr->text_entry = NULL; + + switch(widget_type) + { + case WGT_INT_PAIR: + case WGT_INT: + arr_ptr->widget_type = widget_type; + arr_ptr->int_format = "%d"; + arr_ptr->int_min = 0; + arr_ptr->int_max = 100; + arr_ptr->int_step = 1; + arr_ptr->int_default = 0; + arr_ptr->int_ret = 0; + break; + case WGT_FLT_PAIR: + case WGT_FLT: + arr_ptr->widget_type = widget_type; + arr_ptr->flt_format = "%0.2f"; + arr_ptr->flt_min = 0.0; + arr_ptr->flt_max = 100.0; + arr_ptr->flt_step = 0.1; + arr_ptr->flt_default = 0.0; + arr_ptr->flt_ret = 0.0; + break; + case WGT_TOGGLE: + arr_ptr->widget_type = widget_type; + arr_ptr->int_default = 0; + arr_ptr->int_ret = 0; + break; + case WGT_RADIO: + case WGT_OPTIONMENU: + arr_ptr->widget_type = widget_type; + arr_ptr->radio_argc = 0; + arr_ptr->radio_default = 0; + arr_ptr->radio_ret = 0; + arr_ptr->radio_argv = NULL; + arr_ptr->radio_help_argv = NULL; + break; + case WGT_TEXT: + case WGT_FILESEL: + arr_ptr->widget_type = widget_type; + arr_ptr->text_buf_len = 0; + arr_ptr->text_buf_default = NULL; + arr_ptr->text_buf_ret = NULL; + arr_ptr->text_filesel = NULL; + break; + case WGT_ACT_BUTTON: + arr_ptr->widget_type = widget_type; + arr_ptr->action_functon = NULL; + arr_ptr->action_data = NULL; + break; + default: /* Calling error: undefined widget type */ + arr_ptr->widget_type = WGT_LABEL; + break; + + } + +} /* end p_init_arr_arg */ + +/* ============================================================================ + * simplified calls of p_array_std_dialog + * ============================================================================ + */ + + +gint p_array_dialog(char *title_txt, + char *frame_txt, + int argc, + t_arr_arg argv[]) +{ + static t_but_arg b_argv[2]; + + b_argv[0].but_txt = "OK"; + b_argv[0].but_val = TRUE; + b_argv[1].but_txt = "Cancel"; + b_argv[1].but_val = FALSE; + + return( p_array_std_dialog(title_txt, + frame_txt, + argc, argv, /* widget array */ + 2, b_argv, /* button array */ + FALSE) + ); /* ret value for window close */ +} + +/* ============================================================================ + * p_buttons_dialog + * dialog window wit 1 upto n buttons + * return: the value aassigned with the pressed button. + * (If window closed by windowmanager return b_def_val) + * ============================================================================ + */ + + +gint p_buttons_dialog(char *title_txt, + char *msg_txt, + int b_argc, + t_but_arg b_argv[], + gint b_def_val) +{ + static t_arr_arg argv[1]; + char *frame_txt; + + if(b_argc == 1) frame_txt = "Press Button"; + else frame_txt = "Select"; + + p_init_arr_arg(&argv[0], WGT_LABEL); + argv[0].label_txt = msg_txt; + + return( p_array_std_dialog(title_txt, + frame_txt, + 1, argv, + b_argc, b_argv, + b_def_val) + ); /* ret value for window close */ + +} /* end p_buttons_dialog */ + + + +/* ============================================================================ + * p_slider_dialog + * simplified call of p_array_dialog, using an array with one value. + * + * return the value of the (only) entryfield + * or -1 in case of Error or cancel + * ============================================================================ + */ + +long p_slider_dialog(char *title, char *frame, char *label, char *tooltip, + long min, long max, long curr, long constraint) +{ + static t_arr_arg argv[1]; + + p_init_arr_arg(&argv[0], WGT_INT_PAIR); + argv[0].label_txt = label; + argv[0].help_txt = tooltip; + argv[0].constraint = constraint; + argv[0].entry_width = 45; + argv[0].scale_width = 130; + argv[0].int_min = (gint)min; + argv[0].int_max = (gint)max; + argv[0].int_step = 1; + argv[0].int_ret = (gint)curr; + + if(TRUE == p_array_dialog(title, frame, 1, argv)) + { return (long)(argv[0].int_ret); + } + else return -1; +} /* end p_slider_dialog */ + + + diff --git a/plug-ins/gap/gap_arr_dialog.h b/plug-ins/gap/gap_arr_dialog.h new file mode 100644 index 0000000000..70b736796d --- /dev/null +++ b/plug-ins/gap/gap_arr_dialog.h @@ -0,0 +1,168 @@ +/* gap_arr_dialog.h + * 1998.May.23 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins (Standard array dialog) + * + * - p_array_dialog Dialog Window with one or more rows + * each row can contain one of the following GAP widgets: + * - float pair widget + * (horizontal slidebar combined with a float input field) + * - int pair widget + * (horizontal slidebar combined with a int input field) + * - Toggle Button widget + * - Textentry widget + * - Float entry widget + * - Int entry widget + * - p_slider_dialog + * simplified call of p_pair_array_dialog, + * using an array with one WGT_INT_PAIR. + * - p_buttons_dialog + * + * + * + */ +/* 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: + * version 0.96.03; 1998/08/15 hof: p_arr_gtk_init + * version 0.96.00; 1998/07/09 hof: 1.st release + * (re-implementation of gap_sld_dialog.c) + */ + +#ifndef _ARR_DIALOG_H +#define _ARR_DIALOG_H + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +typedef enum +{ + WGT_LABEL + ,WGT_TEXT + ,WGT_INT + ,WGT_FLT + ,WGT_TOGGLE + ,WGT_RADIO + ,WGT_OPTIONMENU + ,WGT_FLT_PAIR + ,WGT_INT_PAIR + ,WGT_ACT_BUTTON + ,WGT_FILESEL +} t_gap_widget; + +typedef int (*t_action_func) ( gpointer action_data); +/* + * - If one of the Args has set 'has_default' to TRUE + * the action Area will contain an additional Button 'Default' + * + */ + +typedef struct { + t_gap_widget widget_type; + + /* common fields for all widget types */ + char *label_txt; + char *help_txt; + gint entry_width; /* for all Widgets with an entry */ + gint scale_width; /* for the Widgets with a scale */ + gint constraint; /* TRUE: check for min/max values */ + gint has_default; /* TRUE: default value available */ + + /* flt_ fileds are used for WGT_FLT and WGT_FLT_PAIR */ + char *flt_format; /* NULL or something like "%0.2f" */ + gdouble flt_min; + gdouble flt_max; + gdouble flt_step; + gdouble flt_default; + gdouble flt_ret; + gdouble flt_ret_lim; + + /* int_ fileds are used for WGT_INT and WGT_INT_PAIR WGT_TOGGLE */ + char *int_format; /* NULL or something like "%d" */ + gint int_min; + gint int_max; + gint int_step; + gint int_default; + gint int_ret; + gint int_ret_lim; /* for private (arr_dialog.c) use only */ + + /* togg_ field are used for WGT_TOGGLE */ + char *togg_label; /* extra label attached right to toggle button */ + + /* radio_ fileds are used for WGT_RADIO and WGT_OPTIONMENU */ + gint radio_argc; + gint radio_default; + gint radio_ret; + char **radio_argv; + char **radio_help_argv; + + /* text_ fileds are used for WGT_TEXT */ + gint text_buf_len; /* common length for init, default and ret text_buffers */ + char *text_buf_default; + char *text_buf_ret; + GtkWidget *text_filesel; /* for private (arr_dialog.c) use only */ + GtkWidget *text_entry; /* for private (arr_dialog.c) use only */ + + /* action_ fileds are used for WGT_ACT_BUTTON */ + t_action_func action_functon; + gpointer action_data; + +} t_arr_arg; + + +typedef struct { + char *but_txt; + gint but_val; +} t_but_arg; + +void p_init_arr_arg (t_arr_arg *arr_ptr, + gint widget_type); + +gint p_array_dialog (char *title_txt, + char *frame_txt, + int argc, + t_arr_arg argv[]); + +long p_slider_dialog(char *title_txt, + char *frame_txt, + char *label_txt, + char *tooltip_txt, + long min, long max, long curr, long constraint); + + + +gint p_buttons_dialog (char *title_txt, + char *frame_txt, + int b_argc, + t_but_arg b_argv[], + gint b_def_val); + + +gint p_array_std_dialog (char *title_txt, + char *frame_txt, + int argc, + t_arr_arg argv[], + int b_argc, + t_but_arg b_argv[], + gint b_def_val); + +gint p_arr_gtk_init(gint flag); + +#endif diff --git a/plug-ins/gap/gap_dbbrowser_utils.c b/plug-ins/gap/gap_dbbrowser_utils.c new file mode 100644 index 0000000000..cbb0728c34 --- /dev/null +++ b/plug-ins/gap/gap_dbbrowser_utils.c @@ -0,0 +1,791 @@ +/* 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. + */ + + +/* + gap_dbbrowser_utils.c (original code dbbrowser_utils.c by Thomas NOEL + + 27. jan .1999 hof: update for GIMP 1.1.1 (show help) + 09. dec .1998 hof: update for GIMP 1.1 + 12. jan .1998 hof: added "Gen Code" button + + 23. dec .1997 hof: GAP variant of DBbrowser + removed apply_callback + added constraint_procedure, + added 2 buttons + added return type + + + 0.08 26th sept 97 by Thomas NOEL ) +*/ + +#include "gap_dbbrowser_utils.h" + +extern int gap_debug; + +int +gap_db_browser_dialog(char *title_txt, + char *button_1_txt, + char *button_2_txt, + t_constraint_func constraint_func, + t_constraint_func constraint_func_sel1, + t_constraint_func constraint_func_sel2, + t_gap_db_browse_result *result, + gint init_gtk_flag) + /* create the dialog box */ +{ + gchar **l_argsv; + gint l_argsc; + + dbbrowser_t* dbbrowser; + + GtkWidget *button; + GtkWidget *hbox,*searchhbox,*vbox; + GtkWidget *label; + + l_argsc = 1; + l_argsv = g_new (gchar *, 1); + l_argsv[0] = g_strdup ("GAP Animated Filter apply"); + + if (init_gtk_flag) + { + /* gtk init (should be called only once in a plugin-process) */ + gtk_init (&l_argsc, &l_argsv); + } + + dbbrowser = (gpointer)malloc(sizeof(dbbrowser_t)); + + /* store pointers to gap constraint procedures */ + dbbrowser->constraint_func = constraint_func; + dbbrowser->constraint_func_sel1 = constraint_func_sel1; + dbbrowser->constraint_func_sel2 = constraint_func_sel2; + dbbrowser->result = result; + dbbrowser->codegen_flag = 0; /* default: no code generation */ + + /* the dialog box */ + + dbbrowser->dlg = gtk_dialog_new (); + + gtk_window_set_title (GTK_WINDOW (dbbrowser->dlg), "Animated Filter apply (init)"); + gtk_window_position (GTK_WINDOW (dbbrowser->dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dbbrowser->dlg), "destroy", + (GtkSignalFunc) dialog_close_callback, + dbbrowser); + + /* label (fill 1. table_row) */ + label = gtk_label_new(title_txt); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_widget_show(label); + + /* hbox : left=list ; right=description */ + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dbbrowser->dlg)->vbox), + hbox, TRUE, TRUE, 0); + gtk_widget_show (hbox); + + /* left = vbox : the list and the search entry */ + + vbox = gtk_vbox_new( FALSE, 0 ); + gtk_container_border_width (GTK_CONTAINER (vbox), 3); + gtk_box_pack_start (GTK_BOX (hbox), + vbox, FALSE, TRUE, 0); + gtk_widget_show(vbox); + + /* list : list in a scrolled_win */ + + dbbrowser->clist = gtk_clist_new(1); + dbbrowser->scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dbbrowser->scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_clist_set_selection_mode (GTK_CLIST (dbbrowser->clist), + GTK_SELECTION_BROWSE); + + gtk_widget_set_usize(dbbrowser->clist, DBL_LIST_WIDTH, DBL_HEIGHT); + gtk_signal_connect (GTK_OBJECT (dbbrowser->clist), "select_row", + (GtkSignalFunc) procedure_select_callback, + dbbrowser); + gtk_box_pack_start (GTK_BOX (vbox), dbbrowser->scrolled_win, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (dbbrowser->scrolled_win), dbbrowser->clist); + gtk_widget_show(dbbrowser->clist); + gtk_widget_show(dbbrowser->scrolled_win); + + /* search entry */ + + searchhbox = gtk_hbox_new(FALSE,0); + gtk_box_pack_start (GTK_BOX (vbox), + searchhbox, FALSE, TRUE, 0); + gtk_widget_show(searchhbox); + + label = gtk_label_new("Search :"); + gtk_misc_set_alignment( GTK_MISC(label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (searchhbox), + label, TRUE, TRUE, 0); + gtk_widget_show(label); + + dbbrowser->search_entry = gtk_entry_new(); + gtk_box_pack_start (GTK_BOX (searchhbox), + dbbrowser->search_entry, TRUE, TRUE, 0); + gtk_widget_show(dbbrowser->search_entry); + + /* right = description */ + + dbbrowser->descr_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dbbrowser->descr_scroll), + GTK_POLICY_ALWAYS, + GTK_POLICY_ALWAYS + ); + gtk_box_pack_start (GTK_BOX (hbox), + dbbrowser->descr_scroll, TRUE, TRUE, 0); + gtk_widget_set_usize (dbbrowser->descr_scroll, DBL_WIDTH - DBL_LIST_WIDTH, 0); + gtk_widget_show (dbbrowser->descr_scroll); + + /* buttons in dlg->action_aera */ + + gtk_container_border_width (GTK_CONTAINER (GTK_DIALOG(dbbrowser->dlg)->action_area), 0); + + + if (gap_debug) { + button = gtk_button_new_with_label ("Gen Code by name"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) dialog_button_3_callback, dbbrowser ); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dbbrowser->dlg)->action_area), + button, TRUE, TRUE, 0); + gtk_widget_show (button); + } + + dbbrowser->name_button = gtk_button_new_with_label ("Search by name"); + GTK_WIDGET_SET_FLAGS (dbbrowser->name_button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (dbbrowser->name_button), "clicked", + (GtkSignalFunc) dialog_search_callback, dbbrowser); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dbbrowser->dlg)->action_area), + dbbrowser->name_button , TRUE, TRUE, 0); + gtk_widget_show(dbbrowser->name_button); + + dbbrowser->blurb_button = gtk_button_new_with_label ("Search by blurb"); + GTK_WIDGET_SET_FLAGS (dbbrowser->blurb_button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (dbbrowser->blurb_button), "clicked", + (GtkSignalFunc) dialog_search_callback, dbbrowser); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dbbrowser->dlg)->action_area), + dbbrowser->blurb_button , TRUE, TRUE, 0); + gtk_widget_show(dbbrowser->blurb_button); + + if (button_1_txt) { + dbbrowser->app_const_button = gtk_button_new_with_label (button_1_txt); + GTK_WIDGET_SET_FLAGS (dbbrowser->app_const_button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (dbbrowser->app_const_button), "clicked", + (GtkSignalFunc) dialog_button_1_callback, dbbrowser ); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dbbrowser->dlg)->action_area), + dbbrowser->app_const_button, TRUE, TRUE, 0); + gtk_widget_show (dbbrowser->app_const_button); + } else dbbrowser->app_const_button = NULL; + + if (button_2_txt) { + dbbrowser->app_vary_button = gtk_button_new_with_label (button_2_txt); + GTK_WIDGET_SET_FLAGS (dbbrowser->app_vary_button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (dbbrowser->app_vary_button), "clicked", + (GtkSignalFunc) dialog_button_2_callback, dbbrowser ); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dbbrowser->dlg)->action_area), + dbbrowser->app_vary_button, TRUE, TRUE, 0); + gtk_widget_show (dbbrowser->app_vary_button); + } else dbbrowser->app_vary_button = NULL; + + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) dialog_close_callback, dbbrowser); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dbbrowser->dlg)->action_area), + button, TRUE, TRUE, 0); + gtk_widget_show (button); + + + /* now build the list */ + + gtk_widget_show (dbbrowser->clist); + gtk_widget_show (dbbrowser->dlg); + + /* initialize the "return" value (for "apply") */ + + dbbrowser->descr_table = NULL; + dbbrowser->selected_proc_name = NULL; + dbbrowser->selected_scheme_proc_name = NULL; + dbbrowser->selected_proc_blurb = NULL; + dbbrowser->selected_proc_help = NULL; + dbbrowser->selected_proc_author = NULL; + dbbrowser->selected_proc_copyright = NULL; + dbbrowser->selected_proc_date = NULL; + dbbrowser->selected_proc_type = 0; + dbbrowser->selected_nparams = 0; + dbbrowser->selected_nreturn_vals = 0; + dbbrowser->selected_params = NULL; + dbbrowser->selected_return_vals = NULL; + + dbbrowser->result->selected_proc_name[0] = '\0'; + dbbrowser->result->button_nr = -1; + + /* first search (all procedures) */ + dialog_search_callback( NULL, (gpointer)dbbrowser ); + gtk_main (); + gdk_flush (); + + return (dbbrowser->result->button_nr); +} + +static gint +procedure_select_callback (GtkWidget *widget, + gint row, + gint column, + GdkEventButton * bevent, + gpointer data) +{ + dbbrowser_t *dbbrowser = data; + gchar *func; + + g_return_val_if_fail (widget != NULL, FALSE); + /* g_return_val_if_fail (bevent != NULL, FALSE); */ + g_return_val_if_fail (dbbrowser != NULL, FALSE); + + if ((func = (gchar *) (gtk_clist_get_row_data (GTK_CLIST (widget), row)))) + dialog_select (dbbrowser, func); + return FALSE; +} + + +static void +dialog_select (dbbrowser_t *dbbrowser, + gchar *proc_name) + /* update the description box (right) */ +{ + GtkWidget *label; + GtkWidget *old_table; + GtkWidget *help; + GtkWidget *text = NULL; + GtkWidget *vscrollbar; + gint i,row=0; + + if (dbbrowser->selected_proc_name) + g_free(dbbrowser->selected_proc_name); + dbbrowser->selected_proc_name = g_strdup(proc_name); + + if (dbbrowser->selected_scheme_proc_name) + g_free(dbbrowser->selected_scheme_proc_name); + dbbrowser->selected_scheme_proc_name = + g_strdup(proc_name); + convert_string(dbbrowser->selected_scheme_proc_name); + + if (dbbrowser->selected_proc_blurb) g_free(dbbrowser->selected_proc_blurb); + if (dbbrowser->selected_proc_help) g_free(dbbrowser->selected_proc_help); + if (dbbrowser->selected_proc_author) g_free(dbbrowser->selected_proc_author); + if (dbbrowser->selected_proc_copyright) g_free(dbbrowser->selected_proc_copyright); + if (dbbrowser->selected_proc_date) g_free(dbbrowser->selected_proc_date); + if (dbbrowser->selected_params) g_free(dbbrowser->selected_params); + if (dbbrowser->selected_return_vals) g_free(dbbrowser->selected_return_vals); + + gimp_query_procedure (proc_name, + &(dbbrowser->selected_proc_blurb), + &(dbbrowser->selected_proc_help), + &(dbbrowser->selected_proc_author), + &(dbbrowser->selected_proc_copyright), + &(dbbrowser->selected_proc_date), + &(dbbrowser->selected_proc_type), + &(dbbrowser->selected_nparams), + &(dbbrowser->selected_nreturn_vals), + &(dbbrowser->selected_params), + &(dbbrowser->selected_return_vals)); + + /* save the "old" table */ + old_table = dbbrowser->descr_table; + + dbbrowser->descr_table = gtk_table_new( + 10 + dbbrowser->selected_nparams + dbbrowser->selected_nreturn_vals , + 5 , FALSE ); + + gtk_table_set_col_spacings( GTK_TABLE(dbbrowser->descr_table), 3); + + /* show the name */ + + label = gtk_label_new("Name :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+1, GTK_FILL, GTK_FILL, 3, 6); + gtk_widget_show(label); + + label = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(label),dbbrowser->selected_scheme_proc_name); + + gtk_entry_set_editable(GTK_ENTRY(label), FALSE); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 1, 4, row, row+1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + row++; + + /* show the description */ + + label = gtk_label_new("Blurb :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+1, GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show(label); + + label = gtk_label_new(dbbrowser->selected_proc_blurb); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 1, 4, row, row+1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + row++; + + label = gtk_hseparator_new(); /* ok, not really a label ... :) */ + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 4, row, row+1, GTK_FILL, GTK_FILL, 3, 6); + gtk_widget_show(label); + row++; + + /* in parameters */ + if (dbbrowser->selected_nparams) + { + label = gtk_label_new("In :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+(dbbrowser->selected_nparams), + GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show(label); + for (i=0;i<(dbbrowser->selected_nparams);i++) + { + /* name */ + label = gtk_label_new((dbbrowser->selected_params[i]).name); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 1, 2, row, row+1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + + /* type */ + label = gtk_label_new(GParamType2char((dbbrowser->selected_params[i]).type)); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 2, 3, row, row+1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + + /* description */ + label = gtk_label_new((dbbrowser->selected_params[i]).description); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 3, 4, row, row+1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + + row++; + } + } + + if ((dbbrowser->selected_nparams) && + (dbbrowser->selected_nreturn_vals)) { + label = gtk_hseparator_new(); /* ok, not really a label ... :) */ + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 4, row, row+1, + GTK_FILL, GTK_FILL, 3, 6); + gtk_widget_show( label ); + row++; + } + + /* out parameters */ + if (dbbrowser->selected_nreturn_vals) + { + label = gtk_label_new("Out :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+(dbbrowser->selected_nreturn_vals), + GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show(label); + for (i=0;i<(dbbrowser->selected_nreturn_vals);i++) + { + /* name */ + label = gtk_label_new((dbbrowser->selected_return_vals[i]).name); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 1, 2, row, row+1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + + /* type */ + label = gtk_label_new(GParamType2char((dbbrowser->selected_return_vals[i]).type)); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 2, 3, row, row+1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + + /* description */ + label = gtk_label_new((dbbrowser->selected_return_vals[i]).description); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 3, 4, row, row+1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + row++; + + } + } + + if ((dbbrowser->selected_nparams) || + (dbbrowser->selected_nreturn_vals)) { + label = gtk_hseparator_new(); /* ok, not really a label ... :) */ + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 4, row, row+1, + GTK_FILL, GTK_FILL, 3, 6); + gtk_widget_show( label ); + row++; + } + + /* show the help */ + if ((dbbrowser->selected_proc_help) && (strlen(dbbrowser->selected_proc_help) > 1)) + { + label = gtk_label_new("Help :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+1, + GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show(label); + + help = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacing (GTK_TABLE (help), 0, 2); + gtk_table_set_col_spacing (GTK_TABLE (help), 0, 2); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), help, + 1, 4, row, row+1, GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show (help); + row++; + + text = gtk_text_new (NULL, NULL); + gtk_text_set_editable (GTK_TEXT (text), FALSE); + gtk_text_set_word_wrap(GTK_TEXT(text), TRUE); + gtk_widget_set_usize (text, -1, 60); + gtk_table_attach (GTK_TABLE (help), text, 0, 1, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (text); + + vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj); + gtk_table_attach (GTK_TABLE (help), vscrollbar, 1, 2, 0, 1, + GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (vscrollbar); + + label = gtk_hseparator_new(); /* ok, not really a label ... :) */ + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 4, row, row+1, GTK_FILL, GTK_FILL, 3, 6); + gtk_widget_show(label); + row++; + } + + /* show the author & the copyright */ + + label = gtk_label_new("Author :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+1, + GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show(label); + + label = gtk_label_new(dbbrowser->selected_proc_author); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 1, 4, row, row+1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + row++; + + label = gtk_label_new("Date :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+1, + GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show(label); + + label = gtk_label_new(dbbrowser->selected_proc_date); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 1, 4, row, row+1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + row++; + + label = gtk_label_new("Copyright :"); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 0, 1, row, row+1, + GTK_FILL, GTK_FILL, 3, 0); + gtk_widget_show(label); + + label = gtk_label_new(dbbrowser->selected_proc_copyright); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (dbbrowser->descr_table), label, + 1, 4, row, row+1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(label); + row++; + + + if (old_table) gtk_widget_destroy(old_table); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (dbbrowser->descr_scroll), + dbbrowser->descr_table); + + /* now after the table is added to a window add the text */ + if (text != NULL) + { + gtk_widget_realize (text); + gtk_text_freeze (GTK_TEXT (text)); + gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, + dbbrowser->selected_proc_help, -1); + gtk_text_thaw (GTK_TEXT (text)); + } + + + gtk_widget_show(dbbrowser->descr_table); + + /* call constraint functions to check sensibility for the apply buttons */ + if(dbbrowser->app_const_button != NULL) + { + if(0 != (dbbrowser->constraint_func_sel1)(dbbrowser->selected_proc_name)) + { + gtk_widget_set_sensitive (dbbrowser->app_const_button, TRUE); + } + else + { + gtk_widget_set_sensitive (dbbrowser->app_const_button, FALSE); + } + } + if(dbbrowser->app_vary_button != NULL) + { + if(0 != (dbbrowser->constraint_func_sel2)(dbbrowser->selected_proc_name)) + { + gtk_widget_set_sensitive (dbbrowser->app_vary_button, TRUE); + } + else + { + gtk_widget_set_sensitive (dbbrowser->app_vary_button, FALSE); + } + } +} + +static void +dialog_close_callback (GtkWidget *widget, + gpointer data) + /* end of the dialog */ +{ + dbbrowser_t* dbbrowser = data; + + /* we are in the plug_in : kill the gtk application */ + gtk_widget_destroy(dbbrowser->dlg); + gtk_main_quit (); +} + +static void +dialog_button_1_callback (GtkWidget *widget, + gpointer data) + /* end of the dialog */ +{ + dbbrowser_t* dbbrowser = data; + + if (dbbrowser->selected_proc_name==NULL) return; + + strcpy(dbbrowser->result->selected_proc_name, dbbrowser->selected_proc_name); + dbbrowser->result->button_nr = 0; + + gtk_widget_hide(dbbrowser->dlg); + gtk_widget_destroy(dbbrowser->dlg); + gtk_main_quit (); +} + +static void +dialog_button_2_callback (GtkWidget *widget, + gpointer data) + /* end of the dialog */ +{ + dbbrowser_t* dbbrowser = data; + + if (dbbrowser->selected_proc_name==NULL) return; + + strcpy(dbbrowser->result->selected_proc_name, dbbrowser->selected_proc_name); + dbbrowser->result->button_nr = 1; + + gtk_widget_hide(dbbrowser->dlg); + gtk_widget_destroy(dbbrowser->dlg); + gtk_main_quit (); +} + +static void +dialog_button_3_callback (GtkWidget *widget, + gpointer data) + /* end of the dialog */ +{ + dbbrowser_t* dbbrowser = data; + + p_remove_codegen_files(); /* remove old versions of generated CODE */ + dbbrowser->codegen_flag = 1; /* let dialog_search_callback generate */ + dialog_search_callback(dbbrowser->name_button, (gpointer)dbbrowser ); + dbbrowser->codegen_flag = 0; +} + +static void +dialog_search_callback (GtkWidget *widget, + gpointer data) + /* search in the whole db */ +{ + char **proc_list; + int num_procs; + int i, j; + int i_added; + dbbrowser_t* dbbrowser = data; + gchar *func_name, *label, *query_text; + GString *query; + + gtk_clist_freeze(GTK_CLIST(dbbrowser->clist)); + gtk_clist_clear(GTK_CLIST(dbbrowser->clist)); + + /* search */ + + if ( widget == (dbbrowser->name_button) ) + { + gtk_window_set_title (GTK_WINDOW (dbbrowser->dlg), + "Animated Filter apply (by name - please wait)"); + + query = g_string_new (""); + query_text = gtk_entry_get_text(GTK_ENTRY(dbbrowser->search_entry)); + + while (*query_text) + { + if ((*query_text == '_') || (*query_text == '-')) + g_string_append (query, "[-_]"); + else + g_string_append_c (query, *query_text); + + query_text++; + } + + gimp_query_database (query->str, + ".*", ".*", ".*", ".*", ".*", ".*", + &num_procs, &proc_list); + + g_string_free (query, TRUE); + } + else if ( widget == (dbbrowser->blurb_button) ) + { + gtk_window_set_title (GTK_WINDOW (dbbrowser->dlg), + "Animated Filter apply (by blurb - please wait)"); + gimp_query_database (".*", + gtk_entry_get_text( GTK_ENTRY(dbbrowser->search_entry) ), + ".*", ".*", ".*", ".*", ".*", + &num_procs, &proc_list); + } + else { + gtk_window_set_title (GTK_WINDOW (dbbrowser->dlg), + "Animated Filter apply (please wait)"); + gimp_query_database (".*", ".*", ".*", ".*", ".*", ".*", ".*", + &num_procs, &proc_list); + } + + i_added = 0; + for (i = 0; i < num_procs; i++) + { + /* the filter constraint_function checks if the + * PDB-proc has a typical interface to operate on a single drawable. + * all other PDB-procedures are not listed in the GAP-dbbrowser + */ + if(0 != (dbbrowser->constraint_func)((char *)proc_list[i])) + { + j = 0; + while((j < i_added) && + (strcmp(gtk_clist_get_row_data(GTK_CLIST(dbbrowser->clist), j), + proc_list[i]) < 0)) + { + j++; + } + + i_added++; + label = g_strdup(proc_list[i]); + if((dbbrowser->codegen_flag != 0) && (gap_debug)) + { + p_gen_forward_iter_ALT(label); + p_gen_tab_iter_ALT(label); + p_gen_code_iter_ALT(label); + } + convert_string(label); + gtk_clist_insert (GTK_CLIST (GTK_CLIST(dbbrowser->clist)), j, + &label); + func_name = g_strdup (proc_list[i]); + + gtk_clist_set_row_data_full(GTK_CLIST(dbbrowser->clist), j, + func_name, g_free); + } + } + + if ( dbbrowser->clist ) { + ; + } + + g_free( proc_list ); + + gtk_window_set_title (GTK_WINDOW (dbbrowser->dlg), + "Animated Filter apply"); + + gtk_clist_thaw(GTK_CLIST(dbbrowser->clist)); + +} + + +/* utils ... */ + +static void +convert_string (char *str) +{ + while (*str) + { + if (*str == '_') *str = '-'; + str++; + } +} + +static char* +GParamType2char(GParamType t) +{ + switch (t) { + case PARAM_INT32: return "INT32"; + case PARAM_INT16: return "INT16"; + case PARAM_INT8: return "INT8"; + case PARAM_FLOAT: return "FLOAT"; + case PARAM_STRING: return "STRING"; + case PARAM_INT32ARRAY: return "INT32ARRAY"; + case PARAM_INT16ARRAY: return "INT16ARRAY"; + case PARAM_INT8ARRAY: return "INT8ARRAY"; + case PARAM_FLOATARRAY: return "FLOATARRAY"; + case PARAM_STRINGARRAY: return "STRINGARRAY"; + case PARAM_COLOR: return "COLOR"; + case PARAM_REGION: return "REGION"; + case PARAM_DISPLAY: return "DISPLAY"; + case PARAM_IMAGE: return "IMAGE"; + case PARAM_LAYER: return "LAYER"; + case PARAM_CHANNEL: return "CHANNEL"; + case PARAM_DRAWABLE: return "DRAWABLE"; + case PARAM_SELECTION: return "SELECTION"; + case PARAM_BOUNDARY: return "BOUNDARY"; + case PARAM_PATH: return "PATH"; + case PARAM_PARASITE: return "PARASITE"; + case PARAM_STATUS: return "STATUS"; + case PARAM_END: return "END"; + default: return "UNKNOWN?"; + } +} diff --git a/plug-ins/gap/gap_dbbrowser_utils.h b/plug-ins/gap/gap_dbbrowser_utils.h new file mode 100644 index 0000000000..c4cace4b9c --- /dev/null +++ b/plug-ins/gap/gap_dbbrowser_utils.h @@ -0,0 +1,128 @@ +/* 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. + */ + + +/* + gap_dbbrowser_utils.h (original code dbbrowser_utils.h by Thomas NOEL + + 09. dec .1998 hof: update for GIMP 1.1 + 20. dec .1997 hof: GAP variant of DBbrowser + removed apply_callback + added constraint_procedure, + added 2 buttons + added return type + + 0.08 26th sept 97 by Thomas NOEL +*/ + +#ifndef _GAP_DB_BROWSER_UTILS_H +#define _GAP_DB_BROWSER_UTILS_H + +/* configuration */ + +#define DBL_LIST_WIDTH 220 +#define DBL_WIDTH DBL_LIST_WIDTH+400 +#define DBL_HEIGHT 250 + +/* end of configuration */ + +#include +#include "gtk/gtk.h" +#include "libgimp/gimp.h" +# include "gap_filter.h" + +typedef struct { + gchar *label; + gchar *func; +} ListEntry_t; + +typedef struct { + + GtkWidget* dlg; + + GtkWidget* search_entry; + GtkWidget* name_button; + GtkWidget* blurb_button; + GtkWidget* app_const_button; + GtkWidget* app_vary_button; + + GtkWidget* descr_scroll; + GtkWidget* descr_table; + + GtkWidget* clist; + GtkWidget* scrolled_win; + + /* the currently selected procedure */ + gchar *selected_proc_name; + gchar *selected_scheme_proc_name; + gchar *selected_proc_blurb; + gchar *selected_proc_help; + gchar *selected_proc_author; + gchar *selected_proc_copyright; + gchar *selected_proc_date; + int selected_proc_type; + int selected_nparams; + int selected_nreturn_vals; + GParamDef *selected_params; + GParamDef *selected_return_vals; + + t_constraint_func constraint_func; + t_constraint_func constraint_func_sel1; + t_constraint_func constraint_func_sel2; + t_gap_db_browse_result *result; + + gint codegen_flag; +} dbbrowser_t; + +/* local functions */ + +static void +dialog_button_1_callback(GtkWidget *, gpointer ); + +static void +dialog_button_2_callback(GtkWidget *, gpointer ); + +static void +dialog_button_3_callback(GtkWidget *, gpointer ); + +static gint +procedure_select_callback (GtkWidget *widget, + gint row, + gint column, + GdkEventButton * bevent, + gpointer data); + +static void +dialog_search_callback(GtkWidget *, + gpointer); + +static void +dialog_select(dbbrowser_t *dbbrowser, + gchar *proc_name); + +static void +dialog_close_callback(GtkWidget *, + gpointer); + +static void +convert_string (gchar *str); + +static gchar* +GParamType2char(GParamType t); + +#endif diff --git a/plug-ins/gap/gap_exchange_image.c b/plug-ins/gap/gap_exchange_image.c new file mode 100644 index 0000000000..7233c5e27c --- /dev/null +++ b/plug-ins/gap/gap_exchange_image.c @@ -0,0 +1,687 @@ +/* gap_exchange_image.c + * by hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * basic anim functions: + * This Plugin drops the content of the destination + * image (all layers,channels & guides) + * and then copies the content of a source image to dst. image + * + */ +/* 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: + * 0.98.00; 1998/11/30 hof: 1.st release + * (substitute for the procedure "gimp_duplicate_into" + * that was never part of the GIMP core) + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_layer_copy.h" +#include "gap_pdb_calls.h" +#include "gap_exchange_image.h" + + +extern int gap_debug; /* ==0 ... dont print debug infos */ + + +/* ============================================================================ + * p_steal_content + * + * steal all elements (layers, channels, selections, guides, colormap) + * from src_image and add them to dst_image. + * ============================================================================ + */ + +static int +p_steal_content(gint32 dst_image_id, gint32 src_image_id) +{ + int l_rc; + int l_idx; + + gint l_nlayers; + gint l_nchannels; + gint32 *l_layers_list; + gint32 *l_channels_list; + gint32 l_layer_id; + gint32 l_channel_id; + gint32 l_new_channel_id; + gint32 l_layer_mask_id; + gint32 l_guide_id; + gint32 l_src_fsel_id; /* floating selection (in the src_image) */ + gint32 l_src_fsel_attached_to_id; /* the drawable where floating selection is attached to (in the src_image) */ + gint32 l_fsel_attached_to_id; /* the drawable id where to attach the floating selection (dst) */ + gint32 l_fsel_id; /* the drawable id of the floating selection itself (dst) */ + gint32 l_active_layer_id; + gint32 l_active_channel_id; + gint32 l_x1, l_x2, l_y1, l_y2; + guchar *l_cmap; + gint l_ncolors; + + l_rc = -1; /* init retcode to Errorstate */ + l_layers_list = NULL; + l_channels_list = NULL; + l_active_layer_id = -1; + l_active_channel_id = -1; + l_fsel_attached_to_id = -1; /* -1 assume fsel is not available (and not attached to any drawable) */ + l_fsel_id = -1; /* -1 assume there is no floating selection */ + + + if(gap_debug) printf("GAP-DEBUG: START p_steal_content dst_id=%d src_id=%d\n", (int)dst_image_id, (int)src_image_id); + + /* check for floating selection */ + l_src_fsel_attached_to_id = -1; + l_src_fsel_id = gimp_image_floating_selection(src_image_id); + if(l_src_fsel_id >= 0) + { + if(gap_debug) printf("GAP-DEBUG: call floating_sel_relax fsel_id=%d\n", + (int)l_src_fsel_id); + + p_gimp_floating_sel_relax (l_src_fsel_id, FALSE); + l_src_fsel_attached_to_id = p_gimp_image_floating_sel_attached_to(src_image_id); + } + + /* steal all layers */ + l_layers_list = gimp_image_get_layers(src_image_id, &l_nlayers); + + /* foreach layer do */ + for(l_idx = l_nlayers -1; l_idx >= 0; l_idx--) + { + l_layer_id = l_layers_list[l_idx]; + if(gap_debug) printf("GAP-DEBUG: START p_steal_content layer_id=%d\n", (int)l_layer_id); + + + if(l_layer_id == gimp_image_get_active_layer(src_image_id)) + { + l_active_layer_id = l_layer_id; + } + + l_layer_mask_id = gimp_layer_get_mask_id(l_layer_id); + if(l_layer_mask_id >= 0) + { + /* layer has layermask */ + if(gap_debug) printf("GAP-DEBUG: START p_steal_content layer_mask_id=%d\n", (int)l_layer_mask_id); + + /* check for floating selection */ + if(l_layer_mask_id == l_src_fsel_attached_to_id) + { + l_fsel_attached_to_id = l_layer_mask_id; /* the floating selection is attached to this layer_mask */ + } + + p_gimp_drawable_set_image(l_layer_mask_id, dst_image_id); + } + + /* remove layer from source */ + gimp_image_remove_layer(src_image_id, l_layer_id); + + /* and set the dst_image as it's new Master */ + p_gimp_drawable_set_image(l_layer_id, dst_image_id); + + if(l_layer_id == l_src_fsel_id) + { + l_fsel_id = l_layer_id; /* this layer is the floating selection */ + } + else + { + if(gap_debug) printf("GAP-DEBUG: START p_steal_content add_layer_id=%d\n", (int)l_layer_id); + /* add the layer on top of the images layerstak */ + gimp_image_add_layer (dst_image_id, l_layer_id, 0); + + if(l_layer_id == l_src_fsel_attached_to_id) + { + l_fsel_attached_to_id = l_layer_id; /* the floating selection is attached to this layer */ + } + } + + } /* end foreach layer */ + + /* steal all channels */ + + l_channels_list = gimp_image_get_channels(src_image_id, &l_nchannels); + + /* foreach channel do */ + for(l_idx = l_nchannels -1; l_idx >= 0; l_idx--) + { + l_channel_id = l_channels_list[l_idx]; + if(gap_debug) printf("GAP-DEBUG: START p_steal_content channel_id=%d\n", (int)l_channel_id); + + l_new_channel_id = p_my_channel_copy(dst_image_id, l_channel_id); + + if (l_new_channel_id < 0) { goto cleanup; } + + + /* add channel on top of the channelstack */ + gimp_image_add_channel (dst_image_id, l_new_channel_id, 0); + + /* adjust channelproperties */ + gimp_channel_set_visible (l_new_channel_id, gimp_channel_get_visible(l_channel_id)); + gimp_channel_set_show_masked (l_new_channel_id, gimp_channel_get_show_masked(l_channel_id)); + + if(l_channel_id == l_src_fsel_attached_to_id) + { + l_fsel_attached_to_id = l_channel_id; /* the floating_selection is attached to this channel */ + } + + if(l_channel_id == gimp_image_get_active_channel(src_image_id)) + { + l_active_channel_id = l_channel_id; + } + + /* remove channel from source */ + gimp_image_remove_channel(src_image_id, l_channel_id); + + /* and set the dst_image as it's new Master */ + p_gimp_drawable_set_image(l_channel_id, dst_image_id); + + } /* end foreach channel */ + + /* check and see if we have to copy the selection */ + l_channel_id = gimp_image_get_selection(src_image_id); + if((p_get_gimp_selection_bounds(src_image_id, &l_x1, &l_y1, &l_x2, &l_y2)) + && (l_channel_id >= 0)) + { + if(gap_debug) printf("GAP-DEBUG: START p_steal_content selection_channel_id=%d\n", (int)l_channel_id); + + p_gimp_drawable_set_image(l_channel_id, dst_image_id); + p_gimp_selection_load (dst_image_id, l_channel_id); + + } + + + /* attach the floating selection... */ + if((l_fsel_id >= 0) && (l_fsel_attached_to_id >= 0)) + { + if(gap_debug) printf("GAP-DEBUG: attaching floating_selection id=%d to id %d\n", + (int)l_fsel_id, (int)l_fsel_attached_to_id); + if(p_gimp_floating_sel_attach (l_fsel_id, l_fsel_attached_to_id) < 0) + { + /* in case of error add floating_selection like an ordinary layer + * (if patches are not installed you'll get the error for sure) + */ + printf("GAP: floating_selection is added as top-layer (attach failed)\n"); + gimp_image_add_layer (dst_image_id, l_fsel_id, 0); + + } + } + + /* set active layer/channel */ + if(l_active_channel_id >= 0) + { + if(gap_debug) printf("GAP-DEBUG: SET active channel %d\n", (int)l_active_channel_id); + gimp_image_set_active_channel(dst_image_id, l_active_channel_id); + } + if(l_active_layer_id >= 0) + { + if(gap_debug) printf("GAP-DEBUG: SET active layer %d\n", (int)l_active_layer_id); + gimp_image_set_active_layer(dst_image_id, l_active_layer_id); + } + + /* Copy the colormap if necessary */ + if(gimp_image_base_type(src_image_id) == INDEXED) + { + l_cmap = gimp_image_get_cmap (src_image_id, &l_ncolors); + + if(gap_debug) printf("GAP-DEBUG: copy colormap ncolors %d\n", (int)l_ncolors); + gimp_image_set_cmap(dst_image_id, l_cmap, l_ncolors); + } + + /* copy guides + * You need GIMP 1.1 or higher for that feature + * (in GIMP 1.0.2 there is no interface for that job + * and guides will be ignored.) + */ + l_guide_id = p_gimp_image_findnext_guide(src_image_id, 0); /* get 1.st guide */ + while(l_guide_id > 0) + { + /* get position and orientation for the current guide ID */ + p_gimp_image_add_guide(dst_image_id, + p_gimp_image_get_guide_position(src_image_id, l_guide_id), + p_gimp_image_get_guide_orientation(src_image_id, l_guide_id) + ); + l_guide_id = p_gimp_image_findnext_guide(src_image_id, l_guide_id); + } + + l_rc = 0; + +cleanup: + + if(l_layers_list) g_free (l_layers_list); + if(l_channels_list) g_free (l_channels_list); + + if(gap_debug) printf("GAP-DEBUG: END p_steal_content dst_id=%d src_id=%d rc=%d\n", + (int)dst_image_id, (int)src_image_id, l_rc); + + return (l_rc); /* 0 .. OK, or -1 on error */ +} /* end p_steal_content */ + + + +/* ============================================================================ + * p_copy_content + * + * copy all elements (layers, channels, selections, guides, colormap) from src_image + * to dst_image. + * ============================================================================ + */ + +static int +p_copy_content(gint32 dst_image_id, gint32 src_image_id) +{ + int l_rc; + int l_idx; + + gint l_nlayers; + gint l_nchannels; + gint32 *l_layers_list; + gint32 *l_channels_list; + gint32 l_layer_id; + gint32 l_channel_id; + gint32 l_new_layer_id; + gint32 l_new_channel_id; + gint32 l_layer_mask_id; + gint32 l_new_layer_mask_id; + gint32 l_guide_id; + gint32 l_src_fsel_id; /* floating selection (in the src_image) */ + gint32 l_src_fsel_attached_to_id; /* the drawable where floating selection is attached to (in the src_image) */ + gint32 l_fsel_attached_to_id; /* the drawable id where to attach the floating selection (dst) */ + gint32 l_fsel_id; /* the drawable id of the floating selection itself (dst) */ + gint32 l_active_layer_id; + gint32 l_active_channel_id; + gint l_offset_x; + gint l_offset_y; + gint32 l_x1, l_x2, l_y1, l_y2; + guchar *l_cmap; + gint l_ncolors; + + l_rc = -1; /* init retcode to Errorstate */ + l_layers_list = NULL; + l_channels_list = NULL; + l_active_layer_id = -1; + l_active_channel_id = -1; + l_fsel_attached_to_id = -1; /* -1 assume fsel is not available (and not attached to any drawable) */ + l_fsel_id = -1; /* -1 assume there is no floating selection */ + + + if(gap_debug) printf("GAP-DEBUG: START p_copy_content dst_id=%d src_id=%d\n", (int)dst_image_id, (int)src_image_id); + + /* check for floating selection */ + l_src_fsel_attached_to_id = -1; + l_src_fsel_id = gimp_image_floating_selection(src_image_id); + if(l_src_fsel_id >= 0) + { + if(gap_debug) printf("GAP-DEBUG: call floating_sel_relax fsel_id=%d\n", + (int)l_src_fsel_id); + + p_gimp_floating_sel_relax (l_src_fsel_id, FALSE); + l_src_fsel_attached_to_id = p_gimp_image_floating_sel_attached_to(src_image_id); + } + + /* copy all layers */ + l_layers_list = gimp_image_get_layers(src_image_id, &l_nlayers); + + /* foreach layer do */ + for(l_idx = l_nlayers -1; l_idx >= 0; l_idx--) + { + l_layer_id = l_layers_list[l_idx]; + if(gap_debug) printf("GAP-DEBUG: START p_copy_content layer_id=%d\n", (int)l_layer_id); + + + /* copy the layer (including its layermask) */ + l_new_layer_id = p_my_layer_copy(dst_image_id, l_layer_id, + gimp_layer_get_opacity(l_layer_id), + gimp_layer_get_mode(l_layer_id), + &l_offset_x, + &l_offset_y + ); + if (l_new_layer_id < 0) { goto cleanup; } + + if(l_layer_id == l_src_fsel_id) + { + l_fsel_id = l_new_layer_id; /* this layer is the floating selection */ + } + else + { + if(gap_debug) printf("GAP-DEBUG: START p_copy_content add_layer_id=%d\n", (int)l_new_layer_id); + /* add the layer on top of the images layerstak */ + gimp_image_add_layer (dst_image_id, l_new_layer_id, 0); + + if(l_layer_id == l_src_fsel_attached_to_id) + { + l_fsel_attached_to_id = l_new_layer_id; /* the floating selection is attached to this layer */ + } + } + + /* adjust offsets and other layerproperties */ + gimp_layer_set_offsets(l_new_layer_id, l_offset_x, l_offset_y); + gimp_layer_set_visible (l_new_layer_id, gimp_layer_get_visible(l_layer_id)); + p_layer_set_linked (l_new_layer_id, p_layer_get_linked(l_layer_id)); + gimp_layer_set_preserve_transparency (l_new_layer_id, gimp_layer_get_preserve_transparency(l_layer_id)); + + if(l_layer_id == gimp_image_get_active_layer(src_image_id)) + { + l_active_layer_id = l_new_layer_id; + } + + l_layer_mask_id = gimp_layer_get_mask_id(l_layer_id); + if(l_layer_mask_id >= 0) + { + /* layer has layermask */ + l_new_layer_mask_id = gimp_layer_get_mask_id(l_new_layer_id); + + if(gap_debug) printf("GAP-DEBUG: START p_copy_content layer_mask_id=%d\n", (int)l_layer_mask_id); + + /* check for floating selection */ + if(l_layer_mask_id == l_src_fsel_attached_to_id) + { + l_fsel_attached_to_id = l_new_layer_mask_id; /* the floating selection is attached to this layer_mask */ + } + + gimp_layer_set_apply_mask (l_new_layer_id, gimp_layer_get_apply_mask(l_layer_id)); + gimp_layer_set_edit_mask (l_new_layer_id, gimp_layer_get_edit_mask(l_layer_id)); + gimp_layer_set_show_mask (l_new_layer_id, gimp_layer_get_show_mask(l_layer_id)); + + } + + } /* end foreach layer */ + + /* copy all channels */ + + l_channels_list = gimp_image_get_channels(src_image_id, &l_nchannels); + + /* foreach channel do */ + for(l_idx = l_nchannels -1; l_idx >= 0; l_idx--) + { + l_channel_id = l_channels_list[l_idx]; + if(gap_debug) printf("GAP-DEBUG: START p_copy_content channel_id=%d\n", (int)l_channel_id); + + l_new_channel_id = p_my_channel_copy(dst_image_id, l_channel_id); + + if (l_new_channel_id < 0) { goto cleanup; } + + + /* add channel on top of the channelstack */ + gimp_image_add_channel (dst_image_id, l_new_channel_id, 0); + + /* adjust channelproperties */ + gimp_channel_set_visible (l_new_channel_id, gimp_channel_get_visible(l_channel_id)); + gimp_channel_set_show_masked (l_new_channel_id, gimp_channel_get_show_masked(l_channel_id)); + + if(l_channel_id == l_src_fsel_attached_to_id) + { + l_fsel_attached_to_id = l_new_channel_id; /* the floating_selection is attached to this channel */ + } + + if(l_channel_id == gimp_image_get_active_channel(src_image_id)) + { + l_active_channel_id = l_new_channel_id; + } + } /* end foreach channel */ + + /* check and see if we have to copy the selection */ + l_channel_id = gimp_image_get_selection(src_image_id); + if((p_get_gimp_selection_bounds(src_image_id, &l_x1, &l_y1, &l_x2, &l_y2)) + && (l_channel_id >= 0)) + { + if(gap_debug) printf("GAP-DEBUG: START p_copy_content selection_channel_id=%d\n", (int)l_channel_id); + + l_new_channel_id = p_my_channel_copy(dst_image_id, l_channel_id); + + if (l_new_channel_id < 0) { goto cleanup; } + + p_gimp_selection_load (dst_image_id, l_new_channel_id); + + /* delete the channel after load into selection */ + gimp_channel_delete(l_new_channel_id); + } + + + /* attach the floating selection... */ + if((l_fsel_id >= 0) && (l_fsel_attached_to_id >= 0)) + { + if(gap_debug) printf("GAP-DEBUG: attaching floating_selection id=%d to id %d\n", + (int)l_fsel_id, (int)l_fsel_attached_to_id); + if(p_gimp_floating_sel_attach (l_fsel_id, l_fsel_attached_to_id) < 0) + { + /* in case of error add floating_selection like an ordinary layer + * (if patches are not installed you'll get the error for sure) + */ + printf("GAP: floating_selection is added as top-layer (attach failed)\n"); + gimp_image_add_layer (dst_image_id, l_fsel_id, 0); + + } + } + + /* set active layer/channel */ + if(l_active_channel_id >= 0) + { + if(gap_debug) printf("GAP-DEBUG: SET active channel %d\n", (int)l_active_channel_id); + gimp_image_set_active_channel(dst_image_id, l_active_channel_id); + } + if(l_active_layer_id >= 0) + { + if(gap_debug) printf("GAP-DEBUG: SET active layer %d\n", (int)l_active_layer_id); + gimp_image_set_active_layer(dst_image_id, l_active_layer_id); + } + + /* Copy the colormap if necessary */ + if(gimp_image_base_type(src_image_id) == INDEXED) + { + l_cmap = gimp_image_get_cmap (src_image_id, &l_ncolors); + + if(gap_debug) printf("GAP-DEBUG: copy colormap ncolors %d\n", (int)l_ncolors); + gimp_image_set_cmap(dst_image_id, l_cmap, l_ncolors); + } + + /* copy guides + * You need GIMP 1.1 or higher for that feature + * (in GIMP 1.0.2 there is no interface for that job + * and guides will be ignored.) + */ + l_guide_id = p_gimp_image_findnext_guide(src_image_id, 0); /* get 1.st guide */ + while(l_guide_id > 0) + { + /* get position and orientation for the current guide ID */ + p_gimp_image_add_guide(dst_image_id, + p_gimp_image_get_guide_position(src_image_id, l_guide_id), + p_gimp_image_get_guide_orientation(src_image_id, l_guide_id) + ); + l_guide_id = p_gimp_image_findnext_guide(src_image_id, l_guide_id); + } + + l_rc = 0; + +cleanup: + + if(l_layers_list) g_free (l_layers_list); + if(l_channels_list) g_free (l_channels_list); + + if(gap_debug) printf("GAP-DEBUG: END p_copy_content dst_id=%d src_id=%d rc=%d\n", + (int)dst_image_id, (int)src_image_id, l_rc); + + return (l_rc); /* 0 .. OK, or -1 on error */ +} /* end p_copy_content */ + + + +/* ============================================================================ + * p_replace_img + * + * This procedure replaces the content of image_id + * with the content from src_image_id. + * By copying or stealing all its layers and channels. + * (stealing is faster, but requres extensions to the GIMP-core). + * ============================================================================ + */ + +static int +p_replace_img(gint32 image_id, gint32 src_image_id) +{ + int l_rc; + int l_idx; + gint l_nlayers; + gint l_nchannels; + gint32 *l_layers_list; + gint32 *l_channels_list; + gint32 l_layer_id; + gint32 l_channel_id; + gint32 l_guide_id; + gint32 l_old_bg_layer_id; + + if(gap_debug) printf("\nGAP-DEBUG: START p_replace_img img_id=%d \n", (int)image_id); + + l_old_bg_layer_id = -1; + + gimp_image_disable_undo (image_id); + + /* remove selection (if there is any) */ + p_gimp_selection_none(image_id); + + l_channels_list = gimp_image_get_channels(image_id, &l_nchannels); + + /* foreach channel do */ + for(l_idx = 0; l_idx < l_nchannels; l_idx++) + { + l_channel_id = l_channels_list[l_idx]; + gimp_image_remove_channel(image_id, l_channel_id); + gimp_channel_delete(l_channel_id); + } + if(l_channels_list) { g_free (l_channels_list); } + + + /* delete guides */ + l_guide_id = p_gimp_image_findnext_guide(image_id, 0); /* get 1.st guide */ + while(l_guide_id > 0) + { + /* delete guide ID */ + p_gimp_image_delete_guide(image_id, l_guide_id); + + /* get 1.st (of the remaining) guides */ + l_guide_id = p_gimp_image_findnext_guide(image_id, 0); + } + + /* get list of all (old) dst_layers to delete */ + l_layers_list = gimp_image_get_layers(image_id, &l_nlayers); + + /* foreach (old) layer of the do */ + for(l_idx = 0; l_idx < l_nlayers; l_idx++) + { + l_layer_id = l_layers_list[l_idx]; + + if(l_idx == l_nlayers -1) + { + /* the background layer is renamed + * (if not GIMP 1.1 will rename the new imported + * Background Layer as "background#2") + * and deleted later. + * Important: the dst_image_id should not be completely empty. + * anytime it may happen, that its display(s) is (are) updated + * and this may crash on empty images. + */ + gimp_layer_set_name(l_layer_id, "--old-bg-layer-will-die-soon"); + l_old_bg_layer_id = l_layer_id; + } + else + { + if(gap_debug) printf("GAP-DEBUG: p_replace_img del layer_id=%d \n", (int)l_layer_id); + gimp_image_remove_layer(image_id, l_layer_id); + /* gimp_layer_delete(l_layer_id); */ /* did crash in gimp 1.0.2 ?? and in 1.1 too */ + } + } + + + /* copy (or steal) all layers, channels and guides from src_image */ + if (p_pdb_procedure_available("gimp_drawable_set_image") >= 0) + { + /* if the Procedure "gimp_drawable_set_image" is available + * we can steal the layers and channels instead of copying them + * (that gives faster performance) + */ + l_rc = p_steal_content(image_id, src_image_id); + } + else + { + l_rc = p_copy_content(image_id, src_image_id); + } + + if(l_old_bg_layer_id >= 0) + { + /* now delete the (old) background layer */ + if(gap_debug) printf("GAP-DEBUG: p_replace_img del (old bg) layer_id=%d \n", (int)l_old_bg_layer_id); + gimp_image_remove_layer(image_id, l_old_bg_layer_id); + /* gimp_layer_delete(l_old_bg_layer_id); */ /* did crash in gimp 1.0.2 ?? and in 1.1 too */ + } + + if (l_layers_list) { g_free (l_layers_list); } + + gimp_image_enable_undo (image_id); + + if(gap_debug) printf("GAP-DEBUG: END p_replace_img img_id=%d rc=%d\n", (int)image_id, l_rc); + + return l_rc; /* OK */ + +} /* end p_replace_img */ + + + +/* ============================================================================ + * p_exchange_image + * + * + * ============================================================================ + */ + +int p_exchange_image(gint32 dst_image_id, gint32 src_image_id) +{ + int l_rc; + + /* check for equal image type */ + if(gimp_image_base_type(src_image_id) != gimp_image_base_type(dst_image_id)) + { + printf("GAP: p_exchange_image Image Types are not equal\n"); + return -1; + } + + /* check for equal width/height */ + if((gimp_image_height(src_image_id) != gimp_image_height(dst_image_id)) + || (gimp_image_width(src_image_id) != gimp_image_width(dst_image_id))) + { + printf("GAP: p_exchange_image Image Size is not equal\n"); + return -1; + } + + /* Now we drop content and copy (or steal) from src_image + * step by step, using many other PDB-Interfaces. + */ + l_rc = p_replace_img(dst_image_id, src_image_id); + + return (l_rc); + +} /* end p_exchange_image */ diff --git a/plug-ins/gap/gap_exchange_image.h b/plug-ins/gap/gap_exchange_image.h new file mode 100644 index 0000000000..dcfd261d43 --- /dev/null +++ b/plug-ins/gap/gap_exchange_image.h @@ -0,0 +1,36 @@ +/* gap_exchange_image.h + * + * + */ +/* 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: + * version 0.98.00; 1998/11/26 hof: 1.st release + * (substitute for the procedure "gimp_duplicate_into" + * that was never part of the GIMP core) + */ + +#ifndef _GAP_EXCHANGE_IMAGE_H +#define _GAP_EXCHANGE_IMAGE_H + +#include "libgimp/gimp.h" + +int p_exchange_image(gint32 dst_image_id, gint32 src_image_id); + +#endif diff --git a/plug-ins/gap/gap_filter.h b/plug-ins/gap/gap_filter.h new file mode 100644 index 0000000000..2a6565a21e --- /dev/null +++ b/plug-ins/gap/gap_filter.h @@ -0,0 +1,94 @@ +/* gap_filter.h + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * Headers for gap_filter_*.c (animated filter apply to all imagelayers) + * + */ +/* 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. + */ + +#ifndef _GAP_FILTER_H +#define _GAP_FILTER_H + +#include "libgimp/gimp.h" + +/* max buffer size for plugin' stored valus + */ +#define PLUGIN_DATA_SIZE 8192 + +/* ------------------------ + * gap_filter_foreach.h + * ------------------------ + */ + +gint gap_proc_anim_apply(GRunModeType run_mode, gint32 image_id, char *l_plugin_name); + + +/* ------------------------ + * gap_filter_iterators.h + * ------------------------ + */ + +/* Hacked Iterators for some existing Plugins */ + +gint gap_run_iterators_ALT(char *name, GRunModeType run_mode, gint32 total_steps, gdouble current_step, gint32 len_struct); +void gap_query_iterators_ALT(); + +/* ------------------------ + * gap_dbbrowser.h + * ------------------------ + */ + +typedef struct { + char selected_proc_name[256]; + int button_nr; /* -1 on cancel, 0 .. n */ +} t_gap_db_browse_result; + +/* proc to check if to add or not to add the procedure to the browsers listbox + * retcode: + * 0 ... do not add + * 1 ... add the procedure to the browsers listbox + */ +typedef int (*t_constraint_func) ( gchar *proc_name); + +int +gap_db_browser_dialog (char *title_txt, + char *button_1_txt, + char *button_2_txt, + t_constraint_func constraint_func, + t_constraint_func constraint_func_sel1, + t_constraint_func constraint_func_sel2, + t_gap_db_browse_result *result, + gint init_gtk_flag); + +/* ------------------------ + * gap_filter_codegen.h + * ------------------------ + */ + +void p_remove_codegen_files(); +gint p_gen_code_iter_ALT (char *proc_name); +gint p_gen_forward_iter_ALT(char *proc_name); +gint p_gen_tab_iter_ALT (char *proc_name); +gint p_gen_code_iter (char *proc_name); + + + +#endif diff --git a/plug-ins/gap/gap_filter_codegen.c b/plug-ins/gap/gap_filter_codegen.c new file mode 100644 index 0000000000..5ae8fff335 --- /dev/null +++ b/plug-ins/gap/gap_filter_codegen.c @@ -0,0 +1,747 @@ +/* gap_filter_codegen.c + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * - GAP_filter codegenerator procedures for _iterator_ALT procedures + * + * Note: this code is only used in debug mode, + * (for developers (Hackers) to generate code templates + * for _iterator_ALT or _Iterator procedures.) + */ + +/* revision history: + * version 0.99.00 1999.03.14 hof: Codegeneration of File ./gen_filter_iter_code.c + * splittet into single Files XX_iter_ALT.inc + * bugfixes in code generation + * version 0.95.04 1998.06.12 hof: p_delta_drawable (enable use of layerstack anims in drawable iteration) + * version 0.93.00 hof: generate Iterator Source + * in one single file (per plugin), ready to compile + * version 0.91.01; Tue Dec 23 hof: 1.st (pre) release + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include + +/* GIMP includes */ +#include "libgimp/gimp.h" + +/* GAP includes */ + +/* int gap_debug = 1; */ /* print debug infos */ +/* int gap_debug = 0; */ /* 0: dont print debug infos */ + +extern int gap_debug; +gint p_gen_code_iter(char *proc_name); + +#define GEN_FORWARDFILE_NAME "gen_filter_iter_forward.c" +#define GEN_TABFILE_NAME "gen_filter_iter_tab.c" + +void p_remove_codegen_files() +{ + remove(GEN_FORWARDFILE_NAME); + remove(GEN_TABFILE_NAME); + + printf("overwrite file: %s\n", GEN_FORWARDFILE_NAME); + printf("overwrite file: %s\n", GEN_TABFILE_NAME); +} + + +static char* +p_type_to_string(GParamType t) +{ + switch (t) { + case PARAM_INT32: return "long "; + case PARAM_INT16: return "short "; + case PARAM_INT8: return "char "; + case PARAM_FLOAT: return "gdouble "; + case PARAM_STRING: return "char *"; + case PARAM_INT32ARRAY: return "INT32ARRAY"; + case PARAM_INT16ARRAY: return "INT16ARRAY"; + case PARAM_INT8ARRAY: return "INT8ARRAY"; + case PARAM_FLOATARRAY: return "FLOATARRAY"; + case PARAM_STRINGARRAY: return "STRINGARRAY"; + case PARAM_COLOR: return "t_color "; + case PARAM_REGION: return "REGION"; + case PARAM_DISPLAY: return "gint32 "; + case PARAM_IMAGE: return "gint32 "; + case PARAM_LAYER: return "gint32 "; + case PARAM_CHANNEL: return "gint32 "; + case PARAM_DRAWABLE: return "gint32 "; + case PARAM_SELECTION: return "SELECTION"; + case PARAM_BOUNDARY: return "BOUNDARY"; + case PARAM_PATH: return "PATH"; + case PARAM_STATUS: return "STATUS"; + case PARAM_END: return "END"; + default: return "UNKNOWN?"; + } +} + + +static void p_get_gendate(char *gendate) +{ + struct tm *l_t; + long l_ti; + + l_ti = time(0L); /* Get UNIX time */ + l_t = localtime(&l_ti); /* konvert time to tm struct */ + sprintf(gendate, "%02d.%02d.%02d %02d:%02d" + , l_t->tm_mday + , l_t->tm_mon + 1 + , l_t->tm_year + , l_t->tm_hour + , l_t->tm_min); +} + + +static void +p_clean_name(char *name, char *clean_name) +{ + char *l_ptr; + + l_ptr = clean_name; + while(*name != '\0') + { + if((*name == '-') + || (*name == '+') + || (*name == '/') + || (*name == '%') + || (*name == '*') + || (*name == ':') + || (*name == '!') + || (*name == '=') + || (*name == ';') + || (*name == '^') + || (*name == ',') + || (*name == '[') + || (*name == ']') + || (*name == '{') + || (*name == '}') + || (*name == '(') + || (*name == ')') + || (*name == ' ') + || (*name == '$') + || (*name == '<') + || (*name == '|') + || (*name == '>') + || (*name == '?') + || (*name == '~') + ) + { + *l_ptr = '_'; + } + else + { + *l_ptr = *name; + } + name++; + l_ptr++; + } + *l_ptr = '\0'; +} + +gint p_gen_code_iter_ALT(char *proc_name) +{ + FILE *l_fp; + int l_idx; + + int l_nparams; + int l_nreturn_vals; + int l_proc_type; + char *l_proc_blurb; + char *l_proc_help; + char *l_proc_author; + char *l_proc_copyright; + char *l_proc_date; + GParamDef *l_params; + GParamDef *l_return_vals; + gint l_rc; + + char l_filename[512]; + char l_gendate[30]; + char l_clean_proc_name[256]; + char l_clean_par_name[256]; + + + l_rc = 0; + p_get_gendate(&l_gendate[0]); + + /* Query the gimp application's procedural database + * regarding a particular procedure. + */ + if(gimp_query_procedure (proc_name, + &l_proc_blurb, + &l_proc_help, + &l_proc_author, + &l_proc_copyright, + &l_proc_date, + &l_proc_type, + &l_nparams, + &l_nreturn_vals, + &l_params, + &l_return_vals)) + { + p_clean_name(proc_name, &l_clean_proc_name[0]); + /* procedure found in PDB */ + if(gap_debug) fprintf(stderr, "DEBUG: found in PDB %s author: %s copyright: %s\n", + proc_name, l_proc_author, l_proc_copyright); + + /* check if plugin can be a typical one, that works on one drawable */ + if (l_proc_type != PROC_PLUG_IN) { l_rc = -1; } + if (l_nparams < 3) { l_rc = -1; } + if (l_params[0].type != PARAM_INT32) { l_rc = -1; } + if (l_params[1].type != PARAM_IMAGE) { l_rc = -1; } + if (l_params[2].type != PARAM_DRAWABLE) { l_rc = -1; } + + sprintf(l_filename, "%s_iter_ALT.inc", l_clean_proc_name); + l_fp = fopen(l_filename, "w"); + if(l_fp != NULL) + { + fprintf(l_fp, "/* ----------------------------------------------------------------------\n"); + fprintf(l_fp, " * p_%s_iter_ALT \n", l_clean_proc_name); + fprintf(l_fp, " * ----------------------------------------------------------------------\n"); + fprintf(l_fp, " */\n"); + fprintf(l_fp, "gint p_%s_iter_ALT(GRunModeType run_mode, gint32 total_steps, gdouble current_step, gint32 len_struct) \n", l_clean_proc_name); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " typedef struct t_%s_Vals \n", l_clean_proc_name); + fprintf(l_fp, " {\n"); + + for(l_idx = 3; l_idx < l_nparams; l_idx++) + { + p_clean_name(l_params[l_idx].name, &l_clean_par_name[0]); + + fprintf(l_fp, " %s %s;\n", + p_type_to_string(l_params[l_idx].type), l_clean_par_name); + } + fprintf(l_fp, " } t_%s_Vals; \n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " t_%s_Vals buf, *buf_from, *buf_to; \n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(len_struct != sizeof(t_%s_Vals)) \n", l_clean_proc_name); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " fprintf(stderr, \"ERROR: p_\%s_iter_ALT stored Data missmatch in size %%d != %%d\\n\", \n", l_clean_proc_name); + fprintf(l_fp, " (int)len_struct, sizeof(t_%s_Vals) ); \n", l_clean_proc_name); + fprintf(l_fp, " return -1; /* ERROR */ \n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " gimp_get_data(\"%s_ITER_FROM\", g_plugin_data_from); \n", l_clean_proc_name); + fprintf(l_fp, " gimp_get_data(\"%s_ITER_TO\", g_plugin_data_to); \n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " buf_from = (t_%s_Vals *)&g_plugin_data_from[0]; \n", l_clean_proc_name); + fprintf(l_fp, " buf_to = (t_%s_Vals *)&g_plugin_data_to[0]; \n", l_clean_proc_name); + fprintf(l_fp, " memcpy(&buf, buf_from, sizeof(buf));\n"); + fprintf(l_fp, "\n"); + + for(l_idx = 3; l_idx < l_nparams; l_idx++) + { + p_clean_name(l_params[l_idx].name, &l_clean_par_name[0]); + + switch(l_params[l_idx].type) + { + case PARAM_INT32: + fprintf(l_fp, " p_delta_long(&buf.%s, buf_from->%s, buf_to->%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_INT16: + fprintf(l_fp, " p_delta_short(&buf.%s, buf_from->%s, buf_to->%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_INT8: + fprintf(l_fp, " p_delta_char(&buf.%s, buf_from->%s, buf_to->%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_FLOAT: + fprintf(l_fp, " p_delta_gdouble(&buf.%s, buf_from->%s, buf_to->%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_COLOR: + fprintf(l_fp, " p_delta_color(&buf.%s, &buf_from->%s, &buf_to->%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_DRAWABLE: + fprintf(l_fp, " p_delta_drawable(&buf.%s, buf_from->%s, buf_to->%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + default: + break; + } + } + fprintf(l_fp, "\n"); + fprintf(l_fp, " gimp_set_data(\"%s\", &buf, sizeof(buf)); \n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " return 0; /* OK */\n"); + fprintf(l_fp, "}\n"); + + + fclose(l_fp); + } + + + /* free the query information */ + g_free (l_proc_blurb); + g_free (l_proc_help); + g_free (l_proc_author); + g_free (l_proc_copyright); + g_free (l_proc_date); + g_free (l_params); + g_free (l_return_vals); + } + else + { + return -1; + } + + p_gen_code_iter(proc_name); + + return l_rc; +} /* p_gen_code_iter_ALT */ + +gint p_gen_forward_iter_ALT(char *proc_name) +{ + FILE *l_fp; + char l_clean_proc_name[256]; + + p_clean_name(proc_name, &l_clean_proc_name[0]); + l_fp = fopen(GEN_FORWARDFILE_NAME, "a"); + if(l_fp != NULL) + { + fprintf(l_fp, "static gint p_%s_iter_ALT (GRunModeType run_mode, gint32 total_steps, gdouble current_step, gint32 len_struct);\n", + l_clean_proc_name); + fclose(l_fp); + } + return 0; +} + +gint p_gen_tab_iter_ALT(char *proc_name) +{ + FILE *l_fp; + char l_clean_proc_name[256]; + + p_clean_name(proc_name, &l_clean_proc_name[0]); + l_fp = fopen(GEN_TABFILE_NAME, "a"); + if(l_fp != NULL) + { + fprintf(l_fp, " , { \"%s\", p_%s_iter_ALT }\n", + l_clean_proc_name, l_clean_proc_name); + fclose(l_fp); + } + return 0; +} + + + +/* Generate _Itrerator Procedure all in one .c file, + * ready to compile + */ +gint p_gen_code_iter(char *proc_name) +{ + FILE *l_fp; + int l_idx; + + int l_nparams; + int l_nreturn_vals; + int l_proc_type; + char *l_proc_blurb; + char *l_proc_help; + char *l_proc_author; + char *l_proc_copyright; + char *l_proc_date; + GParamDef *l_params; + GParamDef *l_return_vals; + gint l_rc; + + char l_filename[512]; + char l_gendate[30]; + char l_clean_proc_name[256]; + char l_clean_par_name[256]; + + l_rc = 0; + p_get_gendate(&l_gendate[0]); + + /* Query the gimp application's procedural database + * regarding a particular procedure. + */ + if(gimp_query_procedure (proc_name, + &l_proc_blurb, + &l_proc_help, + &l_proc_author, + &l_proc_copyright, + &l_proc_date, + &l_proc_type, + &l_nparams, + &l_nreturn_vals, + &l_params, + &l_return_vals)) + { + p_clean_name(proc_name, &l_clean_proc_name[0]); + /* procedure found in PDB */ + if(gap_debug) fprintf(stderr, "DEBUG: found in PDB %s\n", proc_name); + + /* check if plugin can be a typical one, that works on one drawable */ + if (l_proc_type != PROC_PLUG_IN) { l_rc = -1; } + if (l_nparams < 3) { l_rc = -1; } + if (l_params[0].type != PARAM_INT32) { l_rc = -1; } + if (l_params[1].type != PARAM_IMAGE) { l_rc = -1; } + if (l_params[2].type != PARAM_DRAWABLE) { l_rc = -1; } + + + sprintf(l_filename, "%s_iter.c", l_clean_proc_name); + + l_fp = fopen(l_filename, "w"); + if(l_fp != NULL) + { + + fprintf(l_fp, "/* %s\n", l_filename); + fprintf(l_fp, " * generated by gap_filter_codegen.c\n"); + fprintf(l_fp, " * generation date: %s\n", l_gendate); + fprintf(l_fp, " *\n"); + fprintf(l_fp, " * generation source Gimp PDB entry name: %s\n", l_clean_proc_name); + fprintf(l_fp, " * version : %s\n", l_proc_date); + fprintf(l_fp, " *\n"); + fprintf(l_fp, " * The generated code will not work if the internal data stucture\n"); + fprintf(l_fp, " * (used to store and retrieve \"LastValues\") is different to the\n"); + fprintf(l_fp, " * PDB Calling Interface.\n"); + fprintf(l_fp, " *\n"); + fprintf(l_fp, " * In that case you will get an Error message like that:\n"); + fprintf(l_fp, " * ERROR: xxxx_Iterator stored Data missmatch in size N != M\n"); + fprintf(l_fp, " * if the Iterator is called. \n"); + fprintf(l_fp, " * (via \"Filter all Layers\" using \"Apply Varying\" Button)\n"); + fprintf(l_fp, " *\n"); + fprintf(l_fp, " * When you get this Error, you should change this generated code.\n"); + fprintf(l_fp, " * \n"); + fprintf(l_fp, " */\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "/* SYTEM (UNIX) includes */ \n"); + fprintf(l_fp, "#include \n"); + fprintf(l_fp, "#include \n"); + fprintf(l_fp, "#include \n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "/* GIMP includes */\n"); + fprintf(l_fp, "#include \"gtk/gtk.h\"\n"); + fprintf(l_fp, "#include \"libgimp/gimp.h\"\n"); + fprintf(l_fp, "\n"); + + fprintf(l_fp, "typedef struct { guchar color[3]; } t_color; \n"); + fprintf(l_fp, "typedef struct { gint color[3]; } t_gint_color; \n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "static void query(void); \n"); + fprintf(l_fp, "static void run(char *name, int nparam, GParam *param, int *nretvals, GParam **retvals); \n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "GPlugInInfo PLUG_IN_INFO = \n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " NULL, /* init_proc */ \n"); + fprintf(l_fp, " NULL, /* quit_proc */ \n"); + fprintf(l_fp, " query, /* query_proc */ \n"); + fprintf(l_fp, " run, /* run_proc */ \n"); + fprintf(l_fp, "}; \n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "/* ----------------------------------------------------------------------\n"); + fprintf(l_fp, " * iterator functions for basic datatypes \n"); + fprintf(l_fp, " * ----------------------------------------------------------------------\n"); + fprintf(l_fp, " */\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "static void p_delta_long(long *val, long val_from, long val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " *val = val_from + delta; \n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "static void p_delta_short(short *val, short val_from, short val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " *val = val_from + delta;\n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "static void p_delta_gint(gint *val, gint val_from, gint val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " *val = val_from + delta;\n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "static void p_delta_char(char *val, char val_from, char val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " *val = val_from + delta;\n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "static void p_delta_gdouble(double *val, double val_from, double val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " *val = val_from + delta;\n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "static void p_delta_float(float *val, float val_from, float val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " *val = val_from + delta;\n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "static void p_delta_color(t_color *val, t_color *val_from, t_color *val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, " int l_idx;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " for(l_idx = 0; l_idx < 3; l_idx++)\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " delta = ((double)(val_to->color[l_idx] - val_from->color[l_idx]) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " val->color[l_idx] = val_from->color[l_idx] + delta;\n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "static void p_delta_gint_color(t_gint_color *val, t_gint_color *val_from, t_gint_color *val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " double delta;\n"); + fprintf(l_fp, " int l_idx;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(total_steps < 1) return;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " for(l_idx = 0; l_idx < 3; l_idx++)\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " delta = ((double)(val_to->color[l_idx] - val_from->color[l_idx]) / (double)total_steps) * ((double)total_steps - current_step);\n"); + fprintf(l_fp, " val->color[l_idx] = val_from->color[l_idx] + delta;\n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, "}\n"); + + fprintf(l_fp, "static void p_delta_drawable(gint32 *val, gint32 val_from, gint32 val_to, gint32 total_steps, gdouble current_step)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " gint l_nlayers;\n"); + fprintf(l_fp, " gint32 *l_layers_list;\n"); + fprintf(l_fp, " gint32 l_tmp_image_id;\n"); + fprintf(l_fp, " gint l_idx, l_idx_from, l_idx_to;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " l_tmp_image_id = gimp_drawable_image_id(val_from);\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " /* check if from and to values are both valid drawables within the same image */\n"); + fprintf(l_fp, " if ((l_tmp_image_id > 0)\n"); + fprintf(l_fp, " && (l_tmp_image_id = gimp_drawable_image_id(val_to)))\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " l_idx_from = -1;\n"); + fprintf(l_fp, " l_idx_to = -1;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " /* check the layerstack index of from and to drawable */\n"); + fprintf(l_fp, " l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);\n"); + fprintf(l_fp, " for (l_idx = l_nlayers -1; l_idx >= 0; l_idx--)\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " if( l_layers_list[l_idx] == val_from ) l_idx_from = l_idx;\n"); + fprintf(l_fp, " if( l_layers_list[l_idx] == val_to ) l_idx_to = l_idx;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if((l_idx_from != -1) && (l_idx_to != -1))\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " /* OK found both index values, iterate the index (proceed to next layer) */\n"); + fprintf(l_fp, " p_delta_gint(&l_idx, l_idx_from, l_idx_to, total_steps, current_step);\n"); + fprintf(l_fp, " *val = l_layers_list[l_idx];\n"); + fprintf(l_fp, " break;\n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, " g_free (l_layers_list);\n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, "}\n"); + + + fprintf(l_fp, "\n"); + fprintf(l_fp, "/* ----------------------------------------------------------------------\n"); + fprintf(l_fp, " * p_%s_iter \n", l_clean_proc_name); + fprintf(l_fp, " * ----------------------------------------------------------------------\n"); + fprintf(l_fp, " */\n"); + fprintf(l_fp, "gint p_%s_iter(GRunModeType run_mode, gint32 total_steps, gdouble current_step, gint32 len_struct) \n", l_clean_proc_name); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " typedef struct t_%s_Vals \n", l_clean_proc_name); + fprintf(l_fp, " {\n"); + + for(l_idx = 3; l_idx < l_nparams; l_idx++) + { + p_clean_name(l_params[l_idx].name, &l_clean_par_name[0]); + + fprintf(l_fp, " %s %s;\n", + p_type_to_string(l_params[l_idx].type), l_clean_par_name); + } + fprintf(l_fp, " } t_%s_Vals; \n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " t_%s_Vals buf, buf_from, buf_to; \n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if(len_struct != sizeof(t_%s_Vals)) \n", l_clean_proc_name); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " fprintf(stderr, \"ERROR: p_\%s_iter stored Data missmatch in size %%d != %%d\\n\", \n", l_clean_proc_name); + fprintf(l_fp, " (int)len_struct, sizeof(t_%s_Vals) ); \n", l_clean_proc_name); + fprintf(l_fp, " return -1; /* ERROR */ \n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " gimp_get_data(\"%s_ITER_FROM\", &buf_from); \n", l_clean_proc_name); + fprintf(l_fp, " gimp_get_data(\"%s_ITER_TO\", &buf_to); \n", l_clean_proc_name); + fprintf(l_fp, " memcpy(&buf, &buf_from, sizeof(buf));\n"); + fprintf(l_fp, "\n"); + + for(l_idx = 3; l_idx < l_nparams; l_idx++) + { + p_clean_name(l_params[l_idx].name, &l_clean_par_name[0]); + + switch(l_params[l_idx].type) + { + case PARAM_INT32: + fprintf(l_fp, " p_delta_long(&buf.%s, buf_from.%s, buf_to.%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_INT16: + fprintf(l_fp, " p_delta_short(&buf.%s, buf_from.%s, buf_to.%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_INT8: + fprintf(l_fp, " p_delta_char(&buf.%s, buf_from.%s, buf_to.%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_FLOAT: + fprintf(l_fp, " p_delta_gdouble(&buf.%s, buf_from.%s, buf_to.%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_COLOR: + fprintf(l_fp, " p_delta_color(&buf.%s, &buf_from.%s, &buf_to.%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + case PARAM_DRAWABLE: + fprintf(l_fp, " p_delta_drawable(&buf.%s, buf_from.%s, buf_to.%s, total_steps, current_step);\n", + l_clean_par_name, l_clean_par_name, l_clean_par_name); + break; + default: + break; + } + } + fprintf(l_fp, "\n"); + fprintf(l_fp, " gimp_set_data(\"%s\", &buf, sizeof(buf)); \n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " return 0; /* OK */\n"); + fprintf(l_fp, "}\n"); + + fprintf(l_fp, "MAIN ()\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "/* ----------------------------------------------------------------------\n"); + fprintf(l_fp, " * install (query) _Iterator\n"); + fprintf(l_fp, " * ----------------------------------------------------------------------\n"); + fprintf(l_fp, " */\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "static void query ()\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " char l_blurb_text[300];\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " static GParamDef args_iter[] =\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " {PARAM_INT32, \"run_mode\", \"non-interactive\"},\n"); + fprintf(l_fp, " {PARAM_INT32, \"total_steps\", \"total number of steps (# of layers-1 to apply the related plug-in)\"},\n"); + fprintf(l_fp, " {PARAM_FLOAT, \"current_step\", \"current (for linear iterations this is the layerstack position, otherwise some value inbetween)\"},\n"); + fprintf(l_fp, " {PARAM_INT32, \"len_struct\", \"length of stored data structure with id is equal to the plug_in proc_name\"},\n"); + fprintf(l_fp, " };\n"); + fprintf(l_fp, " static int nargs_iter = sizeof(args_iter) / sizeof(args_iter[0]);\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " static GParamDef *return_vals = NULL;\n"); + fprintf(l_fp, " static int nreturn_vals = 0;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " sprintf(l_blurb_text, \"This extension calculates the modified values for one iterationstep for the call of %s\");\n", l_clean_proc_name); + fprintf(l_fp, "\n"); + fprintf(l_fp, " gimp_install_procedure(\"%s_Iterator\",\n", l_clean_proc_name); + fprintf(l_fp, " l_blurb_text,\n"); + fprintf(l_fp, " \"\",\n"); + fprintf(l_fp, " \"Wolfgang Hofer\",\n"); + fprintf(l_fp, " \"Wolfgang Hofer\",\n"); + fprintf(l_fp, " \"%s\",\n", l_gendate); /* generation date */ + fprintf(l_fp, " NULL, /* do not appear in menus */\n"); + fprintf(l_fp, " NULL,\n"); + fprintf(l_fp, " PROC_EXTENSION,\n"); + fprintf(l_fp, " nargs_iter, nreturn_vals,\n"); + fprintf(l_fp, " args_iter, return_vals);\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "}\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "/* ----------------------------------------------------------------------\n"); + fprintf(l_fp, " * run Iterator\n"); + fprintf(l_fp, " * ----------------------------------------------------------------------\n"); + fprintf(l_fp, " */\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "static void\n"); + fprintf(l_fp, "run (char *name,\n"); + fprintf(l_fp, " int n_params,\n"); + fprintf(l_fp, " GParam *param,\n"); + fprintf(l_fp, " int *nreturn_vals,\n"); + fprintf(l_fp, " GParam **return_vals)\n"); + fprintf(l_fp, "{\n"); + fprintf(l_fp, " static GParam values[1];\n"); + fprintf(l_fp, " GRunModeType run_mode;\n"); + fprintf(l_fp, " GStatusType status = STATUS_SUCCESS;\n"); + fprintf(l_fp, " gint32 image_id;\n"); + fprintf(l_fp, " gint32 len_struct;\n"); + fprintf(l_fp, " gint32 total_steps;\n"); + fprintf(l_fp, " gdouble current_step;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " gint32 l_rc;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " *nreturn_vals = 1;\n"); + fprintf(l_fp, " *return_vals = values;\n"); + fprintf(l_fp, " l_rc = 0;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " run_mode = param[0].data.d_int32;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " if ((run_mode == RUN_NONINTERACTIVE) && (n_params == 4))\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " total_steps = param[1].data.d_int32;\n"); + fprintf(l_fp, " current_step = param[2].data.d_float;\n"); + fprintf(l_fp, " len_struct = param[3].data.d_int32;\n"); + fprintf(l_fp, " l_rc = p_%s_iter(run_mode, total_steps, current_step, len_struct);\n", l_clean_proc_name); + fprintf(l_fp, " if(l_rc < 0)\n"); + fprintf(l_fp, " {\n"); + fprintf(l_fp, " status = STATUS_EXECUTION_ERROR;\n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, " }\n"); + fprintf(l_fp, " else status = STATUS_CALLING_ERROR;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, " values[0].type = PARAM_STATUS;\n"); + fprintf(l_fp, " values[0].data.d_status = status;\n"); + fprintf(l_fp, "\n"); + fprintf(l_fp, "}\n"); + + + + + fclose(l_fp); + } + + + /* free the query information */ + g_free (l_proc_blurb); + g_free (l_proc_help); + g_free (l_proc_author); + g_free (l_proc_copyright); + g_free (l_proc_date); + g_free (l_params); + g_free (l_return_vals); + } + else + { + return -1; + } + + + return l_rc; +} /* p_gen_code_iter */ + diff --git a/plug-ins/gap/gap_filter_foreach.c b/plug-ins/gap/gap_filter_foreach.c new file mode 100644 index 0000000000..ce0e495fa7 --- /dev/null +++ b/plug-ins/gap/gap_filter_foreach.c @@ -0,0 +1,573 @@ +/* gap_filter_foreach.c + * 1997.12.23 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * - GAP_filter foreach: call any Filter (==Plugin Proc) + * with varying settings for all + * layers within one Image. + * + */ +/* 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: + * version 0.97.00 hof: - modul splitted (2.nd part is now gap_filter_pdb.c) + * version 0.96.03 hof: - pitstop dialog provides optional backup on each step + * (and skip option) + * version 0.96.00 hof: - now using gap_arr_dialog.h + * version 0.92.00 hof: - pitstop dialog + * give user a chance to stop after interactive plugin calls + * if you dont want the dialog export GAP_FILTER_PITSTOP="N" + * - fixed bug in restore of layervisibility + * - codegen via explicite button (in gap_debug mode) + * version 0.91.01; Tue Dec 23 hof: 1.st (pre) release + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_arr_dialog.h" +#include "gap_filter.h" +#include "gap_filter_pdb.h" +#include "gap_lib.h" + + +/* ------------------------ + * global gap DEBUG switch + * ------------------------ + */ + +/* int gap_debug = 1; */ /* print debug infos */ +/* int gap_debug = 0; */ /* 0: dont print debug infos */ + +extern int gap_debug; + +static gint32 g_current_image_id; + +void p_gdisplays_update_full(gint32 image_id) +{ + GParam* l_params; + gint l_retvals; + + if(p_procedure_available("gimp_image_update_full", PTYP_ANY) >= 0) + { + + l_params = gimp_run_procedure ("gimp_image_update_full", + &l_retvals, + PARAM_IMAGE, image_id, + PARAM_END); + + /* Note: gimp_displays_update_full is not available in the official release gimp 0.99.16 + * (dont care if procedure is not there, + * --> the user may not see the current layer, because + * gimp_displays_flush() does not update on changes + * of the visibility. + */ + + g_free(l_params); + } + gimp_displays_flush(); + +} + +/* pitstop dialog + * return -1 on cancel, 0 .. on continue, 1 .. on skip + */ +static gint p_pitstop(GRunModeType run_mode, char *plugin_name, gint text_flag, + char *step_backup_file, gint len_step_backup_file, + gint32 layer_idx) +{ + char *l_env; + char l_msg[512]; + static t_but_arg l_but_argv[3]; + gint l_but_argc; + gint l_argc; + static t_arr_arg l_argv[1]; + int l_continue; + char l_skip_txt[20]; + + + + p_init_arr_arg(&l_argv[0], WGT_FILESEL); + l_argv[0].label_txt ="backup to file"; + l_argv[0].entry_width = 140; /* pixel */ + l_argv[0].help_txt = "Make backup of the image after each step"; + l_argv[0].text_buf_len = len_step_backup_file; + l_argv[0].text_buf_ret = step_backup_file; + + l_but_argv[0].but_txt = "Continue"; + l_but_argv[0].but_val = 0; + l_but_argv[1].but_txt = "Cancel"; + l_but_argv[1].but_val = -1; + sprintf(l_skip_txt, "Skip %d", (int)layer_idx); + l_but_argv[2].but_txt = l_skip_txt; + l_but_argv[2].but_val = 1; + + l_but_argc = 2; + l_argc = 0; + /* optional dialog between both calls (to see the effect of 1.call) */ + if(run_mode == RUN_INTERACTIVE) + { + l_env = getenv("GAP_FILTER_PITSTOP"); + if(l_env != NULL) + { + if((*l_env == 'N') || (*l_env == 'n')) + { + return 0; /* continue without question */ + } + } + if(text_flag == 0) + { + sprintf(l_msg, "2.nd call of %s\n(define end-settings)", plugin_name); + } + else + { + sprintf(l_msg, "Non-Interactive call of %s\n(for all layers inbetween)", plugin_name); + l_but_argc = 3; + l_argc = 1; + } + l_continue = p_array_std_dialog ("Animated Filter apply", l_msg, + l_argc, l_argv, + l_but_argc, l_but_argv, 0); + if(l_continue < 0) return -1; + else return l_continue; + + } + + return 0; /* continue without question */ +} /* end p_pitstop */ + + +static void p_visibilty_restore(gint32 image_id, gint nlayers, int *visible_tab, char *plugin_name) +{ + gint32 *l_layers_list; + gint l_nlayers2; + gint32 l_idx; + + l_layers_list = gimp_image_get_layers(image_id, &l_nlayers2); + if(l_nlayers2 == nlayers) + { + for(l_idx = 0; l_idx < nlayers; l_idx++) + { + gimp_layer_set_visible(l_layers_list[l_idx], visible_tab[l_idx]); + if(gap_debug) printf("visibilty restore [%d] %d\n", (int)l_idx, (int)visible_tab[l_idx]); + } + p_gdisplays_update_full(image_id); + } + else + { + printf("Error: Plugin %s has changed Nr. of layers from %d to %d\ncould not restore Layer visibilty.\n", + plugin_name, (int)nlayers, (int)l_nlayers2); + } + + g_free (l_layers_list); +} + +static gint32 p_get_indexed_layerid(gint32 image_id, gint *nlayers, gint32 idx, char *plugin_name) +{ + gint32 *l_layers_list; + gint32 l_layer_id; + gint l_nlayers2; + + l_layers_list = gimp_image_get_layers(image_id, &l_nlayers2); + if(l_layers_list == NULL) + { + printf("Warning: cant get layers (maybe the image was closed)\n"); + return -1; + } + if((l_nlayers2 != *nlayers) && (*nlayers > 0)) + { + printf("Error: Plugin %s has changed Nr. of layers from %d to %d\nAnim Filter apply stopped.\n", + plugin_name, (int)*nlayers, (int)l_nlayers2); + return -1; + } + + *nlayers = l_nlayers2; + l_layer_id = l_layers_list[idx]; + g_free (l_layers_list); + return (l_layer_id); +} + +/* ============================================================================ + * p_foreach_multilayer + * apply the given plugin to each layer of the image. + * returns image_id of the new created multilayer image + * (or -1 on error) + * ============================================================================ + */ + +int p_foreach_multilayer(GRunModeType run_mode, gint32 image_id, + char *plugin_name, t_apply_mode apply_mode) +{ + static char l_key_from[512]; + static char l_key_to[512]; + char *l_plugin_iterator; + gint32 l_layer_id; + gint32 l_top_layer; + gint32 l_idx; + gint l_nlayers; + gdouble l_percentage, l_percentage_step; + GParam *l_params; + gint l_retvals; + int l_rc; + gint l_plugin_data_len; + long l_child_pid; + /* int l_status; */ + int *l_visible_tab; + char l_step_backup_file[120]; + gint l_pit_rc; + + l_rc = 0; + l_plugin_data_len = 0; + l_nlayers = 0; + l_visible_tab = NULL; + l_step_backup_file[0] = '\0'; + + /* check for the Plugin */ + + l_rc = p_procedure_available(plugin_name, PTYP_CAN_OPERATE_ON_DRAWABLE); + if(l_rc < 0) + { + fprintf(stderr, "ERROR: Plugin not available or wrong type %s\n", plugin_name); + return -1; + } + + + /* check for matching Iterator PluginProcedures */ + l_plugin_iterator = p_get_iterator_proc(plugin_name); + + l_percentage = 0.0; + if(run_mode == RUN_INTERACTIVE) + { + gimp_progress_init("Applying Filter to all Layers .."); + } + + l_layer_id = p_get_indexed_layerid(image_id, &l_nlayers, 0, plugin_name); + if(l_layer_id >= 0) + { + if(l_nlayers < 1) + { + fprintf(stderr, "ERROR: need at 1 Layers to apply plugin !\n"); + } + else + { + /* allocate a table to store the visibility attributes for each layer */ + l_visible_tab = (gint*) malloc((l_nlayers +1) * sizeof (gint)); + if(l_visible_tab == NULL) + return -1; + + /* save the visibility of all layers */ + for(l_idx = 0; l_idx < l_nlayers; l_idx++) + { + l_layer_id = p_get_indexed_layerid(image_id, &l_nlayers, l_idx, plugin_name); + l_visible_tab[l_idx] = gimp_layer_get_visible(l_layer_id); + + /* make the backround visible, all others invisible + * (so the user can see the effect of the 1.st applied _FROM filter) + */ + if(l_idx == (l_nlayers -1)) gimp_layer_set_visible(l_layer_id, TRUE); + else gimp_layer_set_visible(l_layer_id, FALSE); + } + p_gdisplays_update_full(image_id); + + l_percentage_step = 1.0 / l_nlayers; + + if((l_plugin_iterator != NULL) && (l_nlayers > 1) && (apply_mode == PTYP_VARYING_LINEAR )) + { + l_child_pid = 0; /* fork(); */ + if(l_child_pid < 0) + { + fprintf(stderr, "ERROR: fork failed !\n"); + return -1; + } + + /* if(l_child_pid != 0) */ + { + /* parent process: call plugin Interactive for background layer[n] */ + /* if(gap_debug) fprintf(stderr, "forked child process pid=%ld\n", l_child_pid); */ + if(gap_debug) fprintf(stderr, "DEBUG start 1.st Interactive call (_FROM values)\n"); + + l_idx = l_nlayers -1; + l_layer_id = p_get_indexed_layerid(image_id, &l_nlayers, l_idx, plugin_name); + if(l_layer_id < 0) + { + l_rc = -1; + } + else + { + if(gap_debug) fprintf(stderr, "DEBUG: apllying %s on Layerstack %d id=%d\n", plugin_name, (int)l_idx, (int)l_layer_id); + l_rc = p_call_plugin(plugin_name, image_id, l_layer_id, RUN_INTERACTIVE); + + /* get values, then store with suffix "_ITER_FROM" */ + l_plugin_data_len = p_get_data(plugin_name); + if(l_plugin_data_len > 0) + { + sprintf(l_key_from, "%s_ITER_FROM", plugin_name); + p_set_data(l_key_from, l_plugin_data_len); + } + else l_rc = -1; + + if(run_mode == RUN_INTERACTIVE) + { + l_percentage += l_percentage_step; + gimp_progress_update (l_percentage); + } + } + } + /* else */ + if((l_rc >= 0) && (l_nlayers > 1)) + { + /* child process: call plugin Interactive for top layer [0] */ + if(gap_debug) fprintf(stderr, "DEBUG start 2.nd Interactive call (_TO values)\n"); + + /* optional dialog between both calls (to see the effect of 1.call) */ + if(p_pitstop(run_mode, plugin_name, 0, + l_step_backup_file, sizeof(l_step_backup_file), 0 + ) + < 0) + { + if(gap_debug) fprintf(stderr, "TERMINATED: by pitstop dialog\n"); + /* restore the visibility of all layers */ + p_visibilty_restore(image_id, l_nlayers, l_visible_tab, plugin_name); + free(l_visible_tab); + return -1; + } + else + { + l_layer_id = p_get_indexed_layerid(image_id, &l_nlayers, 0, plugin_name); + if(l_layer_id < 0) + { + l_rc = -1; + } + else + { + /* make _TO layer visible */ + gimp_layer_set_visible(l_layer_id, TRUE); + p_gdisplays_update_full(image_id); + + if(gap_debug) fprintf(stderr, "DEBUG: apllying %s on Layerstack 0 id=%d\n", plugin_name, (int)l_layer_id); + l_rc = p_call_plugin(plugin_name, image_id, l_layer_id, RUN_INTERACTIVE); + + /* get values, then store with suffix "_ITER_TO" */ + l_plugin_data_len = p_get_data(plugin_name); + if(l_plugin_data_len > 0) + { + sprintf(l_key_to, "%s_ITER_TO", plugin_name); + p_set_data(l_key_to, l_plugin_data_len); + } + else l_rc = -1; + + if(run_mode == RUN_INTERACTIVE) + { + l_percentage += l_percentage_step; + gimp_progress_update (l_percentage); + } + + /* if(gap_debug) fprintf(stderr, "DEBUG child process exit %d\n", (int)l_rc); */ + /* exit(l_rc); */ /* end of childprocess */ + } + + } + + } + l_top_layer = 1; + + /* wait until exit of childprocess */ + /* waitpid(l_child_pid, &l_status, 0); */ + } + else + { + /* no iterator available, call plugin with constant values + * for each layer + */ + /* call plugin only ONCE Interactive for background layer[n] */ + l_layer_id = p_get_indexed_layerid(image_id, &l_nlayers, l_nlayers -1, plugin_name); + if(l_layer_id < 0) + { + l_rc = -1; + } + else + { + if(gap_debug) fprintf(stderr, "DEBUG: NO Varying, apllying %s on Layer id=%d\n", plugin_name, (int)l_layer_id); + l_rc = p_call_plugin(plugin_name, image_id, l_layer_id, RUN_INTERACTIVE); + l_top_layer = 0; + if(run_mode == RUN_INTERACTIVE) + { + l_percentage += l_percentage_step; + gimp_progress_update (l_percentage); + } + } + } + + if((l_rc >= 0) && (l_nlayers > 2)) + { + + /* call plugin foreach layer inbetween + * with runmode RUN_WITH_LAST_VALS + * and modify the last values + */ + l_pit_rc = 1; + for(l_idx = l_nlayers - 2; l_idx >= l_top_layer; l_idx--) + { + if(l_rc < 0) break; + + if(l_pit_rc > 0) /* last pit_rc was a skip, so ask again for the next layer */ + { + l_pit_rc = p_pitstop(run_mode, plugin_name, 1, + l_step_backup_file, sizeof(l_step_backup_file), + l_idx ); + } + if(l_pit_rc < 0) + { + if(gap_debug) fprintf(stderr, "TERMINATED: by pitstop dialog\n"); + l_rc = -1; + } + + l_layer_id = p_get_indexed_layerid(image_id, &l_nlayers, l_idx, plugin_name); + if(l_layer_id < 0) + { + l_rc = -1; + break; + } + + if(gap_debug) fprintf(stderr, "DEBUG: apllying %s on Layerstack %d id=%d\n", plugin_name, (int)l_idx, (int)l_layer_id); + + + if((l_plugin_iterator != NULL) && (apply_mode == PTYP_VARYING_LINEAR )) + { + /* call plugin-specific iterator, to modify + * the plugin's last_values + */ + if(gap_debug) fprintf(stderr, "DEBUG: calling iterator %s current step:%d\n", + l_plugin_iterator, (int)l_idx); + l_params = gimp_run_procedure (l_plugin_iterator, + &l_retvals, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_INT32, l_nlayers -1, /* total steps */ + PARAM_FLOAT, (gdouble)l_idx, /* current step */ + PARAM_INT32, l_plugin_data_len, /* length of stored data struct */ + PARAM_END); + if (l_params[0].data.d_status == FALSE) + { + fprintf(stderr, "ERROR: iterator %s failed\n", l_plugin_iterator); + l_rc = -1; + } + + g_free(l_params); + } + + if(l_rc < 0) break; + + if(l_pit_rc == 0) /* 0 == continue without further dialogs */ + { + /* call the plugin itself with runmode RUN_WITH_LAST_VALUES */ + l_rc = p_call_plugin(plugin_name, image_id, l_layer_id, RUN_WITH_LAST_VALS); + /* check if to save each step to backup file */ + if((l_step_backup_file[0] != '\0') && (l_step_backup_file[0] != ' ')) + { + printf("Saving image to backupfile:%s step = %d\n", + l_step_backup_file, (int)l_idx); + p_save_xcf(image_id, l_step_backup_file); + } + } + + if(run_mode == RUN_INTERACTIVE) + { + l_percentage += l_percentage_step; + gimp_progress_update (l_percentage); + } + + } /* end for */ + + } + + /* restore the visibility of all layers */ + p_visibilty_restore(image_id, l_nlayers, l_visible_tab, plugin_name); + free(l_visible_tab); + + } + + } + + + if(l_plugin_iterator != NULL) free(l_plugin_iterator); + + return l_rc; +} /* end p_foreach_multilayer */ + + + +/* ============================================================================ + * gap_proc_anim_apply + * ============================================================================ + */ + +gint gap_proc_anim_apply(GRunModeType run_mode, gint32 image_id, char *plugin_name) +{ + t_gap_db_browse_result l_browser_result; + t_apply_mode l_apply_mode; + + l_apply_mode = PAPP_CONSTANT; + g_current_image_id = image_id; + + if(run_mode == RUN_INTERACTIVE) + { + + if(gap_db_browser_dialog("Select Filter for Animated apply", + "Apply Constant", + "Apply Varying", + p_constraint_proc, + p_constraint_proc_sel1, + p_constraint_proc_sel2, + &l_browser_result, + TRUE /* call gtk_init */ + ) + < 0) + { + if(gap_debug) fprintf(stderr, "DEBUG: gap_db_browser_dialog cancelled\n"); + return -1; + } + + p_arr_gtk_init(FALSE); /* disable the initial gtk_init in gap_arr_dialog's + * (gtk_init was done by the browser dialog) + */ + + strcpy(plugin_name, l_browser_result.selected_proc_name); + if(l_browser_result.button_nr == 1) l_apply_mode = PTYP_VARYING_LINEAR; + + if(gap_debug) fprintf(stderr, "DEBUG: gap_db_browser_dialog SELECTED:%s\n", plugin_name); + + } + + return(p_foreach_multilayer(run_mode, + image_id, + plugin_name, + l_apply_mode )); + +} + diff --git a/plug-ins/gap/gap_filter_iterators.c b/plug-ins/gap/gap_filter_iterators.c new file mode 100644 index 0000000000..0c3546f99a --- /dev/null +++ b/plug-ins/gap/gap_filter_iterators.c @@ -0,0 +1,678 @@ +/* gap_filter_iterators.c + * + * 1998.01.29 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * - Implementation of XXX_Iterator_ALT Procedures + * for those Plugins of the gimp.0.99.17 release + * - that can operate on a single drawable, + * - and have paramters for varying. + * + * for now i made some Iterator Plugins using the ending _ALT, + * If New plugins were added to the gimp, or existing ones were updated, + * the Authors should supply original _Iterator Procedures + * (without the _ALT ending) + * This Procedures are then used instead of my (Hacked _ALT) versions. + * to modify the settings for the plugin when called step by step + * on animated multilayer Images. + * without name conflicts. + * + * The 2.nd section of this file was generated by gap_filter_codegen.c:p_gen_code_iter_ALT + * using the PDB at version gimp 0.99.18 as base. + * Unforunately, some of the plugins are using datastructures + * to store their "Last Values" + * that are differnt from its calling parameters (as described in the PDB) + * Therfore I had to adjust (edit by hand) the generted structures to fit the + * plugins internal settings. + * + * Common things to all Iteratur Plugins: + * Interface: run_mode # is always RUN_NONINTERACTIVE + * total_steps # total number of handled layers (drawables) + * current_step # current layer (beginning wit 0) + * has type gdouble for later extensions + * to non-linear iterations. + * the iterator always computes linear inbetween steps, + * but the (central) caller may fake step 1.2345 in the future + * for logaritmic iterations or userdefined curves. + * + * Naming Convention: + * Iterators must have the name of the plugin (PDB proc_name), whose values + * are iterated, with Suffix + * "_Iterator" or + * "_Iterator_ALT" (if not provided within the original Plugin's sources) + * + */ + +/* 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. + */ + +/* Change Log: + * 1999.03.14 hof: added iterators for gimp 1.1.3 prerelease + * iterator code reorganized in _iter_ALT.inc Files + * 1998.06.12 hof: added p_delta_drawable (Iterate layers in the layerstack) + * this enables to apply an animated bumpmap. + * 1998.01.29 hof: 1st release + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_filter.h" +#include "gap_filter_iterators.h" + + +static char g_plugin_data_from[PLUGIN_DATA_SIZE + 1]; +static char g_plugin_data_to[PLUGIN_DATA_SIZE + 1]; + + + + +extern int gap_debug; + +typedef struct { + guchar color[3]; +} t_color; + +typedef struct { + gint color[3]; +} t_gint_color; + + +/* ---------------------------------------------------------------------- + * iterator functions for basic datatypes + * (were called from the generated procedures) + * ---------------------------------------------------------------------- + */ + +static void p_delta_long(long *val, long val_from, long val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; + + if(gap_debug) fprintf(stderr, "DEBUG: p_delta_long from: %ld to: %ld curr: %ld delta: %f\n", + val_from, val_to, *val, delta); +} +static void p_delta_short(short *val, short val_from, short val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; +} +static void p_delta_gint(gint *val, gint val_from, gint val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; +} +static void p_delta_char(char *val, char val_from, char val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; +} +static void p_delta_guchar(guchar *val, char val_from, char val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; +} +static void p_delta_gdouble(double *val, double val_from, double val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; + + if(gap_debug) fprintf(stderr, "DEBUG: p_delta_gdouble total: %d from: %f to: %f curr: %f delta: %f\n", + (int)total_steps, val_from, val_to, *val, delta); +} +static void p_delta_float(float *val, float val_from, float val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + + if(total_steps < 1) return; + + delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step); + *val = val_from + delta; + + if(gap_debug) fprintf(stderr, "DEBUG: p_delta_gdouble total: %d from: %f to: %f curr: %f delta: %f\n", + (int)total_steps, val_from, val_to, *val, delta); +} +static void p_delta_color(t_color *val, t_color *val_from, t_color *val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + int l_idx; + + if(total_steps < 1) return; + + for(l_idx = 0; l_idx < 3; l_idx++) + { + delta = ((double)(val_to->color[l_idx] - val_from->color[l_idx]) / (double)total_steps) * ((double)total_steps - current_step); + val->color[l_idx] = val_from->color[l_idx] + delta; + } +} +static void p_delta_gint_color(t_gint_color *val, t_gint_color *val_from, t_gint_color *val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + int l_idx; + + if(total_steps < 1) return; + + for(l_idx = 0; l_idx < 3; l_idx++) + { + delta = ((double)(val_to->color[l_idx] - val_from->color[l_idx]) / (double)total_steps) * ((double)total_steps - current_step); + val->color[l_idx] = val_from->color[l_idx] + delta; + } +} +static void p_delta_drawable(gint32 *val, gint32 val_from, gint32 val_to, gint32 total_steps, gdouble current_step) +{ + gint l_nlayers; + gint32 *l_layers_list; + gint32 l_tmp_image_id; + gint l_idx, l_idx_from, l_idx_to; + + if((val_from < 0) || (val_to < 0)) + { + return; + } + + l_tmp_image_id = gimp_drawable_image_id(val_from); + + /* check if from and to values are both valid drawables within the same image */ + if ((l_tmp_image_id > 0) + && (l_tmp_image_id = gimp_drawable_image_id(val_to))) + { + l_idx_from = -1; + l_idx_to = -1; + + /* check the layerstack index of from and to drawable */ + l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers); + for (l_idx = l_nlayers -1; l_idx >= 0; l_idx--) + { + if( l_layers_list[l_idx] == val_from ) l_idx_from = l_idx; + if( l_layers_list[l_idx] == val_to ) l_idx_to = l_idx; + + if((l_idx_from != -1) && (l_idx_to != -1)) + { + /* OK found both index values, iterate the index (proceed to next layer) */ + p_delta_gint(&l_idx, l_idx_from, l_idx_to, total_steps, current_step); + *val = l_layers_list[l_idx]; + break; + } + } + g_free (l_layers_list); + } +} + + +/* ---------------------------------------------------------------------- + * iterator UTILITIES for Gck Vectors, Material and Light Sewttings + * ---------------------------------------------------------------------- + */ + + typedef struct + { + double color[4]; /* r,g,b,a */ + } t_GckRGB; + typedef struct + { + double coord[3]; /* x,y,z; */ + } t_GckVector3; + + typedef enum { + POINT_LIGHT, + DIRECTIONAL_LIGHT, + SPOT_LIGHT, + NO_LIGHT + } t_LightType; + + typedef enum { + IMAGE_BUMP, + WAVES_BUMP + } t_MapType; + + typedef struct + { + gdouble ambient_int; + gdouble diffuse_int; + gdouble diffuse_ref; + gdouble specular_ref; + gdouble highlight; + t_GckRGB color; + } t_MaterialSettings; + + typedef struct + { + t_LightType type; + t_GckVector3 position; + t_GckVector3 direction; + t_GckRGB color; + gdouble intensity; + } t_LightSettings; + + +static void p_delta_GckRGB(t_GckRGB *val, t_GckRGB *val_from, t_GckRGB *val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + int l_idx; + + if(total_steps < 1) return; + + for(l_idx = 0; l_idx < 4; l_idx++) + { + delta = ((double)(val_to->color[l_idx] - val_from->color[l_idx]) / (double)total_steps) * ((double)total_steps - current_step); + val->color[l_idx] = val_from->color[l_idx] + delta; + } +} +static void p_delta_GckVector3(t_GckVector3 *val, t_GckVector3 *val_from, t_GckVector3 *val_to, gint32 total_steps, gdouble current_step) +{ + double delta; + int l_idx; + + if(total_steps < 1) return; + + for(l_idx = 0; l_idx < 3; l_idx++) + { + delta = ((double)(val_to->coord[l_idx] - val_from->coord[l_idx]) / (double)total_steps) * ((double)total_steps - current_step); + val->coord[l_idx] = val_from->coord[l_idx] + delta; + } +} + +static void p_delta_MaterialSettings(t_MaterialSettings *val, t_MaterialSettings *val_from, t_MaterialSettings *val_to, gint32 total_steps, gdouble current_step) +{ + p_delta_gdouble(&val->ambient_int, val_from->ambient_int, val_to->ambient_int, total_steps, current_step); + p_delta_gdouble(&val->diffuse_int, val_from->diffuse_int, val_to->diffuse_int, total_steps, current_step); + p_delta_gdouble(&val->diffuse_ref, val_from->diffuse_ref, val_to->diffuse_ref, total_steps, current_step); + p_delta_gdouble(&val->specular_ref, val_from->specular_ref, val_to->specular_ref, total_steps, current_step); + p_delta_gdouble(&val->highlight, val_from->highlight, val_to->highlight, total_steps, current_step); + p_delta_GckRGB(&val->color, &val_from->color, &val_to->color, total_steps, current_step); + +} + +static void p_delta_LightSettings(t_LightSettings *val, t_LightSettings *val_from, t_LightSettings *val_to, gint32 total_steps, gdouble current_step) +{ + /* no delta is done for LightType */ + p_delta_GckVector3(&val->position, &val_from->position, &val_to->position, total_steps, current_step); + p_delta_GckVector3(&val->direction, &val_from->direction, &val_to->direction, total_steps, current_step); + p_delta_GckRGB(&val->color, &val_from->color, &val_to->color, total_steps, current_step); + p_delta_gdouble(&val->intensity, val_from->intensity, val_to->intensity, total_steps, current_step); +} + + + +/* ---------------------------------------- 2.nd Section + * ---------------------------------------- + * INCLUDE the generated p_XXX_iter_ALT procedures + * ---------------------------------------- + * ---------------------------------------- + */ + +#include "iter_ALT/mod/plug_in_Twist_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_alienmap_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_applylens_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_blur_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_depth_merge_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_despeckle_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_emboss_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_exchange_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_lighting_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_map_object_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_maze_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_nlfilt_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_nova_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_oilify_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_pagecurl_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_plasma_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_polar_coords_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_sample_colorize_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_sinus_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_solid_noise_iter_ALT.inc" +#include "iter_ALT/mod/plug_in_sparkle_iter_ALT.inc" + +#include "iter_ALT/old/Colorify_iter_ALT.inc" +#include "iter_ALT/old/plug_in_CentralReflection_iter_ALT.inc" +#include "iter_ALT/old/plug_in_anamorphose_iter_ALT.inc" +#include "iter_ALT/old/plug_in_blur2_iter_ALT.inc" +#include "iter_ALT/old/plug_in_encript_iter_ALT.inc" +#include "iter_ALT/old/plug_in_figures_iter_ALT.inc" +#include "iter_ALT/old/plug_in_gflare_iter_ALT.inc" +#include "iter_ALT/old/plug_in_holes_iter_ALT.inc" +#include "iter_ALT/old/plug_in_julia_iter_ALT.inc" +#include "iter_ALT/old/plug_in_magic_eye_iter_ALT.inc" +#include "iter_ALT/old/plug_in_mandelbrot_iter_ALT.inc" +#include "iter_ALT/old/plug_in_randomize_iter_ALT.inc" +#include "iter_ALT/old/plug_in_refract_iter_ALT.inc" +#include "iter_ALT/old/plug_in_struc_iter_ALT.inc" +#include "iter_ALT/old/plug_in_tileit_iter_ALT.inc" +#include "iter_ALT/old/plug_in_universal_filter_iter_ALT.inc" +#include "iter_ALT/old/plug_in_warp_iter_ALT.inc" + + +#include "iter_ALT/gen/plug_in_CML_explorer_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_alpha2color_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_blinds_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_borderaverage_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_bump_map_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_checkerboard_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_color_map_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_colorify_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_convmatrix_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_cubism_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_destripe_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_diffraction_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_displace_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_edge_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_engrave_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_flame_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_flarefx_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_fractal_trace_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_gauss_iir_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_gauss_rle_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_gfig_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_glasstile_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_grid_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_jigsaw_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_make_seamless_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_mblur_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_mosaic_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_newsprint_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_noisify_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_paper_tile_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_pixelize_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_randomize_hurl_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_randomize_pick_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_randomize_slur_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_ripple_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_rotate_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_scatter_hsv_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_sharpen_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_shift_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_spread_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_video_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_vpropagate_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_waves_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_whirl_pinch_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_wind_iter_ALT.inc" +#include "iter_ALT/gen/plug_in_zealouscrop_iter_ALT.inc" + +/* table of proc_names and funtion pointers to iter_ALT procedures */ +/* del ... Deleted (does not make sense to animate) + * + ... generated code did not work (changed manually) + */ +static t_iter_ALT_tab g_iter_ALT_tab[] = +{ + { "Colorify", p_Colorify_iter_ALT } +/*, { "perl_fu_blowinout", p_perl_fu_blowinout_iter_ALT } */ +/*, { "perl_fu_feedback", p_perl_fu_feedback_iter_ALT } */ +/*, { "perl_fu_prep4gif", p_perl_fu_prep4gif_iter_ALT } */ +/*, { "perl_fu_scratches", p_perl_fu_scratches_iter_ALT } */ +/*, { "perl_fu_terraltext", p_perl_fu_terraltext_iter_ALT } */ +/*, { "perl_fu_tex_string_to_float", p_perl_fu_tex_string_to_float_iter_ALT } */ +/*, { "perl_fu_webify", p_perl_fu_webify_iter_ALT } */ +/*, { "perl_fu_windify", p_perl_fu_windify_iter_ALT } */ +/*, { "perl_fu_xach_blocks", p_perl_fu_xach_blocks_iter_ALT } */ +/*, { "perl_fu_xach_shadows", p_perl_fu_xach_shadows_iter_ALT } */ +/*, { "perl_fu_xachvision", p_perl_fu_xachvision_iter_ALT } */ + , { "plug_in_CML_explorer", p_plug_in_CML_explorer_iter_ALT } + , { "plug_in_CentralReflection", p_plug_in_CentralReflection_iter_ALT } + , { "plug_in_Twist", p_plug_in_Twist_iter_ALT } + , { "plug_in_alienmap", p_plug_in_alienmap_iter_ALT } +/*, { "plug_in_align_layers", p_plug_in_align_layers_iter_ALT } */ + , { "plug_in_alpha2color", p_plug_in_alpha2color_iter_ALT } + , { "plug_in_anamorphose", p_plug_in_anamorphose_iter_ALT } +/*, { "plug_in_animationoptimize", p_plug_in_animationoptimize_iter_ALT } */ +/*, { "plug_in_animationplay", p_plug_in_animationplay_iter_ALT } */ +/*, { "plug_in_animationunoptimize", p_plug_in_animationunoptimize_iter_ALT } */ +/*, { "plug_in_apply_canvas", p_plug_in_apply_canvas_iter_ALT } */ + , { "plug_in_applylens", p_plug_in_applylens_iter_ALT } +/*, { "plug_in_autocrop", p_plug_in_autocrop_iter_ALT } */ +/*, { "plug_in_autostretch_hsv", p_plug_in_autostretch_hsv_iter_ALT } */ + , { "plug_in_blinds", p_plug_in_blinds_iter_ALT } + , { "plug_in_blur", p_plug_in_blur_iter_ALT } + , { "plug_in_blur2", p_plug_in_blur2_iter_ALT } +/*, { "plug_in_blur_randomize", p_plug_in_blur_randomize_iter_ALT } */ + , { "plug_in_borderaverage", p_plug_in_borderaverage_iter_ALT } + , { "plug_in_bump_map", p_plug_in_bump_map_iter_ALT } +/*, { "plug_in_c_astretch", p_plug_in_c_astretch_iter_ALT } */ + , { "plug_in_checkerboard", p_plug_in_checkerboard_iter_ALT } +/*, { "plug_in_color_adjust", p_plug_in_color_adjust_iter_ALT } */ + , { "plug_in_color_map", p_plug_in_color_map_iter_ALT } +/*, { "plug_in_colorify", p_plug_in_colorify_iter_ALT } */ +/*, { "plug_in_compose", p_plug_in_compose_iter_ALT } */ + , { "plug_in_convmatrix", p_plug_in_convmatrix_iter_ALT } + , { "plug_in_cubism", p_plug_in_cubism_iter_ALT } +/*, { "plug_in_decompose", p_plug_in_decompose_iter_ALT } */ +/*, { "plug_in_deinterlace", p_plug_in_deinterlace_iter_ALT } */ + , { "plug_in_depth_merge", p_plug_in_depth_merge_iter_ALT } + , { "plug_in_despeckle", p_plug_in_despeckle_iter_ALT } + , { "plug_in_destripe", p_plug_in_destripe_iter_ALT } + , { "plug_in_diffraction", p_plug_in_diffraction_iter_ALT } + , { "plug_in_displace", p_plug_in_displace_iter_ALT } +/*, { "plug_in_ditherize", p_plug_in_ditherize_iter_ALT } */ + , { "plug_in_edge", p_plug_in_edge_iter_ALT } + , { "plug_in_emboss", p_plug_in_emboss_iter_ALT } + , { "plug_in_encript", p_plug_in_encript_iter_ALT } + , { "plug_in_engrave", p_plug_in_engrave_iter_ALT } + , { "plug_in_exchange", p_plug_in_exchange_iter_ALT } +/*, { "plug_in_export_palette", p_plug_in_export_palette_iter_ALT } */ + , { "plug_in_figures", p_plug_in_figures_iter_ALT } +/*, { "plug_in_film", p_plug_in_film_iter_ALT } */ +/*, { "plug_in_filter_pack", p_plug_in_filter_pack_iter_ALT } */ + , { "plug_in_flame", p_plug_in_flame_iter_ALT } + , { "plug_in_flarefx", p_plug_in_flarefx_iter_ALT } + , { "plug_in_fractal_trace", p_plug_in_fractal_trace_iter_ALT } + , { "plug_in_gauss_iir", p_plug_in_gauss_iir_iter_ALT } + , { "plug_in_gauss_rle", p_plug_in_gauss_rle_iter_ALT } + , { "plug_in_gfig", p_plug_in_gfig_iter_ALT } + , { "plug_in_gflare", p_plug_in_gflare_iter_ALT } + , { "plug_in_glasstile", p_plug_in_glasstile_iter_ALT } +/*, { "plug_in_gradmap", p_plug_in_gradmap_iter_ALT } */ + , { "plug_in_grid", p_plug_in_grid_iter_ALT } +/*, { "plug_in_guillotine", p_plug_in_guillotine_iter_ALT } */ + , { "plug_in_holes", p_plug_in_holes_iter_ALT } +/*, { "plug_in_hot", p_plug_in_hot_iter_ALT } */ +/*, { "plug_in_ifs_compose", p_plug_in_ifs_compose_iter_ALT } */ +/*, { "plug_in_illusion", p_plug_in_illusion_iter_ALT } */ +/*, { "plug_in_image_rot270", p_plug_in_image_rot270_iter_ALT } */ +/*, { "plug_in_image_rot90", p_plug_in_image_rot90_iter_ALT } */ +/*, { "plug_in_iwarp", p_plug_in_iwarp_iter_ALT } */ + , { "plug_in_jigsaw", p_plug_in_jigsaw_iter_ALT } + , { "plug_in_julia", p_plug_in_julia_iter_ALT } +/*, { "plug_in_laplace", p_plug_in_laplace_iter_ALT } */ +/*, { "plug_in_layer_rot270", p_plug_in_layer_rot270_iter_ALT } */ +/*, { "plug_in_layer_rot90", p_plug_in_layer_rot90_iter_ALT } */ +/*, { "plug_in_layers_import", p_plug_in_layers_import_iter_ALT } */ +/*, { "plug_in_lic", p_plug_in_lic_iter_ALT } */ + , { "plug_in_lighting", p_plug_in_lighting_iter_ALT } + , { "plug_in_magic_eye", p_plug_in_magic_eye_iter_ALT } +/*, { "plug_in_mail_image", p_plug_in_mail_image_iter_ALT } */ + , { "plug_in_make_seamless", p_plug_in_make_seamless_iter_ALT } + , { "plug_in_mandelbrot", p_plug_in_mandelbrot_iter_ALT } + , { "plug_in_map_object", p_plug_in_map_object_iter_ALT } +/*, { "plug_in_max_rgb", p_plug_in_max_rgb_iter_ALT } */ + , { "plug_in_maze", p_plug_in_maze_iter_ALT } + , { "plug_in_mblur", p_plug_in_mblur_iter_ALT } + , { "plug_in_mosaic", p_plug_in_mosaic_iter_ALT } + , { "plug_in_newsprint", p_plug_in_newsprint_iter_ALT } + , { "plug_in_nlfilt", p_plug_in_nlfilt_iter_ALT } + , { "plug_in_noisify", p_plug_in_noisify_iter_ALT } +/*, { "plug_in_normalize", p_plug_in_normalize_iter_ALT } */ + , { "plug_in_nova", p_plug_in_nova_iter_ALT } + , { "plug_in_oilify", p_plug_in_oilify_iter_ALT } + , { "plug_in_pagecurl", p_plug_in_pagecurl_iter_ALT } + , { "plug_in_paper_tile", p_plug_in_paper_tile_iter_ALT } + , { "plug_in_pixelize", p_plug_in_pixelize_iter_ALT } + , { "plug_in_plasma", p_plug_in_plasma_iter_ALT } + , { "plug_in_polar_coords", p_plug_in_polar_coords_iter_ALT } +/*, { "plug_in_qbist", p_plug_in_qbist_iter_ALT } */ + , { "plug_in_randomize", p_plug_in_randomize_iter_ALT } + , { "plug_in_randomize_hurl", p_plug_in_randomize_hurl_iter_ALT } + , { "plug_in_randomize_pick", p_plug_in_randomize_pick_iter_ALT } + , { "plug_in_randomize_slur", p_plug_in_randomize_slur_iter_ALT } + , { "plug_in_refract", p_plug_in_refract_iter_ALT } + , { "plug_in_ripple", p_plug_in_ripple_iter_ALT } + , { "plug_in_rotate", p_plug_in_rotate_iter_ALT } + , { "plug_in_sample_colorize", p_plug_in_sample_colorize_iter_ALT } + , { "plug_in_scatter_hsv", p_plug_in_scatter_hsv_iter_ALT } +/*, { "plug_in_semiflatten", p_plug_in_semiflatten_iter_ALT } */ + , { "plug_in_sharpen", p_plug_in_sharpen_iter_ALT } + , { "plug_in_shift", p_plug_in_shift_iter_ALT } + , { "plug_in_sinus", p_plug_in_sinus_iter_ALT } +/*, { "plug_in_small_tiles", p_plug_in_small_tiles_iter_ALT } */ +/*, { "plug_in_smooth_palette", p_plug_in_smooth_palette_iter_ALT } */ +/*, { "plug_in_sobel", p_plug_in_sobel_iter_ALT } */ + , { "plug_in_solid_noise", p_plug_in_solid_noise_iter_ALT } + , { "plug_in_sparkle", p_plug_in_sparkle_iter_ALT } + , { "plug_in_spread", p_plug_in_spread_iter_ALT } + , { "plug_in_struc", p_plug_in_struc_iter_ALT } +/*, { "plug_in_the_egg", p_plug_in_the_egg_iter_ALT } */ +/*, { "plug_in_threshold_alpha", p_plug_in_threshold_alpha_iter_ALT } */ +/*, { "plug_in_tile", p_plug_in_tile_iter_ALT } */ + , { "plug_in_tileit", p_plug_in_tileit_iter_ALT } + , { "plug_in_universal_filter", p_plug_in_universal_filter_iter_ALT } + , { "plug_in_video", p_plug_in_video_iter_ALT } +/*, { "plug_in_vinvert", p_plug_in_vinvert_iter_ALT } */ + , { "plug_in_vpropagate", p_plug_in_vpropagate_iter_ALT } + , { "plug_in_warp", p_plug_in_warp_iter_ALT } + , { "plug_in_waves", p_plug_in_waves_iter_ALT } + , { "plug_in_whirl_pinch", p_plug_in_whirl_pinch_iter_ALT } + , { "plug_in_wind", p_plug_in_wind_iter_ALT } +/*, { "plug_in_zealouscrop", p_plug_in_zealouscrop_iter_ALT } */ +}; /* end g_iter_ALT_tab */ + + +#define MAX_ITER_ALT ( sizeof(g_iter_ALT_tab) / sizeof(t_iter_ALT_tab) ) + + +/* ---------------------------------------------------------------------- + * install (query) iterators_ALT + * ---------------------------------------------------------------------- + */ + +static void p_install_proc_iter_ALT(char *name) +{ + char l_iter_proc_name[256]; + char l_blurb_text[300]; + + static GParamDef args_iter[] = + { + {PARAM_INT32, "run_mode", "non-interactive"}, + {PARAM_INT32, "total_steps", "total number of steps (# of layers-1 to apply the related plug-in)"}, + {PARAM_FLOAT, "current_step", "current (for linear iterations this is the layerstack position, otherwise some value inbetween)"}, + {PARAM_INT32, "len_struct", "length of stored data structure with id is equal to the plug_in proc_name"}, + }; + static int nargs_iter = sizeof(args_iter) / sizeof(args_iter[0]); + + static GParamDef *return_vals = NULL; + static int nreturn_vals = 0; + + sprintf(l_iter_proc_name, "%s_Iterator_ALT", name); + sprintf(l_blurb_text, "This extension calculates the modified values for one iterationstep for the call of %s", name); + + gimp_install_procedure(l_iter_proc_name, + l_blurb_text, + "", + "Wolfgang Hofer", + "Wolfgang Hofer", + "Dec. 1997", + NULL, /* do not appear in menus */ + NULL, + PROC_EXTENSION, + nargs_iter, nreturn_vals, + args_iter, return_vals); + +} + +void gap_query_iterators_ALT() +{ + int l_idx; + for(l_idx = 0; l_idx < MAX_ITER_ALT; l_idx++) + { + p_install_proc_iter_ALT (g_iter_ALT_tab[l_idx].proc_name); + } +} + +/* ---------------------------------------------------------------------- + * run iterators_ALT + * ---------------------------------------------------------------------- + */ + +gint gap_run_iterators_ALT(char *name, GRunModeType run_mode, gint32 total_steps, gdouble current_step, gint32 len_struct) +{ + gint l_rc; + int l_idx; + char *l_name; + int l_cut; + + l_name = g_strdup(name); + l_cut = strlen(l_name) - strlen("_Iterator_ALT"); + if(l_cut < 1) + { + fprintf(stderr, "ERROR: gap_run_iterators_ALT: proc_name ending _Iterator_ALT missing%s\n", name); + return -1; + } + if(strcmp(&l_name[l_cut], "_Iterator_ALT") != 0) + { + fprintf(stderr, "ERROR: gap_run_iterators_ALT: proc_name ending _Iterator_ALT missing%s\n", name); + return -1; + } + + + l_name[l_cut] = '\0'; /* cut off "_Iterator_ALT" from l_name end */ + l_rc = -1; + for(l_idx = 0; l_idx < MAX_ITER_ALT; l_idx++) + { + if (strcmp (l_name, g_iter_ALT_tab[l_idx].proc_name) == 0) + { + if(gap_debug) fprintf(stderr, "DEBUG: gap_run_iterators_ALT: FOUND %s\n", l_name); + l_rc = (g_iter_ALT_tab[l_idx].proc_func)(run_mode, total_steps, current_step, len_struct); + } + } + + if(l_rc < 0) fprintf(stderr, "ERROR: gap_run_iterators_ALT: NOT FOUND proc_name=%s (%s)\n", name, l_name); + + return l_rc; +} diff --git a/plug-ins/gap/gap_filter_iterators.h b/plug-ins/gap/gap_filter_iterators.h new file mode 100644 index 0000000000..8e4aff439c --- /dev/null +++ b/plug-ins/gap/gap_filter_iterators.h @@ -0,0 +1,50 @@ +/* gap_filter_iterators.h + * + * 1997.12.18 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * - Headers for XXX_Iterator_ALT Procedures + * + * for now i made some Iterator Plugins using the ending _ALT, + * If New plugins were added to the gimp, or existing ones were updated, + * the Authors should supply original _Iterator Procedures + * (without the _ALT ending) + * This Procedures are then used instead of my (Hacked _ALT) versions. + * to modify the settings for the plugin when called step by step + * on animated multilayer Images. + * without name conflicts. + */ + +/* 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. + */ + +#ifndef _GAP_FILTER_ITERATORS_H +#define _GAP_FILTER_ITERATORS_H + +typedef gint (*t_iter_ALT_func) (GRunModeType run_mode, gint32 total_steps, gdouble current_step, gint32 len_struct); + + +typedef struct t_iter_ALT_tab +{ + char *proc_name; + t_iter_ALT_func proc_func; +} t_iter_ALT_tab; + +#endif diff --git a/plug-ins/gap/gap_filter_main.c b/plug-ins/gap/gap_filter_main.c new file mode 100644 index 0000000000..9782308866 --- /dev/null +++ b/plug-ins/gap/gap_filter_main.c @@ -0,0 +1,205 @@ +/* gap_filter_main.c + * 1997.12.18 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * - MAIN of GAP_filter foreach: call any Filter (==Plugin Proc) + * with varying settings for all + * layers within one Image. + * - query registration of gap_foreach Procedure + * and for all Iterator_ALT Procedures + * - run invoke the gap_foreach procedure by its PDB name + * + * + * + */ +/* 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. + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_filter.h" + +static char *gap_filter_version = "0.92.00; 1998/01/16"; + +/* revision history: + * version 0.92.00 hof: set gap_debug from environment + * version 0.91.01; Tue Dec 23 hof: 1.st (pre) release + */ + +/* ------------------------ + * global gap DEBUG switch + * ------------------------ + */ + +/* int gap_debug = 1; */ /* print debug infos */ +/* int gap_debug = 0; */ /* 0: dont print debug infos */ + +int gap_debug = 0; + + + +static void query(void); +static void run(char *name, int nparam, GParam *param, + int *nretvals, GParam **retvals); + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +MAIN () + +static void +query () +{ + static GParamDef args_foreach[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_STRING, "proc_name", "name of plugin procedure to run for each layer)"}, + }; + static int nargs_foreach = sizeof(args_foreach) / sizeof(args_foreach[0]); + + static GParamDef *return_vals = NULL; + static int nreturn_vals = 0; + + gimp_install_procedure("plug_in_gap_layers_run_animfilter", + "This plugin calls another plugin for each layer of an image, varying its settings (to produce animated effects). The called plugin must work on a single drawable and must be able to RUN_WITH_LAST_VALS", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_filter_version, + "/Filters/Animation/Filter all Layers", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_foreach, nreturn_vals, + args_foreach, return_vals); + + /* ------------------ ALTernative Iterators ------------------------------ */ + + gap_query_iterators_ALT(); + + +} /* end query */ + + +static void +run (char *name, + int n_params, + GParam *param, + int *nreturn_vals, + GParam **return_vals) +{ +#define MAX_PLUGIN_NAME_LEN 256 + + char l_plugin_name[MAX_PLUGIN_NAME_LEN]; + static GParam values[1]; + GRunModeType run_mode; + GStatusType status = STATUS_SUCCESS; + gint32 image_id; + gint32 len_struct; + gint32 total_steps; + gdouble current_step; + + gint32 l_rc; + char *l_env; + + *nreturn_vals = 1; + *return_vals = values; + l_rc = 0; + + l_env = getenv("GAP_DEBUG"); + if(l_env != NULL) + { + if((*l_env != 'n') && (*l_env != 'N')) gap_debug = 1; + } + + + run_mode = param[0].data.d_int32; + + + if(gap_debug) fprintf(stderr, "\n\ngap_filter_main: debug name = %s\n", name); + + if (strcmp (name, "plug_in_gap_layers_run_animfilter") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 4) + { + status = STATUS_CALLING_ERROR; + } + else + { + strncpy(l_plugin_name, param[3].data.d_string, MAX_PLUGIN_NAME_LEN -1); + l_plugin_name[MAX_PLUGIN_NAME_LEN -1] = '\0'; + } + } + else if(run_mode == RUN_WITH_LAST_VALS) + { + /* probably get last values (name of last plugin) */ + gimp_get_data("plug_in_gap_layers_run_animfilter", l_plugin_name); + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + + l_rc = gap_proc_anim_apply(run_mode, image_id, l_plugin_name); + gimp_set_data("plug_in_gap_layers_run_animfilter", l_plugin_name, sizeof(l_plugin_name)); + } + } + else + { + if ((run_mode == RUN_NONINTERACTIVE) && (n_params == 4)) + { + total_steps = param[1].data.d_int32; + current_step = param[2].data.d_float; + len_struct = param[3].data.d_int32; + l_rc = gap_run_iterators_ALT(name, run_mode, total_steps, current_step, len_struct); + } + else status = STATUS_CALLING_ERROR; + } + + if(l_rc < 0) + { + status = STATUS_EXECUTION_ERROR; + } + + + if (run_mode != RUN_NONINTERACTIVE) + gimp_displays_flush(); + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; + +} diff --git a/plug-ins/gap/gap_filter_pdb.c b/plug-ins/gap/gap_filter_pdb.c new file mode 100644 index 0000000000..4e22e8b070 --- /dev/null +++ b/plug-ins/gap/gap_filter_pdb.c @@ -0,0 +1,489 @@ +/* gap_filter_pdb.c + * 1998.10.14 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * - GAP_filter pdb: functions for calling any Filter (==Plugin Proc) + * that operates on a drawable + * + */ +/* 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: + * version 0.97.00 hof: - created module (as extract gap_filter_foreach) + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_arr_dialog.h" +#include "gap_filter.h" +#include "gap_filter_pdb.h" +#include "gap_lib.h" + + +/* ------------------------ + * global gap DEBUG switch + * ------------------------ + */ + +/* int gap_debug = 1; */ /* print debug infos */ +/* int gap_debug = 0; */ /* 0: dont print debug infos */ + +extern int gap_debug; + +static char g_plugin_data[PLUGIN_DATA_SIZE + 1]; + +static gint32 g_current_image_id; + + + +gint p_call_plugin(char *plugin_name, gint32 image_id, gint32 layer_id, GRunModeType run_mode) +{ + GDrawable *l_drawable; + GParam *l_ret_params; + GParam *l_argv; + gint l_retvals; + int l_idx; + + int l_nparams; + int l_nreturn_vals; + int l_proc_type; + char *l_proc_blurb; + char *l_proc_help; + char *l_proc_author; + char *l_proc_copyright; + char *l_proc_date; + GParamDef *l_params; + GParamDef *l_return_vals; + + + l_drawable = gimp_drawable_get(layer_id); /* use the background layer */ + + /* query for plugin_name to get its argument types */ + if (FALSE == gimp_query_procedure (plugin_name, + &l_proc_blurb, + &l_proc_help, + &l_proc_author, + &l_proc_copyright, + &l_proc_date, + &l_proc_type, + &l_nparams, + &l_nreturn_vals, + &l_params, + &l_return_vals)) + { + fprintf(stderr, "ERROR: Plugin not available, Name was %s\n", plugin_name); + return -1; + } + + /* construct the procedures arguments */ + l_argv = g_new (GParam, l_nparams); + memset (l_argv, 0, (sizeof (GParam) * l_nparams)); + + /* initialize the argument types */ + for (l_idx = 0; l_idx < l_nparams; l_idx++) + { + l_argv[l_idx].type = l_params[l_idx].type; + switch(l_params[l_idx].type) + { + case PARAM_DISPLAY: + l_argv[l_idx].data.d_display = -1; + break; + case PARAM_DRAWABLE: + case PARAM_LAYER: + case PARAM_CHANNEL: + l_argv[l_idx].data.d_drawable = -1; + break; + case PARAM_IMAGE: + l_argv[l_idx].data.d_image = -1; + break; + case PARAM_INT32: + case PARAM_INT16: + case PARAM_INT8: + l_argv[l_idx].data.d_int32 = 0; + break; + case PARAM_FLOAT: + l_argv[l_idx].data.d_float = 0.0; + break; + case PARAM_STRING: + l_argv[l_idx].data.d_string = NULL; + break; + default: + l_argv[l_idx].data.d_int32 = 0; + break; + + } + } + + /* init the standard parameters, that should be common to all plugins */ + l_argv[0].data.d_int32 = run_mode; + l_argv[1].data.d_image = image_id; + l_argv[2].data.d_drawable = l_drawable->id; + + /* run the plug-in procedure */ + l_ret_params = gimp_run_procedure2 (plugin_name, &l_retvals, l_nparams, l_argv); + /* free up arguments and values */ + g_free (l_argv); + + + /* free the query information */ + g_free (l_proc_blurb); + g_free (l_proc_help); + g_free (l_proc_author); + g_free (l_proc_copyright); + g_free (l_proc_date); + g_free (l_params); + g_free (l_return_vals); + + + + if (l_ret_params[0].data.d_status == FALSE) + { + fprintf(stderr, "ERROR: p_call_plugin %s failed.\n", plugin_name); + g_free(l_ret_params); + return -1; + } + else + { + if(gap_debug) fprintf(stderr, "DEBUG: p_call_plugin: %s successful.\n", plugin_name); + g_free(l_ret_params); + return 0; + } +} + + +int +p_save_xcf(gint32 image_id, char *sav_name) +{ + GParam* l_params; + gint l_retvals; + + /* save current image as xcf file + * 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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, image_id, + PARAM_DRAWABLE, 0, + PARAM_STRING, sav_name, + PARAM_STRING, sav_name, /* raw name ? */ + PARAM_END); + + if (l_params[0].data.d_status == FALSE) return(-1); + + return 0; +} + + +/* ============================================================================ + * p_get_data + * try to get the plugin's data (key is usually the name of the plugin) + * and check for the length of the retrieved data. + * if all done OK return the length of the retrieved data, + * return -1 in case of errors. + * + * RISK: this procedure may crash if the retrieved data + * is longer than PLUGIN_DATA_SIZE and gimp_get_data_size + * is not available + * (there was no way for a plugin to findout the length + * in older GIMP releases) + * ============================================================================ + */ +gint p_get_data(char *key) +{ + int l_len; + +#ifdef GIMP_HAVE_PROCEDURAL_DB_GET_DATA_SIZE + l_len = gimp_get_data_size (key); + if(l_len >= PLUGIN_DATA_SIZE) + { + fprintf(stderr, "ERROR: stored data too big Key %s (%d > %d)\n", + key, (int)l_len, (int)PLUGIN_DATA_SIZE); + return -1; + } + gimp_get_data(key, g_plugin_data); + +#else + { + int l_l1, l_l2; + + memset(g_plugin_data, 'X', PLUGIN_DATA_SIZE); + gimp_get_data(key, g_plugin_data); + + for(l_l1 = PLUGIN_DATA_SIZE -1; l_l1 >= 0; l_l1--) + { + if (g_plugin_data[l_l1] != 'X' ) + break; + } + + memset(g_plugin_data, '\0', PLUGIN_DATA_SIZE); + gimp_get_data(key, g_plugin_data); + + for(l_l2 = PLUGIN_DATA_SIZE -1; l_l2 >= 0; l_l2--) + { + if (g_plugin_data[l_l2] != '\0' ) + break; + } + + if(l_l1 > l_l2) l_len = l_l1; + else l_len = l_l2; + + l_len++; /* length is index of last valid byte + 1 */ + } +#endif + + if(l_len < 1) + { + fprintf(stderr, "ERROR: no stored data found for Key %s\n", key); + return -1; + } + + if(gap_debug) fprintf(stderr, "DEBUG p_get_data Key:%s retrieved bytes %d\n", key, (int)l_len); + return (l_len); +} + +/* ============================================================================ + * p_set_data + * + * set g_plugin_data + * ============================================================================ + */ +void p_set_data(char *key, gint plugin_data_len) +{ + gimp_set_data(key, g_plugin_data, plugin_data_len); +} + +/* ============================================================================ + * p_procedure_available + * ============================================================================ + */ + + +gint p_procedure_available(char *proc_name, t_proc_type ptype) +{ + int l_nparams; + int l_nreturn_vals; + int l_proc_type; + char *l_proc_blurb; + char *l_proc_help; + char *l_proc_author; + char *l_proc_copyright; + char *l_proc_date; + GParamDef *l_params; + GParamDef *l_return_vals; + gint l_rc; + + l_rc = 0; + + /* Query the gimp application's procedural database + * regarding a particular procedure. + */ + if(gimp_query_procedure (proc_name, + &l_proc_blurb, + &l_proc_help, + &l_proc_author, + &l_proc_copyright, + &l_proc_date, + &l_proc_type, + &l_nparams, + &l_nreturn_vals, + &l_params, + &l_return_vals)) + { + /* procedure found in PDB */ + if(gap_debug) fprintf(stderr, "DEBUG: found in PDB %s\n", proc_name); + + switch(ptype) + { + case PTYP_ITERATOR: + /* check exactly for Input Parametertypes (common to all Iterators) */ + if (l_proc_type != PROC_EXTENSION ) { l_rc = -1; break; } + if (l_nparams != 4) { l_rc = -1; break; } + if (l_params[0].type != PARAM_INT32) { l_rc = -1; break; } + if (l_params[1].type != PARAM_INT32) { l_rc = -1; break; } + if (l_params[2].type != PARAM_FLOAT) { l_rc = -1; break; } + if (l_params[3].type != PARAM_INT32) { l_rc = -1; break; } + break; + case PTYP_CAN_OPERATE_ON_DRAWABLE: + /* check if plugin can be a typical one, that works on one drawable */ + if (l_proc_type != PROC_PLUG_IN) { l_rc = -1; break; } + if (l_nparams < 3) { l_rc = -1; break; } + if (l_params[0].type != PARAM_INT32) { l_rc = -1; break; } + if (l_params[1].type != PARAM_IMAGE) { l_rc = -1; break; } + if (l_params[2].type != PARAM_DRAWABLE) { l_rc = -1; break; } + break; + default: + break; + } + /* free the query information */ + g_free (l_proc_blurb); + g_free (l_proc_help); + g_free (l_proc_author); + g_free (l_proc_copyright); + g_free (l_proc_date); + g_free (l_params); + g_free (l_return_vals); + } + else + { + return -1; + } + + + return l_rc; +} /* end p_procedure_available */ + + +/* ============================================================================ + * p_get_iterator_proc + * check the PDB for Iterator Procedures (suffix "_Iterator" or "_Iterator_ALT" + * return Pointer to the name of the Iterator Procedure + * or NULL if not found (or malloc error) + * ============================================================================ + */ + +char * p_get_iterator_proc(char *plugin_name) +{ + char *l_plugin_iterator; + + /* check for matching Iterator PluginProcedures */ + l_plugin_iterator = malloc(strlen(plugin_name) + strlen("_Iterator_ALT") +2); + if(l_plugin_iterator != NULL) + { + sprintf(l_plugin_iterator, "%s_Iterator", plugin_name); + + /* check if iterator is available in PDB */ + if(p_procedure_available(l_plugin_iterator, PTYP_ITERATOR) < 0) + { + sprintf(l_plugin_iterator, "%s_Iterator_ALT", plugin_name); + + /* check for alternative Iterator _Iterator_ALT + * for now i made some Iterator Plugins using the ending _ALT, + * If New plugins were added or existing ones were updated + * the Authors should supply original _Iterator Procedures + * to be used instead of my Hacked versions without name conflicts. + */ + if(p_procedure_available(l_plugin_iterator, PTYP_ITERATOR) < 0) + { + /* both iterator names are not available */ + free(l_plugin_iterator); + l_plugin_iterator = NULL; + } + } + } + + return (l_plugin_iterator); +} /* end p_get_iterator_proc */ + + +/* ============================================================================ + * constraint procedures + * + * aer responsible for: + * - sensitivity of the dbbrowser's Apply Buttons + * - filter for dbbrowser's listbox + * ============================================================================ + */ + +int p_constraint_proc_sel1(gchar *proc_name) +{ + int l_rc; + GImageType l_base_type; + + /* here we should check, if proc_name + * can operate on the current Imagetype (RGB, INDEXED, GRAY) + * if not, 0 should be returned. + * + * I did not find a way to do this with the PDB Interface of gimp 0.99.16 + */ + return 1; + + l_rc = 0; /* 0 .. set Apply Button in_sensitive */ + + l_base_type =gimp_image_base_type(g_current_image_id); + switch(l_base_type) + { + case RGB: + case GRAY: + case INDEXED: + l_rc = 1; + break; + } + return l_rc; +} + +int p_constraint_proc_sel2(gchar *proc_name) +{ + char *l_plugin_iterator; + int l_rc; + + l_rc = p_constraint_proc_sel1(proc_name); + if(l_rc != 0) + { + l_plugin_iterator = p_get_iterator_proc(proc_name); + if(l_plugin_iterator != NULL) + { + free(l_plugin_iterator); + return 1; /* 1 .. set "Apply Varying" Button sensitive */ + } + } + + return 0; /* 0 .. set "Apply Varying" Button in_sensitive */ +} + +int p_constraint_proc(gchar *proc_name) +{ + int l_rc; + + if(strncmp(proc_name, "file", 4) == 0) + { + /* Do not add file Plugins (check if name starts with "file") */ + return 0; + } + + if(strncmp(proc_name, "plug_in_gap_", 12) == 0) + { + /* Do not add GAP Plugins (check if name starts with "plug_in_gap_") */ + return 0; + } + + l_rc = p_procedure_available(proc_name, PTYP_CAN_OPERATE_ON_DRAWABLE); + + if(l_rc < 0) + { + /* Do not add, Plugin not available or wrong type */ + return 0; + } + + return 1; /* 1 add the plugin procedure */ +} + diff --git a/plug-ins/gap/gap_filter_pdb.h b/plug-ins/gap/gap_filter_pdb.h new file mode 100644 index 0000000000..b13317a80b --- /dev/null +++ b/plug-ins/gap/gap_filter_pdb.h @@ -0,0 +1,64 @@ +/* gap_filter_pdb.h + * + * GAP ... Gimp Animation Plugins + * + */ +/* 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. + */ + +#ifndef _GAP_FILTER_PDB_H +#define _GAP_FILTER_PDB_H + +#include "libgimp/gimp.h" + +/* max buffer size for plugin' stored valus + */ +#define PLUGIN_DATA_SIZE 8192 + + +typedef enum +{ PTYP_ANY = 0, + PTYP_ITERATOR = 1, + PTYP_CAN_OPERATE_ON_DRAWABLE = 2 +} t_proc_type; + + +typedef enum +{ PAPP_CONSTANT = 0, + PTYP_VARYING_LINEAR = 1 +} t_apply_mode; + + +/* ------------------------ + * gap_filter_pdb.h + * ------------------------ + */ + +gint p_call_plugin(char *plugin_name, gint32 image_id, gint32 layer_id, GRunModeType run_mode); +int p_save_xcf(gint32 image_id, char *sav_name); +gint p_get_data(char *key); +void p_set_data(char *key, gint plugin_data_len); +gint p_procedure_available(char *proc_name, t_proc_type ptype); +char * p_get_iterator_proc(char *plugin_name); + +int p_constraint_proc_sel1(gchar *proc_name); +int p_constraint_proc_sel2(gchar *proc_name); +int p_constraint_proc(gchar *proc_name); + + +#endif diff --git a/plug-ins/gap/gap_layer_copy.c b/plug-ins/gap/gap_layer_copy.c new file mode 100644 index 0000000000..b46280e13a --- /dev/null +++ b/plug-ins/gap/gap_layer_copy.c @@ -0,0 +1,162 @@ +/* gap_layer_copy.c + * by hof (Wolfgang Hofer) + * + */ +/* 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: + * version 0.99.00 1999.03.03 hof: use the regular gimp_layer_copy and gimp_channel_copy + * (removed private variant) + * version 0.98.00 1998.11.26 hof: added channel copy + * version 1998.11.26 hof: bugfix have to copy the layer's layer_mask too. + * type check of destination image + * version 0.96.00 hof: bugfix memory leak (must set src_tile to unref after use) + * version 0.93.01 hof: when creating the destination layer + * add alpha channel if needed in extra call + * version 0.90.00; hof: 1.st (pre) release + */ + +/* SYTEM (UNIX) includes */ +/* GIMP includes */ +/* GAP includes */ +#include "gap_layer_copy.h" +#include "gap_pdb_calls.h" + +extern int gap_debug; /* ==0 ... dont print debug infos */ + +/* ============================================================================ + * p_my_layer_copy + * copy src_layer to the dst_image, + * return the id of the new created layer (the copy) + * NOTE: source layer MUST have same type (bpp) for now + * it would be fine to extend the code to convert between any type + * ============================================================================ + */ +gint32 p_my_layer_copy (gint32 dst_image_id, + gint32 src_layer_id, + gdouble opacity, /* 0.0 upto 100.0 */ + GLayerMode mode, + gint *src_offset_x, + gint *src_offset_y ) +{ + gint32 l_new_layer_id; + gint32 l_ret_id; + char *l_name; + GDrawableType l_src_type; + + if(gap_debug) printf("GAP p_my_layer_copy: START\n"); + + l_ret_id = -1; /* prepare error retcode -1 */ + l_name = NULL; + + if(opacity > 99.99) opacity = 100.0; + if(opacity < 0.0) opacity = 0.0; + + l_name = gimp_layer_get_name(src_layer_id); + l_src_type = gimp_layer_type(src_layer_id); + + switch(l_src_type) + { + case RGB_IMAGE: /* 0 */ + case RGBA_IMAGE: /* 1 */ + if(gimp_image_base_type(dst_image_id) != RGB) { return -1; } + break; + case GRAY_IMAGE: /* 2 */ + case GRAYA_IMAGE: /* 3 */ + if(gimp_image_base_type(dst_image_id) != GRAY) { return -1; } + break; + case INDEXED_IMAGE: /* 4 */ + case INDEXEDA_IMAGE: /* 5 */ + if(gimp_image_base_type(dst_image_id) != INDEXED) { return -1; } + break; + } + + + /* copy the layer */ + l_new_layer_id = gimp_layer_copy(src_layer_id); + + if(l_new_layer_id >= 0) + { + if(p_gimp_drawable_set_image(l_new_layer_id, dst_image_id) >= 0) + { + if(! gimp_drawable_has_alpha(l_new_layer_id)) + { + /* have to add alpha channel */ + gimp_layer_add_alpha(l_new_layer_id); + } + + /* findout the offsets of the original layer within the source Image */ + gimp_drawable_offsets(src_layer_id, src_offset_x, src_offset_y ); + + gimp_layer_set_name(l_new_layer_id, l_name); + gimp_layer_set_opacity(l_new_layer_id, opacity); + gimp_layer_set_mode(l_new_layer_id, mode); + + + l_ret_id = l_new_layer_id; /* all done OK */ + } + + } + + if(l_name != NULL) { g_free (l_name); } + + if(gap_debug) printf("GAP p_my_layer_copy: ret %d\n", (int)l_ret_id); + + return l_ret_id; +} /* end p_my_layer_copy */ + + +/* ============================================================================ + * p_my_channel_copy + * copy a channel to dst_IMAGE + * ============================================================================ + */ +gint32 p_my_channel_copy (gint32 dst_image_id, + gint32 src_channel_id) +{ + gint32 l_new_channel_id; + gint32 l_ret_id; + char *l_name; + + if(gap_debug) printf("GAP :p_my_channel_copy START\n"); + + l_ret_id = -1; /* prepare error retcode -1 */ + l_name = NULL; + + /* create new channel in destination image */ + l_name = gimp_channel_get_name(src_channel_id); + + /* copy the channel */ + l_new_channel_id = gimp_channel_copy(src_channel_id); + if(l_new_channel_id >= 0) + { + if(p_gimp_drawable_set_image(l_new_channel_id, dst_image_id) >= 0) + { + gimp_channel_set_name(l_new_channel_id, l_name); + l_ret_id = l_new_channel_id; /* all done OK */ + } + } + + if(l_name != NULL) { g_free (l_name); } + + if(gap_debug) printf("GAP :p_my_channel_copy id=%d\n", (int)l_ret_id); + + return l_ret_id; +} /* end p_my_channel_copy */ + diff --git a/plug-ins/gap/gap_layer_copy.h b/plug-ins/gap/gap_layer_copy.h new file mode 100644 index 0000000000..4441a3f23f --- /dev/null +++ b/plug-ins/gap/gap_layer_copy.h @@ -0,0 +1,58 @@ +/* my_layer_copy.h + * 1997.11.06 hof (Wolfgang Hofer) + * + * + * this procedure works similar to gimp_layer_copy(src_id); + * ==> gimp_layer_copy works only for layers within the same image !! + * ==> Workaround: + * p_my_layer_copy is my 'private' version of layercopy + * that can copy layers from another image of the same type. + * + * returns the id of the new layer + * and the offests of the original within the source image + */ +/* 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: + * version 0.98.00 1998.11.26 hof: added channel copy + * version 0.90.00; hof: 1.st (pre) release + */ + +#ifndef _GAP_LAYER_COPY_H +#define _GAP_LAYER_COPY_H + +/* SYTEM (UNIX) includes */ +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +gint32 p_my_layer_copy (gint32 dst_image_id, + gint32 src_layer_id, + gdouble opacity, /* 0.0 upto 100.0 */ + GLayerMode mode, + gint *src_offset_x, + gint *src_offset_y ); + +gint32 p_my_channel_copy (gint32 dst_image_id, + gint32 src_channel_id); + +#endif diff --git a/plug-ins/gap/gap_lib.c b/plug-ins/gap/gap_lib.c new file mode 100644 index 0000000000..14cc32c177 --- /dev/null +++ b/plug-ins/gap/gap_lib.c @@ -0,0 +1,2068 @@ +/* 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: + * 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 + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_layer_copy.h" +#include "gap_lib.h" +#include "gap_arr_dialog.h" +#include "gap_mov_dialog.h" +#include "gap_exchange_image.h" + +extern int gap_debug; /* ==0 ... dont print debug infos */ + +static char g_errtxt[1024]; /* buffer for current message- or errortext */ + +/* ------------------------------------------ */ +/* 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); + +/* ============================================================================ + * 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(GRunModeType run_mode, char *msg) +{ + static t_but_arg l_argv[1]; + int l_argc; + + l_argv[0].but_txt = "CANCEL"; + 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 == 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_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 *)calloc(1, (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, "r"); /* open read */ + if(l_fp == NULL) + { + fprintf (stderr, "open(read) error on '%s'\n", fname); + free(l_buffer); + return -1; + } + fread(l_buffer, 1, (size_t)l_len, l_fp); + fclose(l_fp); + + l_fp = fopen(fname_copy, "w"); /* open write */ + if(l_fp == NULL) + { + fprintf (stderr, "file_copy: open(write) error on '%s' \n", fname_copy); + free(l_buffer); + return -1; + } + + if(l_len > 0) + { + fwrite(l_buffer, l_len, 1, l_fp); + } + + fclose(l_fp); + 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; + int l_rc; + + l_fname = p_alloc_fname(ainfo_ptr->basename, nr, ainfo_ptr->extension); + if(l_fname == NULL) { return(1); } + + if(gap_debug) fprintf(stderr, "\nDEBUG p_delete_frame: %s\n", l_fname); + l_rc = remove(l_fname); + + free(l_fname); + + 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; + 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) { free(l_from_fname); 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); + + free(l_from_fname); + free(l_to_fname); + + return(l_rc); + +} /* end p_rename_frame */ + + +/* ============================================================================ + * p_alloc_basename + * + * build the basename from an images name + * Extension and trailing "_0000" 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 *)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 and underscore (_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 + { + 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 = calloc(1, (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) +{ + char *l_fname; + + if(basename == NULL) return (NULL); + l_fname = (char *)malloc(strlen(basename) + strlen(extension) + 8); + if(l_fname != NULL) + { + sprintf(l_fname, "%s_%04ld%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, GRunModeType run_mode) +{ + t_anim_info *l_ainfo_ptr; + + l_ainfo_ptr = (t_anim_info*)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) + { + l_ainfo_ptr->old_filename = malloc(30); + sprintf(l_ainfo_ptr->old_filename, "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 = 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 = "/" ; 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: cant 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: + sprintf(dirname_buff, "%s", l_dp->d_name); + break; + case 1: + sprintf(dirname_buff, "%c%s", G_DIR_SEPARATOR, l_dp->d_name); + break; + default: + /* UNIX: "/dir/file" + * DOS: "drv:\dir\file" + */ + sprintf(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; + } + + free(l_dummy); + } + } + } + } + closedir( l_dirp ); + } + + 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 = l_minnr; + + 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) + free(aptr->basename); + + if(aptr->extension) + free(aptr->extension); + + if(aptr->new_filename) + free(aptr->new_filename); + + if(aptr->old_filename) + free(aptr->old_filename); + + free(aptr); +} + + +/* ============================================================================ + * 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)\n"); + } + 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\nGAP-plugins works only with filenames\nthat ends with _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) +{ + char* l_cmd; + char* l_tmpname; + int l_rc, l_rc2; + + if(zip == NULL) return NULL; + + l_cmd = NULL; + l_tmpname = new_name; + + l_cmd = calloc(1, (strlen(l_tmpname) + strlen(orig_name) + 20)); + if(l_cmd == NULL) + { + return NULL; + } + + /* 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') + { + sprintf(l_cmd, "gzip -cfd <\"%s\" >\"%s\"", orig_name, l_tmpname); + } + else + { + sprintf(l_cmd, "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; + } + 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) +{ + static char *l_msg = + "You are using a fileformat != xcf\nSave Operations may result\nin loss of layerinformation"; + + static char l_save_as_name[80]; + + static t_but_arg l_argv[3]; + int l_argc; + int l_save_as_mode; + + /* check if there are SAVE_AS_MODE settings (from privious calls within one gimp session) */ + l_save_as_mode = -1; + /* sprintf(l_save_as_name, "plug_in_gap_plugins_SAVE_AS_MODE_%d", (int)image_id);*/ + sprintf(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 = "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; + } + + 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 0; +} /* end p_decide_save_as */ + + + +/* ============================================================================ + * p_save_named_image + * ============================================================================ + */ +gint32 p_save_named_image(gint32 image_id, char *sav_name, GRunModeType run_mode) +{ + GDrawable *l_drawable; + gint l_nlayers; + gint32 *l_layers_list; + GParam *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, + PARAM_INT32, run_mode, + PARAM_IMAGE, image_id, + PARAM_DRAWABLE, l_drawable->id, + PARAM_STRING, sav_name, + PARAM_STRING, sav_name, /* raw name ? */ + PARAM_END); + + if(gap_debug) fprintf(stderr, "DEBUG: after p_save_named_image: '%s' nlayers=%d\n", sav_name, (int)l_nlayers); + + g_free (l_layers_list); + g_free (l_drawable); + + + if (l_params[0].data.d_status == FALSE) + { + 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) +{ + GParam* l_params; + char *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 = (char *)malloc(strlen(sav_name) + strlen(".gtmp") + strlen(l_ext) +2); + if(l_tmpname == NULL) + { + return -1; + } + sprintf(l_tmpname, "%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, + PARAM_STRING, + &l_ext[1], + PARAM_END); + + if(l_params[1].data.d_string != NULL) + { + l_tmpname = l_params[1].data.d_string; + } + g_free(l_params); + } + + free(l_ext); + + + if(gap_debug) + { + l_ext = getenv("GAP_NO_SAVE"); + if(l_ext != NULL) + { + fprintf(stderr, "DEBUG: GAP_NO_SAVE is set: save is skipped: '%s'\n", l_tmpname); + 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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, image_id, + PARAM_DRAWABLE, 0, + PARAM_STRING, l_tmpname, + PARAM_STRING, l_tmpname, /* raw name ? */ + PARAM_END); + if(gap_debug) fprintf(stderr, "DEBUG: after xcf p_save_named_frame: '%s'\n", l_tmpname); + + if (l_params[0].data.d_status != FALSE) + { + 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); + if(l_rc >= 0) + { + l_rc = p_save_named_image(image_id, l_tmpname, RUN_NONINTERACTIVE); + } + } + + if(l_rc < 0) + { + remove(l_tmpname); + 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: cant 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); + } + } + + 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) +{ + GParam* 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, + PARAM_STRING, + &l_ext[1], /* extension */ + PARAM_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; + 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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_STRING, l_tmpname, + PARAM_STRING, l_tmpname, /* raw name ? */ + PARAM_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) */ + gimp_image_delete(l_tmp_image_id); + + /* use the original lod_name */ + gimp_image_set_filename (image_id, lod_name); + 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) 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)) + { + sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n" + , l_hi, l_lo); + p_msg_win(ainfo_ptr->run_mode, g_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) 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\n", + (int)range_from, (int)range_to, (int)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 */ + p_save_named_frame(ainfo_ptr->image_id, l_curr_name); + + /* use a new name (_0001.xcf Konvention) */ + gimp_image_set_filename (ainfo_ptr->image_id, l_curr_name); + 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 == 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)) + { + sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n", l_lo, l_hi); + p_msg_win(ainfo_ptr->run_mode, g_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 == 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_file_copy(l_curr_name, l_dup_name); + free(l_dup_name); + free(l_curr_name); + } + if(ainfo_ptr->run_mode == 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) 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; + + 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)) + { + sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n", dest, l_tmp_nr); + p_msg_win(ainfo_ptr->run_mode, g_errtxt); + return -1; + } + if(0 != p_rename_frame(ainfo_ptr, ainfo_ptr->curr_frame_nr, dest)) + { + sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n", ainfo_ptr->curr_frame_nr, dest); + p_msg_win(ainfo_ptr->run_mode, g_errtxt); + return -1; + } + if(0 != p_rename_frame(ainfo_ptr, l_tmp_nr, ainfo_ptr->curr_frame_nr)) + { + sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n", l_tmp_nr, ainfo_ptr->curr_frame_nr); + p_msg_win(ainfo_ptr->run_mode, g_errtxt); + return -1; + } + + /* load from the "new" current frame */ + if(ainfo_ptr->new_filename != NULL) 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; + char *l_curr_name; + 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); + free(l_curr_name); + + l_percentage = 0.0; + if(ainfo_ptr->run_mode == 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)) + { + sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n", l_lo, l_hi); + p_msg_win(ainfo_ptr->run_mode, g_errtxt); + return -1; + } + if(ainfo_ptr->run_mode == 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)) + { + sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n", l_lo, l_hi); + p_msg_win(ainfo_ptr->run_mode, g_errtxt); + return -1; + } + if(ainfo_ptr->run_mode == 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) 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(GRunModeType 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(GRunModeType 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(GRunModeType 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(GRunModeType 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. + * RUN_INTERACTIVE: + * show dialogwindow where user can enter the destination frame Nr. + * ============================================================================ + */ + +int gap_goto(GRunModeType run_mode, gint32 image_id, int nr) +{ + int rc; + t_anim_info *ainfo_ptr; + + long l_dest; + char l_hline[50]; + char l_title[50]; + + 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 == RUN_INTERACTIVE) + { + sprintf(l_title, "Goto Frame (%ld/%ld)" + , ainfo_ptr->curr_frame_nr + , ainfo_ptr->frame_cnt); + sprintf(l_hline, "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); + + 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(GRunModeType run_mode, gint32 image_id, int nr) +{ + int rc; + t_anim_info *ainfo_ptr; + + long l_cnt; + long l_max; + char l_hline[50]; + char l_title[50]; + + 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 == RUN_INTERACTIVE) + { + sprintf(l_title, "Delete Frames (%ld/%ld)" + , ainfo_ptr->curr_frame_nr + , ainfo_ptr->frame_cnt); + sprintf(l_hline, "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); + + 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]; + char l_title[50]; + + sprintf(l_title, "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 = 0; + argv[2].int_max = 99; + argv[2].int_ret = 1; + argv[2].help_txt = "Copy selected Range n-times \n(you may type in Values > 99)"; + + if(TRUE == p_array_dialog(l_title, "Duplicate Framerange", 3, argv)) + { *range_from = (long)(argv[0].int_ret); + *range_to = (long)(argv[1].int_ret); + return (int)(argv[2].int_ret); + } + else + { + return -1; + } + + +} /* end p_dup_dialog */ + + +/* ============================================================================ + * gap_dup + * ============================================================================ + */ +int gap_dup(GRunModeType 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 == RUN_INTERACTIVE) + { + if(0 != p_chk_framechange(ainfo_ptr)) { l_cnt = -1; } + else { 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(GRunModeType run_mode, gint32 image_id, int nr) +{ + int rc; + t_anim_info *ainfo_ptr; + + long l_dest; + long l_initial; + char l_hline[50]; + char l_title[50]; + + 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 == 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; + } + sprintf(l_title, "Exchange current Frame (%ld)" + , ainfo_ptr->curr_frame_nr); + sprintf(l_hline, "With Frame (Number)"); + + l_dest = p_slider_dialog(l_title, l_hline, "Number :", NULL + , ainfo_ptr->first_frame_nr + , ainfo_ptr->last_frame_nr + , l_initial + , TRUE); + + 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]; + char l_title[50]; + + sprintf(l_title, "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 = FALSE; + 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)) + { *range_from = (long)(argv[0].int_ret); + *range_to = (long)(argv[1].int_ret); + return (int)(argv[2].int_ret); + } + else + { + return 0; + } + + +} /* end p_shift_dialog */ + + +/* ============================================================================ + * gap_shift + * ============================================================================ + */ +int gap_shift(GRunModeType 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 == 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 */ + diff --git a/plug-ins/gap/gap_lib.h b/plug-ins/gap/gap_lib.h new file mode 100644 index 0000000000..343c9a9597 --- /dev/null +++ b/plug-ins/gap/gap_lib.h @@ -0,0 +1,117 @@ +/* gap_lib.h + * 1997.11.01 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * basic anim functions + * + */ +/* 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: + * 0.99.00; 1999/03/15 hof: prepared for win/dos filename conventions + * 0.96.02; 1998/08/05 hof: extended gap_dup (duplicate range instead of singele frame) + * added gap_shift (framesequence shift) + * 0.96.00; 1998/06/27 hof: added gap animation sizechange plugins + * (moved range_ops to seperate .h file) + * 0.94.01; 1998/04/27 hof: added flatten_mode to plugin: gap_range_to_multilayer + * 0.90.00; hof: 1.st (pre) release + */ + +#ifndef _GAP_LIB_H +#define _GAP_LIB_H + +#include "libgimp/gimp.h" + +/* G_DIR_SEPARATOR (is defined in glib.h if you have glib-1.2.0 or later) */ +#ifdef NATIVE_WIN32 + +/* Filenames in WIN/DOS Style */ +#ifndef G_DIR_SEPARATOR +#define G_DIR_SEPARATOR '\\' +#endif +#define DIR_ROOT ':' + +#else /* !NATIVE_WIN32 */ + +/* Filenames in UNIX Style */ +#ifndef G_DIR_SEPARATOR +#define G_DIR_SEPARATOR '/' +#endif +#define DIR_ROOT '/' + +#endif /* !NATIVE_WIN32 */ + + +typedef struct t_anim_info { + gint32 image_id; + char *basename; /* may include path */ + long frame_nr; + char *extension; + char *new_filename; + char *old_filename; + GRunModeType run_mode; + long width; + long height; + long type; + long frame_cnt; + long curr_frame_nr; + long first_frame_nr; + long last_frame_nr; +} t_anim_info; + + +/* procedures used in other gap*.c files */ +int p_file_exists(char *fname); +int p_file_copy(char *fname, char *fname_copy); +void p_free_ainfo(t_anim_info **ainfo); +char* p_alloc_basename(char *imagename, long *number); +char* p_alloc_extension(char *imagename); +t_anim_info* p_alloc_ainfo(gint32 image_id, GRunModeType run_mode); +int p_dir_ainfo(t_anim_info *ainfo_ptr); +int p_chk_framerange(t_anim_info *ainfo_ptr); +int p_chk_framechange(t_anim_info *ainfo_ptr); + +int p_save_named_frame (gint32 image_id, char *sav_name); +int p_load_named_frame (gint32 image_id, char *lod_name); +gint32 p_load_image (char *lod_name); +gint32 p_save_named_image(gint32 image_id, char *sav_name, GRunModeType run_mode); +char* p_alloc_fname(char *basename, long nr, char *extension); +char* p_gzip (char *orig_name, char *new_name, char *zip); + + +/* animation menu fuctions provided by gap_lib.c */ + +int gap_next(GRunModeType run_mode, gint32 image_id); +int gap_prev(GRunModeType run_mode, gint32 image_id); +int gap_first(GRunModeType run_mode, gint32 image_id); +int gap_last(GRunModeType run_mode, gint32 image_id); +int gap_goto(GRunModeType run_mode, gint32 image_id, int nr); + +int gap_dup(GRunModeType run_mode, gint32 image_id, int nr, long range_from, long range_to); +int gap_del(GRunModeType run_mode, gint32 image_id, int nr); +int gap_exchg(GRunModeType run_mode, gint32 image_id, int nr); +int gap_shift(GRunModeType run_mode, gint32 image_id, int nr, long range_from, long range_to); + +void p_msg_win(GRunModeType run_mode, char *msg); + + +#endif + + diff --git a/plug-ins/gap/gap_main.c b/plug-ins/gap/gap_main.c new file mode 100644 index 0000000000..72e72dbaab --- /dev/null +++ b/plug-ins/gap/gap_main.c @@ -0,0 +1,1214 @@ +/* gap_main.c + * 1997.11.05 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Package + * + * This Module contains: + * - MAIN of all GAP_Plugins + * - query registration of GAP Procedures (AnimFrames Menu) in the PDB + * - run invoke the selected GAP procedure by its PDB name + * + * + * GAP provides Animation Functions for the gimp, + * working on a series of Images stored on disk in gimps .xcf Format. + * + * Frames are Images with naming convention like this: + * Imagename_. + * Example: snoopy_0001.xcf, snoopy_0002.xcf, snoopy_0003.xcf + * + * if gzip is installed on your system you may optional + * use gziped xcf frames with extensions ".xcfgz" + * + */ +/* 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. + */ + +static char *gap_main_version = "0.99.00; 1999/03/17"; + +/* revision history: + * version 0.99.00; 1999/03/17 hof: updated main version + * version 0.98.02; 1999/01/27 hof: updated main version + * version 0.98.01; 1998/12/21 hof: updated main version, e-mail adress + * version 0.98.00; 1998/11/27 hof: updated main version, started port to GIMP 1.1 interfaces + * Use no '_' (underscore) in menunames. (has special function in 1.1) + * version 0.96.03; 1998/08/31 hof: updated main version, + * gap_range_to_multilayer now returns image_id + * gap_split_image now returns image_id + * version 0.96.02; 1998/08/05 hof: updated main version, + * added gap_shift + * version 0.96.00; 1998/06/27 hof: added gap animation sizechange plugins + * gap_split_image + * gap_mpeg_encode + * version 0.94.01; 1998/04/28 hof: updated main version, + * added flatten_mode to plugin: gap_range_to_multilayer + * version 0.94.00; 1998/04/25 hof: updated main version + * version 0.93.01; 1998/02/03 hof: + * version 0.92.00; hof: set gap_debug from environment + * version 0.91.00; 1997/12/22 hof: + * version 0.90.00; hof: 1.st (pre) release + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_lib.h" +#include "gap_match.h" +#include "gap_range_ops.h" +#include "gap_split.h" +#include "gap_mov_exec.h" +#include "gap_mpege.h" +#include "gap_mod_layer.h" +#include "gap_arr_dialog.h" + +/* ------------------------ + * global gap DEBUG switch + * ------------------------ + */ + +/* int gap_debug = 1; */ /* print debug infos */ +/* int gap_debug = 0; */ /* 0: dont print debug infos */ + +int gap_debug = 0; + + + +static void query(void); +static void run(char *name, int nparam, GParam *param, + int *nretvals, GParam **retvals); + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +MAIN () + +static void +query () +{ + static GParamDef args_std[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (current one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + }; + static int nargs_std = sizeof(args_std) / sizeof(args_std[0]); + + static GParamDef args_goto[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (current one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "nr", "frame nr where to go"}, + }; + static int nargs_goto = sizeof(args_goto) / sizeof(args_goto[0]); + + static GParamDef args_del[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (current one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "nr", "number of frames to delete (delete starts at current frame)"}, + }; + static int nargs_del = sizeof(args_del) / sizeof(args_del[0]); + + + static GParamDef args_dup[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (current one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "nr", "how often to copy current frame"}, + {PARAM_INT32, "range_from", "frame nr to start"}, + {PARAM_INT32, "range_to", "frame nr to stop (can be lower than range_from)"}, + }; + static int nargs_dup = sizeof(args_dup) / sizeof(args_dup[0]); + + static GParamDef args_exchg[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (current one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "nr", "nr of frame to exchange with current frame"}, + }; + static int nargs_exchg = sizeof(args_exchg) / sizeof(args_exchg[0]); + + static GParamDef args_mov[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + }; + static int nargs_mov = sizeof(args_std) / sizeof(args_mov[0]); + + + static GParamDef args_f2multi[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "range_from", "frame nr to start"}, + {PARAM_INT32, "range_to", "frame nr to stop (can be lower than range_from)"}, + {PARAM_INT32, "flatten_mode", "{ expand-as-necessary(0), CLIP-TO_IMG(1), CLIP-TO-BG-LAYER(2), FLATTEN(3) }"}, + {PARAM_INT32, "bg_visible", "{ BG_NOT_VISIBLE (0), BG_VISIBLE(1) }"}, + {PARAM_INT32, "framerate", "frame delaytime in ms"}, + {PARAM_STRING, "frame_basename", "basename for all generated layers"}, + {PARAM_INT32, "select_mode", "Mode how to identify a layer: 0-3 by layername 0=equal, 1=prefix, 2=suffix, 3=contains, 4=layerstack_numberslist, 5=inv_layerstack, 6=all_visible"}, + {PARAM_INT32, "select_case", "0: ignore case 1: select_string is case sensitive"}, + {PARAM_INT32, "select_invert", "0: select normal 1: invert (select all unselected layers)"}, + {PARAM_STRING, "select_string", "string to match with layername (how to match is defined by select_mode)"}, + }; + static int nargs_f2multi = sizeof(args_f2multi) / sizeof(args_f2multi[0]); + + static GParamDef return_f2multi[] = + { + { PARAM_IMAGE, "new_image", "Output image" } + }; + static int nreturn_f2multi = sizeof(return_f2multi) / sizeof(return_f2multi[0]); + + + + + static GParamDef args_rflatt[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "range_from", "frame nr to start"}, + {PARAM_INT32, "range_to", "frame nr to stop (can be lower than range_from)"}, + }; + static int nargs_rflatt = sizeof(args_rflatt) / sizeof(args_rflatt[0]); + + static GParamDef args_rlayerdel[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "range_from", "frame nr to start"}, + {PARAM_INT32, "range_to", "frame nr to stop (can be lower than range_from)"}, + {PARAM_INT32, "nr", "layerstack position (0 == on top)"}, + }; + static int nargs_rlayerdel = sizeof(args_rlayerdel) / sizeof(args_rlayerdel[0]); + + + static GParamDef args_rconv[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "range_from", "frame nr to start"}, + {PARAM_INT32, "range_to", "frame nr to stop (can be lower than range_from)"}, + {PARAM_INT32, "flatten", "0 .. dont flatten image before save"}, + {PARAM_INT32, "dest_type", "0=RGB, 1=GRAY, 2=INDEXED"}, + {PARAM_INT32, "dest_colors", "1 upto 256 (used only for dest_type INDEXED)"}, + {PARAM_INT32, "dest_dither", "0=no, 1=floyd-steinberg (used only for dest_type INDEXED)"}, + {PARAM_STRING, "extension", "extension for the destination filetype (jpg, tif ...or any other gimp supported type)"}, + {PARAM_STRING, "basename", "(optional parameter) here you may specify the basename of the destination frames \"/my_dir/myframe\" _0001.ext is added)"}, + }; + static int nargs_rconv = sizeof(args_rconv) / sizeof(args_rconv[0]); + + + /* resize and crop share the same parameters */ + static GParamDef args_resize[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "new_width", "width of the resulting anim_frame images in pixels"}, + {PARAM_INT32, "new_height", "height of the resulting anim_frame images in pixels"}, + {PARAM_INT32, "offset_x", "X offset in pixels"}, + {PARAM_INT32, "offset_y", "Y offset in pixels"}, + }; + static int nargs_resize = sizeof(args_resize) / sizeof(args_resize[0]); + + static GParamDef args_scale[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "new_width", "width of the resulting anim_frame images in pixels"}, + {PARAM_INT32, "new_height", "height of the resulting anim_frame images in pixels"}, + }; + static int nargs_scale = sizeof(args_scale) / sizeof(args_scale[0]); + + + static GParamDef args_split[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (NO Anim Frame allowed)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "inverse_order", "True/False"}, + {PARAM_INT32, "no_alpha", "True: remove alpha channel(s) in the destination frames"}, + {PARAM_STRING, "extension", "extension for the destination filetype (jpg, tif ...or any other gimp supported type)"}, + }; + static int nargs_split = sizeof(args_split) / sizeof(args_split[0]); + + static GParamDef return_split[] = + { + { PARAM_IMAGE, "new_image", "Output image (first or last resulting frame)" } + }; + static int nreturn_split = sizeof(return_split) / sizeof(return_split[0]); + + + static GParamDef args_mpege[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + }; + static int nargs_mpege = sizeof(args_mpege) / sizeof(args_mpege[0]); + + static GParamDef *return_vals = NULL; + static int nreturn_vals = 0; + + + static GParamDef args_shift[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (current one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "nr", "how many framenumbers to shift the framesequence"}, + {PARAM_INT32, "range_from", "frame nr to start"}, + {PARAM_INT32, "range_to", "frame nr to stop"}, + }; + static int nargs_shift = sizeof(args_shift) / sizeof(args_shift[0]); + + static GParamDef args_modify[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image (current one of the Anim Frames)"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + {PARAM_INT32, "range_from", "frame nr to start"}, + {PARAM_INT32, "range_to", "frame nr to stop"}, + {PARAM_INT32, "action_mode", "0:set_visible, 1:set_invisible, 2:set_linked, 3:set_unlinked, 4:raise, 5:lower, 6:merge_expand, 7:merge_img, 8:merge_bg, 9:apply_filter, 10:duplicate, 11:delete, 12:rename"}, + {PARAM_INT32, "select_mode", "Mode how to identify a layer: 0-3 by layername 0=equal, 1=prefix, 2=suffix, 3=contains, 4=layerstack_numberslist, 5=inv_layerstack, 6=all_visible"}, + {PARAM_INT32, "select_case", "0: ignore case 1: select_string is case sensitive"}, + {PARAM_INT32, "select_invert", "0: select normal 1: invert (select all unselected layers)"}, + {PARAM_STRING, "select_string", "string to match with layername (how to match is defined by select_mode)"}, + {PARAM_STRING, "new_layername", "is only used at action rename. [####] is replaced by the framnumber"}, + }; + static int nargs_modify = sizeof(args_modify) / sizeof(args_modify[0]); + + gimp_install_procedure("plug_in_gap_next", + "This plugin exchanges current image with (next nubered) image from disk.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Goto Next", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_std, nreturn_vals, + args_std, return_vals); + + gimp_install_procedure("plug_in_gap_prev", + "This plugin exchanges current image with (previous nubered) image from disk.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Goto Prev", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_std, nreturn_vals, + args_std, return_vals); + + gimp_install_procedure("plug_in_gap_first", + "This plugin exchanges current image with (lowest nubered) image from disk.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Goto First", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_std, nreturn_vals, + args_std, return_vals); + + gimp_install_procedure("plug_in_gap_last", + "This plugin exchanges current image with (highest nubered) image from disk.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Goto Last", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_std, nreturn_vals, + args_std, return_vals); + + gimp_install_procedure("plug_in_gap_goto", + "This plugin exchanges current image with requested image (nr) from disk.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Goto Any", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_goto, nreturn_vals, + args_goto, return_vals); + + gimp_install_procedure("plug_in_gap_del", + "This plugin deletes the given number of frames from disk including the current frame.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Delete Frames", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_del, nreturn_vals, + args_del, return_vals); + + gimp_install_procedure("plug_in_gap_dup", + "This plugin duplicates the current frames on disk n-times.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Duplicate Frames", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_dup, nreturn_vals, + args_dup, return_vals); + + gimp_install_procedure("plug_in_gap_exchg", + "This plugin exchanges content of the current with destination frame.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Exchange Frame", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_exchg, nreturn_vals, + args_exchg, return_vals); + + gimp_install_procedure("plug_in_gap_move", + "This plugin copies layer(s) from one sourceimage to multiple frames on disk, varying position, size and opacity.", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Move Path", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_mov, nreturn_vals, + args_mov, return_vals); + + gimp_install_procedure("plug_in_gap_range_to_multilayer", + "This plugin creates a new image from the given range of frame-images. Each frame is converted to one layer in the new image, according to flatten_mode. (the frames on disk are not changed).", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames to Image", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_f2multi, nreturn_f2multi, + args_f2multi, return_f2multi); + + gimp_install_procedure("plug_in_gap_range_flatten", + "This plugin flattens the given range of frame-images (on disk)", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames Flatten", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_rflatt, nreturn_vals, + args_rflatt, return_vals); + + gimp_install_procedure("plug_in_gap_range_layer_del", + "This plugin deletes one layer in the given range of frame-images (on disk). exception: the last remaining layer of a frame is not deleted", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames LayerDel", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_rlayerdel, nreturn_vals, + args_rlayerdel, return_vals); + + gimp_install_procedure("plug_in_gap_range_convert", + "This plugin converts the given range of frame-images to other fileformats (on disk) depending on extension", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames Convert", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_rconv, nreturn_vals, + args_rconv, return_vals); + + gimp_install_procedure("plug_in_gap_anim_resize", + "This plugin resizes all anim_frames (images on disk) to the given new_width/new_height", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames Resize", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_resize, nreturn_vals, + args_resize, return_vals); + + gimp_install_procedure("plug_in_gap_anim_crop", + "This plugin crops all anim_frames (images on disk) to the given new_width/new_height", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames Crop", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_resize, nreturn_vals, + args_resize, return_vals); + + gimp_install_procedure("plug_in_gap_anim_scale", + "This plugin scales all anim_frames (images on disk) to the given new_width/new_height", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames Scale", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_scale, nreturn_vals, + args_scale, return_vals); + + gimp_install_procedure("plug_in_gap_split", + "This plugin splits the current image to anim frames (images on disk). Each layer is saved as one frame", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Split Img to Frames", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_split, nreturn_split, + args_split, return_split); + + gimp_install_procedure("plug_in_gap_mpeg_encode", + "This plugin calls mpeg_encode to convert anim frames to MPEG1, or just generates a param file for mpeg_encode. (mpeg_encode must be installed on your system)", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames MPEG1 encode", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_mpege, nreturn_vals, + args_mpege, return_vals); + + + gimp_install_procedure("plug_in_gap_mpeg2encode", + "This plugin calls mpeg2encode to convert anim frames to MPEG1 or MPEG2, or just generates a param file for mpeg2encode. (mpeg2encode must be installed on your system)", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames MPEG2 encode", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_mpege, nreturn_vals, + args_mpege, return_vals); + + + gimp_install_procedure("plug_in_gap_shift", + "This plugin exchanges frame numbers in the given range. (discfile frame_0001.xcf is renamed to frame_0002.xcf, 2->3, 3->4 ... n->1)", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Framesequence Shift", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_shift, nreturn_vals, + args_shift, return_vals); + + gimp_install_procedure("plug_in_gap_modify", + "This plugin performs a modifying action on each selected layer in each selected framerange", + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_main_version, + "/AnimFrames/Frames Modify", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_modify, nreturn_vals, + args_modify, return_vals); +} /* end query */ + + + +static void +run (char *name, + int n_params, + GParam *param, + int *nreturn_vals, + GParam **return_vals) +{ + typedef struct + { + long lock; /* 0 ... NOT Locked, 1 ... locked */ + gint32 image_id; + long timestamp; /* locktime not used for now */ + } t_lockdata; + + t_lockdata l_lock; + static char l_lockname[50]; + char *l_env; + + char l_extension[32]; + char l_sel_str[MAX_LAYERNAME]; + char l_layername[MAX_LAYERNAME]; + char *l_basename_ptr; + static GParam values[2]; + GRunModeType run_mode; + GStatusType status = STATUS_SUCCESS; + gint32 image_id; + gint32 nr; + long range_from, range_to; + GImageType dest_type; + gint32 dest_colors; + gint32 dest_dither; + gint32 mode; + long new_width; + long new_height; + long offs_x; + long offs_y; + gint32 inverse_order; + gint32 no_alpha; + long framerate; +#define FRAME_BASENAME_LEN 256 + char frame_basename[FRAME_BASENAME_LEN]; + gint32 sel_mode, sel_case, sel_invert; + + gint32 l_rc; + + *nreturn_vals = 1; + *return_vals = values; + nr = 0; + l_rc = 0; + + + l_env = getenv("GAP_DEBUG"); + if(l_env != NULL) + { + if((*l_env != 'n') && (*l_env != 'N')) gap_debug = 1; + } + + run_mode = param[0].data.d_int32; + + + if(gap_debug) fprintf(stderr, "\n\ngap_main: debug name = %s\n", name); + + image_id = param[1].data.d_image; + + /* check for locks */ + l_lock.lock = 0; + sprintf(l_lockname, "plug_in_gap_plugins_LOCK_%d", (int)image_id); + gimp_get_data (l_lockname, &l_lock); + + if((l_lock.lock != 0) && (l_lock.image_id == image_id)) + { + fprintf(stderr, "gap_plugin is LOCKED for Image ID=%s\n", l_lockname); + + status = STATUS_EXECUTION_ERROR; + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; + return ; + } + + + /* set LOCK on current image (for all gap_plugins) */ + l_lock.lock = 1; + l_lock.image_id = image_id; + gimp_set_data (l_lockname, &l_lock, sizeof(l_lock)); + + + if (strcmp (name, "plug_in_gap_next") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + + l_rc = gap_next(run_mode, image_id); + + } + } + else if (strcmp (name, "plug_in_gap_prev") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + + l_rc = gap_prev(run_mode, image_id); + + } + } + else if (strcmp (name, "plug_in_gap_first") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + + l_rc = gap_first(run_mode, image_id); + + } + } + else if (strcmp (name, "plug_in_gap_last") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + + l_rc = gap_last(run_mode, image_id); + + } + } + else if (strcmp (name, "plug_in_gap_goto") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 4) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + nr = param[3].data.d_int32; /* destination frame nr */ + + l_rc = gap_goto(run_mode, image_id, nr); + + } + } + else if (strcmp (name, "plug_in_gap_del") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 4) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + nr = param[3].data.d_int32; /* number of frames to delete */ + + l_rc = gap_del(run_mode, image_id, nr); + + } + } + else if (strcmp (name, "plug_in_gap_dup") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + /* accept the old interface with 4 parameters */ + if ((n_params != 4) && (n_params != 6)) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + nr = param[3].data.d_int32; /* how often to copy current frame */ + if (n_params != 6) + { + range_from = param[4].data.d_int32; /* frame nr to start */ + range_to = param[5].data.d_int32; /* frame nr to stop */ + } + else + { + range_from = -1; + range_to = -1; + } + + l_rc = gap_dup(run_mode, image_id, nr, range_from, range_to ); + + } + } + else if (strcmp (name, "plug_in_gap_exchg") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 4) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + nr = param[3].data.d_int32; /* nr of frame to exchange with current frame */ + + l_rc = gap_exchg(run_mode, image_id, nr); + + } + } + else if (strcmp (name, "plug_in_gap_move") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + /* if (n_params != ?? ) */ + status = STATUS_CALLING_ERROR; + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + + l_rc = gap_move(run_mode, image_id); + + } + } + else if (strcmp (name, "plug_in_gap_range_to_multilayer") == 0) + { + l_rc = -1; + if (run_mode == RUN_NONINTERACTIVE) + { + if ((n_params != 7) && (n_params != 9) && (n_params != 13)) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + range_from = param[3].data.d_int32; /* frame nr to start */ + range_to = param[4].data.d_int32; /* frame nr to stop */ + mode = param[5].data.d_int32; /* { expand-as-necessary(0), CLIP-TO_IMG(1), CLIP-TO-BG-LAYER(2), FLATTEN(3)} */ + nr = param[6].data.d_int32; /* { BG_NOT_VISIBLE (0), BG_VISIBLE(1)} */ + /* old interface: use default values for framerate and frame_basename */ + framerate = 50; + strcpy(frame_basename, "frame"); + + sel_mode = MTCH_ALL_VISIBLE; + sel_invert = FALSE; + sel_case = TRUE; + + if ( n_params >= 9 ) + { + framerate = param[7].data.d_int32; + if(param[8].data.d_string != NULL) + { + strncpy(frame_basename, param[8].data.d_string, FRAME_BASENAME_LEN -1); + frame_basename[FRAME_BASENAME_LEN -1] = '\0'; + } + } + if ( n_params >= 13 ) + { + sel_mode = param[9].data.d_int32; + sel_case = param[10].data.d_int32; + sel_invert = param[11].data.d_int32; + if(param[12].data.d_string != NULL) + { + strncpy(l_sel_str, param[12].data.d_string, sizeof(l_sel_str) -1); + l_sel_str[sizeof(l_sel_str) -1] = '\0'; + } + } + + l_rc = gap_range_to_multilayer(run_mode, image_id, range_from, range_to, mode, nr, + framerate, frame_basename, FRAME_BASENAME_LEN, + sel_mode, sel_case, sel_invert, l_sel_str); + + } + + *nreturn_vals = 2; + values[1].type = PARAM_IMAGE; + values[1].data.d_int32 = l_rc; /* return the new generated image_id */ + } + else if (strcmp (name, "plug_in_gap_range_flatten") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 5) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + range_from = param[3].data.d_int32; /* frame nr to start */ + range_to = param[4].data.d_int32; /* frame nr to stop */ + + l_rc = gap_range_flatten(run_mode, image_id, range_from, range_to); + + } + } + else if (strcmp (name, "plug_in_gap_range_layer_del") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 6) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + range_from = param[3].data.d_int32; /* frame nr to start */ + range_to = param[4].data.d_int32; /* frame nr to stop */ + nr = param[5].data.d_int32; /* layerstack position (0 == on top) */ + + l_rc = gap_range_layer_del(run_mode, image_id, range_from, range_to, nr); + + } + } + else if (strcmp (name, "plug_in_gap_range_convert") == 0) + { + l_basename_ptr = NULL; + if (run_mode == RUN_NONINTERACTIVE) + { + if ((n_params != 10) && (n_params != 11)) + { + status = STATUS_CALLING_ERROR; + } + else + { + strncpy(l_extension, param[9].data.d_string, sizeof(l_extension) -1); + l_extension[sizeof(l_extension) -1] = '\0'; + if (n_params == 11) + { + l_basename_ptr = param[10].data.d_string; + } + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + range_from = param[3].data.d_int32; /* frame nr to start */ + range_to = param[4].data.d_int32; /* frame nr to stop */ + nr = param[5].data.d_int32; /* flatten (0 == no , 1 == flatten) */ + dest_type = param[6].data.d_int32; + dest_colors = param[7].data.d_int32; + dest_dither = param[8].data.d_int32; + + l_rc = gap_range_conv(run_mode, image_id, range_from, range_to, nr, + dest_type, dest_colors, dest_dither, + l_basename_ptr, l_extension); + + } + } + else if (strcmp (name, "plug_in_gap_anim_resize") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 7) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + new_width = param[3].data.d_int32; + new_height = param[4].data.d_int32; + offs_x = param[5].data.d_int32; + offs_y = param[6].data.d_int32; + + l_rc = gap_anim_sizechange(run_mode, ASIZ_RESIZE, image_id, + new_width, new_height, offs_x, offs_y); + + } + } + else if (strcmp (name, "plug_in_gap_anim_crop") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 7) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + new_width = param[3].data.d_int32; + new_height = param[4].data.d_int32; + offs_x = param[5].data.d_int32; + offs_y = param[6].data.d_int32; + + l_rc = gap_anim_sizechange(run_mode, ASIZ_CROP, image_id, + new_width, new_height, offs_x, offs_y); + + } + } + else if (strcmp (name, "plug_in_gap_anim_scale") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 5) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + new_width = param[3].data.d_int32; + new_height = param[4].data.d_int32; + + l_rc = gap_anim_sizechange(run_mode, ASIZ_SCALE, image_id, + new_width, new_height, 0, 0); + + } + } + else if (strcmp (name, "plug_in_gap_split") == 0) + { + l_rc = -1; + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 6) + { + status = STATUS_CALLING_ERROR; + } + else + { + strncpy(l_extension, param[5].data.d_string, sizeof(l_extension) -1); + l_extension[sizeof(l_extension) -1] = '\0'; + } + } + + if (status == STATUS_SUCCESS) + { + image_id = param[1].data.d_image; + inverse_order = param[3].data.d_int32; + no_alpha = param[4].data.d_int32; + + l_rc = gap_split_image(run_mode, image_id, + inverse_order, no_alpha, l_extension); + + } + *nreturn_vals = 2; + values[1].type = PARAM_IMAGE; + values[1].data.d_int32 = l_rc; /* return the new generated image_id */ + } + else if (strcmp (name, "plug_in_gap_mpeg_encode") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + else + { + /* planed: define non interactive PARAMS */ + l_extension[sizeof(l_extension) -1] = '\0'; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + /* planed: define non interactive PARAMS */ + + l_rc = gap_mpeg_encode(run_mode, image_id, MPEG_ENCODE /* more PARAMS */); + + } + } + else if (strcmp (name, "plug_in_gap_mpeg2encode") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + else + { + /* planed: define non interactive PARAMS */ + l_extension[sizeof(l_extension) -1] = '\0'; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + /* planed: define non interactive PARAMS */ + + l_rc = gap_mpeg_encode(run_mode, image_id, MPEG2ENCODE /* more PARAMS */); + + } + } + else if (strcmp (name, "plug_in_gap_shift") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 6) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + nr = param[3].data.d_int32; /* how many framenumbers to shift the framesequence */ + range_from = param[4].data.d_int32; /* frame nr to start */ + range_to = param[5].data.d_int32; /* frame nr to stop */ + + l_rc = gap_shift(run_mode, image_id, nr, range_from, range_to ); + + } + } + else if (strcmp (name, "plug_in_gap_modify") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 11) + { + status = STATUS_CALLING_ERROR; + } + else + { + if(param[9].data.d_string != NULL) + { + strncpy(l_sel_str, param[9].data.d_string, sizeof(l_sel_str) -1); + l_sel_str[sizeof(l_sel_str) -1] = '\0'; + } + if(param[10].data.d_string != NULL) + { + strncpy(l_layername, param[10].data.d_string, sizeof(l_layername) -1); + l_layername[sizeof(l_layername) -1] = '\0'; + } + } + } + + if (status == STATUS_SUCCESS) + { + + image_id = param[1].data.d_image; + range_from = param[3].data.d_int32; /* frame nr to start */ + range_to = param[4].data.d_int32; /* frame nr to stop */ + nr = param[5].data.d_int32; /* action_nr */ + sel_mode = param[6].data.d_int32; + sel_case = param[7].data.d_int32; + sel_invert = param[8].data.d_int32; + + l_rc = gap_mod_layer(run_mode, image_id, range_from, range_to, + nr, sel_mode, sel_case, sel_invert, + l_sel_str, l_layername); + + } + } + + /* ---------- return handling --------- */ + + if(l_rc < 0) + { + status = STATUS_EXECUTION_ERROR; + } + + + if (run_mode != RUN_NONINTERACTIVE) + gimp_displays_flush(); + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; + + /* remove LOCK on this image for all gap_plugins */ + l_lock.lock = 0; + l_lock.image_id = -1; + gimp_set_data (l_lockname, &l_lock, sizeof(l_lock)); + +} diff --git a/plug-ins/gap/gap_match.c b/plug-ins/gap/gap_match.c new file mode 100644 index 0000000000..49e53bc982 --- /dev/null +++ b/plug-ins/gap/gap_match.c @@ -0,0 +1,325 @@ +/* gap_match.c + * 1998.10.14 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * layername and layerstacknumber matching procedures + * + */ +/* 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: + * version 0.97.00 1998.10.14 hof: - created module + */ + +/* SYSTEM (UNIX) includes */ +#include +#include +#include +#include +#include + + +/* GIMP includes */ +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_match.h" + +int +p_is_empty (char *str) +{ + if(str == NULL) return(TRUE); + if(*str == '\0') return(TRUE); + + while(*str != '\0') + { + if(*str != ' ') return(FALSE); + str++; + } + + return(TRUE); +} + +/* ============================================================================ + * p_substitute_framenr + * copy new_layername to buffer + * and substitute [####] by curr frame number + * ============================================================================ + */ +void +p_substitute_framenr (char *buffer, int buff_len, char *new_layername, long curr) +{ + int l_idx; + int l_digits; + int l_cpy; + char l_fmt_str[21]; + + l_fmt_str[0] = '%'; + l_fmt_str[1] = '0'; + l_digits = 0; + + l_idx = 0; + if(new_layername != NULL) + { + while((l_idx < (buff_len-1)) && (*new_layername != '\0') ) + { + l_cpy = 1; + switch(*new_layername) + { + case '[': + if((new_layername[1] == '#') && (l_digits == 0)) + { + l_cpy = 0; + l_digits = 1; + } + break; + case '#': + if(l_digits > 0) + { + l_cpy = 0; + l_digits++; + } + break; + case ']': + if(l_digits > 0) + { + l_digits--; + sprintf(&l_fmt_str[2], "%dd", l_digits); + sprintf(&buffer[l_idx], l_fmt_str, (int)curr); + l_idx += l_digits; + l_digits = 0; + l_cpy = 0; + } + break; + default: + l_digits = 0; + break; + } + if(l_cpy != 0) + { + buffer[l_idx] = (*new_layername); + l_idx++; + } + + new_layername++; + + } + } + + buffer[l_idx] = '\0'; +} /* end p_substitute_framenr */ + + +void str_toupper(char *str) +{ + if(str != NULL) + { + while(*str != '\0') + { + *str = toupper(*str); + str++; + } + } +} + + +/* match layer_idx (int) with pattern + * pattern contains a list like that: + * "0, 3-4, 7, 10" + */ +int p_match_number(gint32 layer_idx, char *pattern) +{ + char l_digit_buff[128]; + char *l_ptr; + int l_idx; + gint32 l_num, l_range_start; + + l_idx = 0; + l_num = -1; l_range_start = -1; + for(l_ptr = pattern; 1 == 1; l_ptr++) + { + if(isdigit(*l_ptr)) + { + l_digit_buff[l_idx] = *l_ptr; /* collect digits here */ + l_idx++; + } + else + { + if(l_idx > 0) + { + /* now we are one character past a number */ + l_digit_buff[l_idx] = '\0'; + l_num = atol(l_digit_buff); /* scann the number */ + + if(l_num == layer_idx) + { + return(TRUE); /* matches number exactly */ + } + + if((l_range_start >= 0) + && (layer_idx >= l_range_start) && (layer_idx <= l_num )) + { + return(TRUE); /* matches given range */ + } + + l_range_start = -1; /* disable number for opening a range */ + l_idx = 0; + } + + switch(*l_ptr) + { + case '\0': + return (FALSE); + break; + case ' ': + case '\t': + break; + case ',': + l_num = -1; /* disable number for opening a range */ + break; + case '-': + if (l_num >= 0) + { + l_range_start = l_num; /* prev. number opens a range */ + } + break; + default: + /* found illegal characters */ + /* return (FALSE); */ + l_num = -1; /* disable number for opening a range */ + l_range_start = -1; /* disable number for opening a range */ + + break; + } + } + } /* end for */ + + return(FALSE); +} /* end p_match_number */ + + +/* simple stringmatching without wildcards */ +int p_match_name(char *layername, char *pattern, gint32 mode, gint32 case_sensitive) +{ + int l_idx; + int l_llen; + int l_plen; + char *l_name_ptr; + char *l_patt_ptr; + char l_name_buff[256]; + char l_patt_buff[256]; + + if(pattern == NULL) return (FALSE); + if(layername == NULL) return (FALSE); + + if(case_sensitive) + { + /* case sensitive can compare on the originals */ + l_name_ptr = layername; + l_patt_ptr = pattern; + } + else + { + /* ignore case by converting everything to UPPER before comare */ + strcpy (l_name_buff, layername); + strcpy (l_patt_buff, pattern); + + str_toupper (l_name_buff); + str_toupper (l_patt_buff); + + l_name_ptr = l_name_buff; + l_patt_ptr = l_patt_buff; + } + + switch (mode) + { + case MTCH_EQUAL: + if (0 == strcmp(l_name_ptr, l_patt_ptr)) + { + return(TRUE); + } + break; + case MTCH_START: + l_plen = strlen(l_patt_ptr); + if (0 == strncmp(l_name_ptr, l_patt_ptr, l_plen)) + { + return(TRUE); + } + break; + case MTCH_END: + l_llen = strlen(l_name_ptr); + l_plen = strlen(l_patt_ptr); + if(l_llen > l_plen) + { + if(0 == strncmp(&l_name_ptr[l_llen - l_plen], l_patt_ptr, l_plen)) + { + return(TRUE); + } + } + break; + case MTCH_ANYWHERE: + l_llen = strlen(l_name_ptr); + l_plen = strlen(l_patt_ptr); + for(l_idx = 0; l_idx <= (l_llen - l_plen); l_idx++) + { + if (strncmp(&l_name_ptr[l_idx], l_patt_ptr, l_plen) == 0) + { + return (TRUE); + } + } + break; + default: + break; + + } + + return (FALSE); + +} + +int p_match_layer(gint32 layer_idx, char *layername, char *pattern, + gint32 mode, gint32 case_sensitive, gint32 invert, + gint nlayers, gint32 layer_id) +{ + int l_rc; + switch(mode) + { + case MTCH_NUMBERLIST: + l_rc = p_match_number(layer_idx, pattern); + break; + case MTCH_INV_NUMBERLIST: + l_rc = p_match_number((nlayers -1) - layer_idx, pattern); + break; + case MTCH_ALL_VISIBLE: + l_rc = gimp_layer_get_visible(layer_id); + break; + default: + l_rc = p_match_name(layername, pattern, mode, case_sensitive); + break; + } + + if(invert == TRUE) + { + if(l_rc == FALSE) { return(TRUE); } + return(FALSE); + } + return (l_rc); +} diff --git a/plug-ins/gap/gap_match.h b/plug-ins/gap/gap_match.h new file mode 100644 index 0000000000..62545fbebb --- /dev/null +++ b/plug-ins/gap/gap_match.h @@ -0,0 +1,53 @@ +/* gap_match.h + * + * GAP ... Gimp Animation Plugins + * + */ +/* 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: + * version 0.97.00 1998.10.14 hof: - created module + */ + + +#ifndef _GAP_MATCH_H +#define _GAP_MATCH_H + +#include "libgimp/gimp.h" + +#define MTCH_EQUAL 0 +#define MTCH_START 1 +#define MTCH_END 2 +#define MTCH_ANYWHERE 3 +#define MTCH_NUMBERLIST 4 +#define MTCH_INV_NUMBERLIST 5 +#define MTCH_ALL_VISIBLE 6 + +int p_is_empty (char *str); +void p_substitute_framenr (char *buffer, int buff_len, char *new_layername, long curr); + +void str_toupper(char *str); + +int p_match_number(gint32 layer_id, char *pattern); +int p_match_name(char *layername, char *pattern, gint32 mode, gint32 case_sensitive); +int p_match_layer(gint32 layer_idx, char *layername, char *pattern, + gint32 mode, gint32 case_sensitive, gint32 invert, + gint nlayers, gint32 layer_id); + +#endif diff --git a/plug-ins/gap/gap_mod_layer.c b/plug-ins/gap/gap_mod_layer.c new file mode 100644 index 0000000000..3f248f3026 --- /dev/null +++ b/plug-ins/gap/gap_mod_layer.c @@ -0,0 +1,1203 @@ +/* gap_mod_layer.c + * 1998.10.14 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * modify Layer (perform actions (like raise, set visible, apply filter) + * - foreach selected layer + * - in each frame of the selected framerange) + * + */ +/* 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: + * version 0.98.00 1998.11.27 hof: - use new module gap_pdb_calls.h + * version 0.97.00 1998.10.19 hof: - created module + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_arr_dialog.h" +#include "gap_filter.h" +#include "gap_filter_pdb.h" +#include "gap_pdb_calls.h" +#include "gap_match.h" +#include "gap_lib.h" +#include "gap_range_ops.h" +#include "gap_mod_layer.h" + + +extern int gap_debug; /* ==0 ... dont print debug infos */ + + +/* ============================================================================ + * p_layer_modify_dialog + * retcode + * 0 ... Generate Paramfile + * 1 ... Generate Paramfile and start mpeg_encode + * ============================================================================ + */ +static +int p_layer_modify_dialog(t_anim_info *ainfo_ptr, + gint32 *range_from, gint32 *range_to, + gint32 *action_mode, gint32 *sel_mode, + gint32 *sel_case, gint32 *sel_invert, + char *sel_pattern, char *new_layername) +{ + static t_arr_arg argv[9]; + static t_but_arg b_argv[2]; + gint l_rc; + + static char l_buf[MAX_LAYERNAME]; + + /* Layer select modes */ + static char *sel_args[7] = {"Pattern is equal to LayerName", + "Pattern is Start of LayerName", + "Pattern is End of Layername", + "Pattern is a Part of LayerName", + "Pattern is LayerstackNumber List", + "Pattern is REVERSE-stack List", + "All Visible (ignore Pattern)" + }; + static char *sel_help[7] = {"select all Layers where Layername is equal to Pattern", + "select all Layers where Layername starts with Pattern", + "select all Layers where Layername ends up with Pattern", + "select all Layers where Layername contains Pattern", + "select Layerstack positions.\n0, 4-5, 8\nwhere 0 == Top-layer", + "select Layerstack positions.\n0, 4-5, 8\nwhere 0 == BG-layer", + "select all visible Layers" + }; + + /* action items what to do with the selected layer(s) */ + static char *action_args[13] = {"set Layer(s) visible", + "set Layer(s) invisible", + "set Layer(s) linked", + "set Layer(s) unlinked", + "raise Layer(s)", + "lower Layer(s)", + "merge Layer(s) expand as necessary", + "merge Layer(s) clipped to image", + "merge Layer(s) clipped to bg-layer", + "apply filter on Layer(s)", + "duplicate Layer(s)", + "delete Layer(s)", + "rename Layer(s)" + }; + + +/* + static char *action_help[13] = {"set all selected layers visible", + "set all selected layers invisible", + "set all selected layers linked", + "set all selected layers unlinked", + "raise all selected layers", + "lower all selected layers", + "merge expand as necessary", + "merge clipped to image", + "merge clipped to bg-layer", + "APPLY FILTER to all selected layers", + "duplicate all selected layers", + "delete REMOVES all selected layers", + "rename all selected layers\nto NewLayername." + }; +*/ + + l_rc = -1; + + sprintf(l_buf, "Perform function on one or more Layer(s)\nin all frames of the selected framerange\n"); + + /* the 3 Action Buttons */ + b_argv[0].but_txt = "OK"; + b_argv[0].but_val = 0; + b_argv[1].but_txt = "Cancel"; + b_argv[1].but_val = -1; + + p_init_arr_arg(&argv[0], WGT_LABEL); + argv[0].label_txt = &l_buf[0]; + + p_init_arr_arg(&argv[1], WGT_INT_PAIR); + argv[1].constraint = TRUE; + argv[1].label_txt = "From Frame:"; + argv[1].help_txt = "first handled frame"; + 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; + + p_init_arr_arg(&argv[2], WGT_INT_PAIR); + argv[2].constraint = TRUE; + argv[2].label_txt = "To Frame:"; + argv[2].help_txt = "last handled frame"; + argv[2].int_min = (gint)ainfo_ptr->first_frame_nr; + argv[2].int_max = (gint)ainfo_ptr->last_frame_nr; + argv[2].int_ret = (gint)ainfo_ptr->last_frame_nr; + + + /* Layer select mode RADIO buttons */ + p_init_arr_arg(&argv[3], WGT_RADIO); + argv[3].label_txt = "Select Layer(s):"; + argv[3].radio_argc = 7; + argv[3].radio_argv = sel_args; + argv[3].radio_help_argv = sel_help; + argv[3].radio_ret = 4; + + /* Layer select pattern string */ + sprintf (sel_pattern, "0"); + p_init_arr_arg(&argv[4], WGT_TEXT); + argv[4].label_txt ="Select Pattern:"; + argv[4].entry_width = 140; /* pixel */ + argv[4].help_txt ="String to identify layer names \nor layerstack position numbers\n0,3-5"; + argv[4].text_buf_len = MAX_LAYERNAME; + argv[4].text_buf_ret = sel_pattern; + + /* case sensitive checkbutton */ + p_init_arr_arg(&argv[5], WGT_TOGGLE); + argv[5].label_txt = "Case sensitive"; + argv[5].help_txt = "Lowercase and UPPERCASE letters are considered as different"; + argv[5].int_ret = 1; + + /* invert selection checkbutton */ + p_init_arr_arg(&argv[6], WGT_TOGGLE); + argv[6].label_txt = "Invert Selection"; + argv[6].help_txt = "Perform actions on all unselected Layers"; + argv[6].int_ret = 0; + + /* desired action to perform OPTIONMENU */ + p_init_arr_arg(&argv[7], WGT_OPTIONMENU); + argv[7].label_txt = "Function :"; + argv[7].radio_argc = 13; + argv[7].radio_argv = action_args; + /* argv[7].radio_help_argv = action_help */ + argv[7].help_txt = "Function to be performed on all selected layers"; + argv[7].radio_ret = 0; + + /* a new name for the handled Layer(s) */ + *new_layername = '\0'; + p_init_arr_arg(&argv[8], WGT_TEXT); + argv[8].label_txt ="New Layername:"; + argv[8].entry_width = 140; /* pixel */ + argv[8].help_txt ="New Layername for all handled layers \n[####] is replaced by frame number\n(is used on function rename only)"; + argv[8].text_buf_len = MAX_LAYERNAME; + argv[8].text_buf_ret = new_layername; + + + l_rc = p_array_std_dialog("Frames Modify", + "Settings", + 9, argv, /* widget array */ + 2, b_argv, /* button array */ + 0); + + /* return the entered values */ + *range_from = argv[1].int_ret; + *range_to = argv[2].int_ret; + *sel_mode = argv[3].int_ret; + /* [4] sel_pattern */ + *sel_case = argv[5].int_ret; + *sel_invert = argv[6].int_ret; + *action_mode = argv[7].int_ret; + /* [8] l_new_layername */ + + + return (l_rc); +} /* end p_layer_modify_dialog */ + + +/* ============================================================================ + * p_pitstop_dialog + * return -1 on CANCEL + * 0 on Continue (OK) + * ============================================================================ + */ +static gint +p_pitstop_dialog(gint text_flag, char *filter_procname) +{ + char *l_env; + char l_msg[512]; + static t_but_arg l_but_argv[2]; + gint l_but_argc; + gint l_argc; + static t_arr_arg l_argv[1]; + int l_continue; + + + + + l_but_argv[0].but_txt = "Continue"; + l_but_argv[0].but_val = 0; + l_but_argv[1].but_txt = "Cancel"; + l_but_argv[1].but_val = -1; + + l_but_argc = 2; + l_argc = 0; + + /* optional dialog between both calls (to see the effect of 1.call) */ + l_env = getenv("GAP_FILTER_PITSTOP"); + if(l_env != NULL) + { + if((*l_env == 'N') || (*l_env == 'n')) + { + return 0; /* continue without question */ + } + } + if(text_flag == 0) + { + sprintf(l_msg, "2.nd call of %s\n(define end-settings)", filter_procname); + } + else + { + sprintf(l_msg, "Non-Interactive call of %s\n(for all selected layers)", filter_procname); + } + l_continue = p_array_std_dialog ("Animated Filter apply", l_msg, + l_argc, l_argv, + l_but_argc, l_but_argv, 0); + + return (l_continue); + +} /* end p_pitstop_dialog */ + + +/* ============================================================================ + * p_get_1st_selected + * return index of the 1.st selected layer + * or -1 if no selection was found + * ============================================================================ + */ +int +p_get_1st_selected (t_LayliElem * layli_ptr, gint nlayers) +{ + int l_idx; + + for(l_idx = 0; l_idx < nlayers; l_idx++) + { + if(layli_ptr[l_idx].selected != FALSE) + { + return (l_idx); + } + } + return(-1); +} /* end p_get_1st_selected */ + + +/* ============================================================================ + * p_alloc_layli + * returns pointer to a new allocated image_id of the new created multilayer image + * (or NULL on error) + * ============================================================================ + */ + +t_LayliElem * +p_alloc_layli(gint32 image_id, gint32 *l_sel_cnt, gint *nlayers, + gint32 sel_mode, + gint32 sel_case, + gint32 sel_invert, + char *sel_pattern ) +{ + gint32 *l_layers_list; + gint32 l_layer_id; + gint32 l_idx; + t_LayliElem *l_layli_ptr; + char *l_layername; + + *l_sel_cnt = 0; + + l_layers_list = gimp_image_get_layers(image_id, nlayers); + if(l_layers_list == NULL) + { + return(NULL); + } + + l_layli_ptr = calloc((*nlayers), sizeof(t_LayliElem)); + if(l_layli_ptr == NULL) + { + g_free (l_layers_list); + return(NULL); + } + + for(l_idx = 0; l_idx < (*nlayers); l_idx++) + { + l_layer_id = l_layers_list[l_idx]; + l_layername = gimp_layer_get_name(l_layer_id); + l_layli_ptr[l_idx].layer_id = l_layer_id; + l_layli_ptr[l_idx].visible = gimp_layer_get_visible(l_layer_id); + l_layli_ptr[l_idx].selected = p_match_layer(l_idx, + l_layername, + sel_pattern, + sel_mode, + sel_case, + sel_invert, + *nlayers, l_layer_id); + if(l_layli_ptr[l_idx].selected != FALSE) + { + (*l_sel_cnt)++; /* count all selected layers */ + } + if(gap_debug) printf("gap: p_alloc_layli [%d] id:%d, sel:%d %s\n", + (int)l_idx, (int)l_layer_id, + (int)l_layli_ptr[l_idx].selected, l_layername); + g_free (l_layername); + } + + g_free (l_layers_list); + + return( l_layli_ptr ); +} /* end p_alloc_layli */ + +/* ============================================================================ + * p_prevent_empty_image + * check if the resulting image has at least one layer + * (gimp 1.0.0 tends to crash on layerless images) + * ============================================================================ + */ + +void p_prevent_empty_image(gint32 image_id) +{ + GImageType l_type; + guint l_width, l_height; + gint32 l_layer_id; + gint l_nlayers; + gint32 *l_layers_list; + + l_layers_list = gimp_image_get_layers(image_id, &l_nlayers); + if(l_layers_list != NULL) + { + g_free (l_layers_list); + } + else l_nlayers = 0; + + if(l_nlayers == 0) + { + /* the resulting image has no layer, add a transparent dummy layer */ + + /* get info about the image */ + l_width = gimp_image_width(image_id); + l_height = gimp_image_height(image_id); + l_type = gimp_image_base_type(image_id); + + /* add a transparent dummy layer */ + l_layer_id = gimp_layer_new(image_id, "dummy", + l_width, l_height, l_type, + 0.0, /* Opacity full transparent */ + 0); /* NORMAL */ + gimp_image_add_layer(image_id, l_layer_id, 0); + } + +} /* end p_prevent_empty_image */ + + +/* ============================================================================ + * p_raise_layer + * raise layer (check if possible before) + * (without the check each failed attempt would open an inf window) + * ============================================================================ + */ +static void +p_raise_layer (gint32 image_id, gint32 layer_id, t_LayliElem * layli_ptr, gint nlayers) +{ + if(! gimp_drawable_has_alpha (layer_id)) return; /* has no alpha channel */ + + if(layli_ptr[0].layer_id == layer_id) return; /* is already on top */ + + gimp_image_raise_layer(image_id, layer_id); +} /* end p_raise_layer */ + +static void +p_lower_layer (gint32 image_id, gint32 layer_id, t_LayliElem * layli_ptr, gint nlayers) +{ + if(! gimp_drawable_has_alpha (layer_id)) return; /* has no alpha channel */ + + if(layli_ptr[nlayers-1].layer_id == layer_id) return; /* is already on bottom */ + + if(nlayers > 1) + { + if((layli_ptr[nlayers-2].layer_id == layer_id) + && (! gimp_drawable_has_alpha (layli_ptr[nlayers-1].layer_id))) + { + /* the layer is one step above a "bottom-layer without alpha" */ + return; + } + } + + gimp_image_lower_layer(image_id, layer_id); +} /* end p_lower_layer */ + +/* ============================================================================ + * p_apply_action + * + * perform function (defined by action_mode) + * on all selcted layer(s) + * + * returns 0 if all done OK + * (or -1 on error) + * ============================================================================ + */ + +static int +p_apply_action(gint32 image_id, + gint32 action_mode, + t_LayliElem *layli_ptr, + gint nlayers, + gint32 sel_cnt, + + long from, + long to, + long curr, + char *new_layername, + char *filter_procname + ) +{ + int l_idx; + int l_rc; + gint32 l_layer_id; + gint32 l_new_layer_id; + gint l_merge_mode; + gint l_vis_result; + char l_name_buff[MAX_LAYERNAME]; + + if(gap_debug) fprintf(stderr, "gap: p_apply_action START\n"); + + l_rc = 0; + l_merge_mode = -44; /* none of the flatten modes */ + + if(action_mode == ACM_MERGE_EXPAND) l_merge_mode = FLAM_MERG_EXPAND; + if(action_mode == ACM_MERGE_IMG) l_merge_mode = FLAM_MERG_CLIP_IMG; + if(action_mode == ACM_MERGE_BG) l_merge_mode = FLAM_MERG_CLIP_BG; + + /* merge actions require one call per image */ + if(l_merge_mode != (-44)) + { + if(sel_cnt < 2) + { + return(0); /* OK, nothing to merge */ + } + + l_vis_result = FALSE; + + /* set selected layers visible, all others invisible for merge */ + for(l_idx = 0; l_idx < nlayers; l_idx++) + { + if(layli_ptr[l_idx].selected == FALSE) + { + gimp_layer_set_visible(layli_ptr[l_idx].layer_id, FALSE); + } + else + { + if(gimp_layer_get_visible(layli_ptr[l_idx].layer_id)) + { + /* result will we visible if at least one of the + * selected layers was visible before + */ + l_vis_result = TRUE; + } + gimp_layer_set_visible(layli_ptr[l_idx].layer_id, TRUE); + } + } + + /* merge all visible layers (i.e. all selected layers) */ + l_layer_id = gimp_image_merge_visible_layers (image_id, l_merge_mode); + if(l_vis_result == FALSE) + { + gimp_layer_set_visible(l_layer_id, FALSE); + } + + /* if new_layername is available use that name + * for the new merged layer + */ + if (!p_is_empty (new_layername)) + { + p_substitute_framenr(&l_name_buff[0], sizeof(l_name_buff), + new_layername, curr); + gimp_layer_set_name(l_layer_id, &l_name_buff[0]); + } + + /* restore visibility flags after merge */ + for(l_idx = 0; l_idx < nlayers; l_idx++) + { + if(layli_ptr[l_idx].selected == FALSE) + { + gimp_layer_set_visible(layli_ptr[l_idx].layer_id, + layli_ptr[l_idx].visible); + } + } + + return(0); + } + + /* -----------------------------*/ + /* non-merge actions require calls foreach selected layer */ + for(l_idx = 0; (l_idx < nlayers) && (l_rc == 0); l_idx++) + { + l_layer_id = layli_ptr[l_idx].layer_id; + + /* apply function defined by action_mode */ + if(layli_ptr[l_idx].selected != FALSE) + { + if(gap_debug) fprintf(stderr, "gap: p_apply_action on selected LayerID:%d layerstack:%d\n", + (int)l_layer_id, (int)l_idx); + switch(action_mode) + { + case ACM_SET_VISIBLE: + gimp_layer_set_visible(l_layer_id, TRUE); + break; + case ACM_SET_INVISIBLE: + gimp_layer_set_visible(l_layer_id, FALSE); + break; + case ACM_SET_LINKED: + l_rc = p_layer_set_linked (l_layer_id, TRUE); + break; + case ACM_SET_UNLINKED: + l_rc = p_layer_set_linked (l_layer_id, FALSE); + break; + case ACM_RAISE: + p_raise_layer(image_id, l_layer_id, layli_ptr, nlayers); + break; + case ACM_LOWER: + p_lower_layer(image_id, l_layer_id, layli_ptr, nlayers); + break; + case ACM_APPLY_FILTER: + l_rc = p_call_plugin(filter_procname, + image_id, + l_layer_id, + RUN_WITH_LAST_VALS); + if(gap_debug) fprintf(stderr, "gap: p_apply_action FILTER:%s rc =%d\n", + filter_procname, (int)l_rc); + break; + case ACM_DUPLICATE: + l_new_layer_id = gimp_layer_copy(l_layer_id); + gimp_image_add_layer (image_id, l_new_layer_id, -1); + if (!p_is_empty (new_layername)) + { + p_substitute_framenr(&l_name_buff[0], sizeof(l_name_buff), + new_layername, curr); + gimp_layer_set_name(l_new_layer_id, &l_name_buff[0]); + } + break; + case ACM_DELETE: + gimp_image_remove_layer(image_id, l_layer_id); + break; + case ACM_RENAME: + p_substitute_framenr(&l_name_buff[0], sizeof(l_name_buff), + new_layername, curr); + gimp_layer_set_name(l_layer_id, &l_name_buff[0]); + break; + default: + break; + } + } + } + + return (l_rc); +} /* end p_apply_action */ + + +/* ============================================================================ + * p_do_filter_dialogs + * additional dialog steps + * a) gap_pdb_browser (select the filter) + * b) 1st interactive filtercall + * c) 1st pitstop dialog + * ============================================================================ + */ +static int +p_do_filter_dialogs(t_anim_info *ainfo_ptr, + gint32 image_id, gint32 *dpy_id, + t_LayliElem * layli_ptr, gint nlayers , + char *filter_procname, int filt_len, + gint *plugin_data_len, + t_apply_mode *apply_mode + ) +{ + t_gap_db_browse_result l_browser_result; + gint32 l_layer_id; + int l_rc; + int l_idx; + static char l_key_from[512]; + static gint l_gtk_init = TRUE; /* gkt_init at 1.st call */ + + if(ainfo_ptr->run_mode == RUN_INTERACTIVE) + { + l_gtk_init = FALSE; /* gtk_init was done in 1.st modify dialog before */ + } + + + /* GAP-PDB-Browser Dialog */ + /* ---------------------- */ + if(gap_db_browser_dialog("Select Filter for Animated frames-apply", + "Apply Constant", + "Apply Varying", + p_constraint_proc, + p_constraint_proc_sel1, + p_constraint_proc_sel2, + &l_browser_result, + l_gtk_init /* do not call gtk_init */ + ) + < 0) + { + if(gap_debug) fprintf(stderr, "DEBUG: gap_db_browser_dialog cancelled\n"); + return -1; + } + + p_arr_gtk_init(FALSE); /* disable the initial gtk_init in gap_arr_dialog's + * (gtk_init was done before) + */ + + strncpy(filter_procname, l_browser_result.selected_proc_name, filt_len-1); + filter_procname[filt_len-1] = '\0'; + if(l_browser_result.button_nr == 1) *apply_mode = PTYP_VARYING_LINEAR; + else *apply_mode = PAPP_CONSTANT; + + /* 1.st INTERACTIV Filtercall dialog */ + /* --------------------------------- */ + /* check for the Plugin */ + + l_rc = p_procedure_available(filter_procname, PTYP_CAN_OPERATE_ON_DRAWABLE); + if(l_rc < 0) + { + fprintf(stderr, "ERROR: Plugin not available or wrong type %s\n", filter_procname); + return -1; + } + + /* get 1.st selected layer (of 1.st handled frame in range ) */ + l_idx = p_get_1st_selected(layli_ptr, nlayers); + if(l_idx < 0) + { + fprintf(stderr, "ERROR: No layer selected in 1.st handled frame\n"); + return (-1); + } + l_layer_id = layli_ptr[l_idx].layer_id; + + /* open a view for the 1.st handled frame */ + *dpy_id = gimp_display_new (image_id); + + l_rc = p_call_plugin(filter_procname, image_id, l_layer_id, RUN_INTERACTIVE); + + /* OOPS: cant delete the display here, because + * closing the last display seems to free up + * at least parts of the image, + * and causes crashes if the image_id is used + * in further gimp procedures + */ + /* gimp_display_delete(*dpy_id); */ + + /* get values, then store with suffix "_ITER_FROM" */ + *plugin_data_len = p_get_data(filter_procname); + if(*plugin_data_len > 0) + { + sprintf(l_key_from, "%s_ITER_FROM", filter_procname); + p_set_data(l_key_from, *plugin_data_len); + } + else + { + return (-1); + } + + if(*apply_mode != PTYP_VARYING_LINEAR) + { + return (p_pitstop_dialog(1, filter_procname)); + } + + return(0); +} /* end p_do_filter_dialogs */ + + +/* ============================================================================ + * p_do_2nd_filter_dialogs + * d) [ 2nd interactive filtercall + * e) 2nd pitstop dialog ] + * + * (temporary) open the last frame of the range + * get its 1.st selected laye + * and do the Interctive Filtercall (to get the end-values) + * + * then close everything (without save). + * (the last frame will be processed later, with all its selected layers) + * ============================================================================ + */ +static gint +p_do_2nd_filter_dialogs(char *filter_procname, + t_apply_mode l_apply_mode, + char *last_frame_filename, + gint32 sel_mode, gint32 sel_case, + gint32 sel_invert, char *sel_pattern + ) +{ + gint32 l_layer_id; + gint32 l_dpy_id; + int l_rc; + int l_idx; + static char l_key_to[512]; + gint32 l_last_image_id; + t_LayliElem *l_layli_ptr; + gint l_nlayers; + gint32 l_sel_cnt; + gint l_plugin_data_len; + + l_layli_ptr = NULL; + l_rc = -1; /* assume cancel or error */ + l_last_image_id = -1; + l_dpy_id = -1; + + /* 2.nd INTERACTIV Filtercall dialog */ + /* --------------------------------- */ + if(last_frame_filename == NULL) + { + return (-1); /* there is no 2.nd frame for 2.nd filter call */ + } + + if(p_pitstop_dialog(0, filter_procname) < 0) + goto cleanup; + + /* load last frame into temporary image */ + l_last_image_id = p_load_image(last_frame_filename); + if (l_last_image_id < 0) + goto cleanup; + + /* get informations (id, visible, selected) about all layers */ + l_layli_ptr = p_alloc_layli(l_last_image_id, &l_sel_cnt, &l_nlayers, + sel_mode, sel_case, sel_invert, sel_pattern); + + if (l_layli_ptr == NULL) + goto cleanup; + + /* get 1.st selected layer (of last handled frame in range ) */ + l_idx = p_get_1st_selected(l_layli_ptr, l_nlayers); + if(l_idx < 0) + { + p_msg_win (RUN_INTERACTIVE, "GAP Modify: No layer selected in last handled frame\n"); + goto cleanup; + } + l_layer_id = l_layli_ptr[l_idx].layer_id; + + /* open a view for the last handled frame */ + l_dpy_id = gimp_display_new (l_last_image_id); + + /* 2.nd INTERACTIV Filtercall dialog */ + /* --------------------------------- */ + l_rc = p_call_plugin(filter_procname, l_last_image_id, l_layer_id, RUN_INTERACTIVE); + + /* get values, then store with suffix "_ITER_TO" */ + l_plugin_data_len = p_get_data(filter_procname); + if(l_plugin_data_len <= 0) + goto cleanup; + + sprintf(l_key_to, "%s_ITER_TO", filter_procname); + p_set_data(l_key_to, l_plugin_data_len); + + + l_rc = p_pitstop_dialog(1, filter_procname); + +cleanup: + if(l_dpy_id >= 0) gimp_display_delete(l_dpy_id); + if(l_last_image_id >= 0) gimp_image_delete(l_last_image_id); + if(l_layli_ptr != NULL) free(l_layli_ptr); + + return (l_rc); + + +} /* end p_do_2nd_filter_dialogs */ + + +/* ============================================================================ + * p_frames_modify + * + * foreach frame of the range (given by range_from and range_to) + * perform function defined by action_mode + * on all selected layer(s) described by sel_mode, sel_case + * sel_invert and sel_pattern + * returns 0 if all done OK + * (or -1 on error or cancel) + * ============================================================================ + */ +static gint32 +p_frames_modify(t_anim_info *ainfo_ptr, + long range_from, long range_to, + gint32 action_mode, gint32 sel_mode, + gint32 sel_case, gint32 sel_invert, + char *sel_pattern, char *new_layername) +{ + long l_cur_frame_nr; + long l_step, l_begin, l_end; + gint32 l_tmp_image_id; + gint32 l_dpy_id; + gint l_nlayers; + gdouble l_percentage, l_percentage_step; + int l_rc; + int l_idx; + gint32 l_sel_cnt; + t_LayliElem *l_layli_ptr; + + GParam *l_params; + gint l_retvals; + gint l_plugin_data_len; + char l_filter_procname[256]; + char *l_plugin_iterator; + gdouble l_cur_step; + gint l_total_steps; + t_apply_mode l_apply_mode; + char *l_last_frame_filename; + + + + + if(gap_debug) fprintf(stderr, "gap: p_frames_modify START, action_mode=%d sel_mode=%d case=%d, invert=%d patt:%s:\n", + (int)action_mode, (int)sel_mode, (int)sel_case, (int)sel_invert, sel_pattern); + + l_percentage = 0.0; + if(ainfo_ptr->run_mode == RUN_INTERACTIVE) + { + gimp_progress_init("Modifying Frames/Layer(s) .."); + } + + + l_begin = range_from; + l_end = range_to; + l_tmp_image_id = -1; + l_layli_ptr = NULL; + l_rc = 0; + l_plugin_iterator = NULL; + l_plugin_data_len = 0; + l_cur_step = 0; + l_apply_mode = PAPP_CONSTANT; + l_dpy_id = -1; + l_last_frame_filename = NULL; + + /* init step direction */ + if(range_from > range_to) + { + l_step = -1; /* operate in descending (reverse) order */ + l_percentage_step = 1.0 / ((1.0 + range_from) - range_to); + + if(range_to < ainfo_ptr->first_frame_nr) + { l_begin = ainfo_ptr->first_frame_nr; + } + if(range_from > ainfo_ptr->last_frame_nr) + { l_end = ainfo_ptr->last_frame_nr; + } + + l_total_steps = l_begin - l_end; + } + else + { + l_step = 1; /* operate in ascending order */ + l_percentage_step = 1.0 / ((1.0 + range_to) - range_from); + + if(range_from < ainfo_ptr->first_frame_nr) + { l_begin = ainfo_ptr->first_frame_nr; + } + if(range_to > ainfo_ptr->last_frame_nr) + { l_end = ainfo_ptr->last_frame_nr; + } + + l_total_steps = l_end - l_begin; + } + + l_total_steps--; + + l_cur_frame_nr = l_begin; + while(1) /* loop foreach frame in range */ + { + if(gap_debug) fprintf(stderr, "p_frames_modify While l_cur_frame_nr = %d\n", (int)l_cur_frame_nr); + + /* build the frame name */ + if(ainfo_ptr->new_filename != NULL) free(ainfo_ptr->new_filename); + ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, + l_cur_frame_nr, + ainfo_ptr->extension); + if(ainfo_ptr->new_filename == NULL) + goto error; + + /* load current frame into temporary image */ + l_tmp_image_id = p_load_image(ainfo_ptr->new_filename); + if(l_tmp_image_id < 0) + goto error; + + /* get informations (id, visible, selected) about all layers */ + l_layli_ptr = p_alloc_layli(l_tmp_image_id, &l_sel_cnt, &l_nlayers, + sel_mode, sel_case, sel_invert, sel_pattern); + + if(l_layli_ptr == NULL) + { + printf("gap: p_frames_modify: cant alloc layer info list\n"); + goto error; + } + + if((l_cur_frame_nr == l_begin) && (action_mode == ACM_APPLY_FILTER)) + { + /* ------------- 1.st frame: extra dialogs for APPLY_FILTER ---------- */ + + if(l_sel_cnt < 1) + { + p_msg_win(RUN_INTERACTIVE, "No selected Layer in start frame\n"); + goto error; + } + + if(l_begin != l_end) + { + l_last_frame_filename = p_alloc_fname(ainfo_ptr->basename, + l_end, + ainfo_ptr->extension); + } + + /* additional dialog steps a) gap_pdb_browser (select the filter) + * b) 1st interactive filtercall + * c) 1st pitstop dialog + * d) [ 2nd interactive filtercall + * e) 2nd pitstop dialog ] + */ + + l_rc = p_do_filter_dialogs(ainfo_ptr, + l_tmp_image_id, &l_dpy_id, + l_layli_ptr, l_nlayers, + &l_filter_procname[0], sizeof(l_filter_procname), + &l_plugin_data_len, + &l_apply_mode + ); + + if(l_last_frame_filename != NULL) + { + if((l_rc == 0) && (l_apply_mode == PTYP_VARYING_LINEAR)) + { + l_rc = p_do_2nd_filter_dialogs(&l_filter_procname[0], + l_apply_mode, + l_last_frame_filename, + sel_mode, sel_case, sel_invert, sel_pattern + ); + } + + free(l_last_frame_filename); + l_last_frame_filename = NULL; + } + + /* the 1st selected layer has been filtered + * in the INTERACTIVE call b) + * therefore we unselect this layer, to avoid + * a 2nd processing + */ + l_idx = p_get_1st_selected(l_layli_ptr, l_nlayers); + if(l_idx >= 0) + { + l_layli_ptr[l_idx].selected = FALSE; + l_sel_cnt--; + } + + /* check for matching Iterator PluginProcedures */ + if(l_apply_mode == PTYP_VARYING_LINEAR ) + { + l_plugin_iterator = p_get_iterator_proc(&l_filter_procname[0]); + } + } + + if(l_rc != 0) + goto error; + + /* perform function (defined by action_mode) on selcted layer(s) */ + l_rc = p_apply_action(l_tmp_image_id, + action_mode, + l_layli_ptr, + l_nlayers, + l_sel_cnt, + l_begin, l_end, l_cur_frame_nr, + new_layername, + &l_filter_procname[0] + ); + if(l_rc != 0) + { + if(gap_debug) fprintf(stderr, "gap: p_frames_modify p_apply-action failed. rc=%d\n", (int)l_rc); + goto error; + } + + /* free layli info table for the current frame */ + if(l_layli_ptr != NULL) + { + free(l_layli_ptr); + l_layli_ptr = NULL; + } + + /* check if the resulting image has at least one layer */ + p_prevent_empty_image(l_tmp_image_id); + + /* save current frame with same name */ + l_rc = p_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename); + if(l_rc < 0) + { + printf("gap: p_frames_modify save frame %d failed.\n", (int)l_cur_frame_nr); + goto error; + } + else l_rc = 0; + + /* iterator call (for filter apply with varying values) */ + if((action_mode == ACM_APPLY_FILTER) + && (l_plugin_iterator != NULL) && (l_apply_mode == PTYP_VARYING_LINEAR )) + { + l_cur_step += 1.0; + /* call plugin-specific iterator, to modify + * the plugin's last_values + */ + if(gap_debug) fprintf(stderr, "DEBUG: calling iterator %s current frame:%d\n", + l_plugin_iterator, (int)l_cur_frame_nr); + l_params = gimp_run_procedure (l_plugin_iterator, + &l_retvals, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_INT32, l_total_steps, /* total steps */ + PARAM_FLOAT, (gdouble)l_cur_step, /* current step */ + PARAM_INT32, l_plugin_data_len, /* length of stored data struct */ + PARAM_END); + if (l_params[0].data.d_status == FALSE) + { + fprintf(stderr, "ERROR: iterator %s failed\n", l_plugin_iterator); + l_rc = -1; + } + + g_free(l_params); + } + + + /* close display (if open) */ + if (l_dpy_id >= 0) + { + gimp_display_delete(l_dpy_id); + l_dpy_id = -1; + } + + /* destroy the tmp image */ + gimp_image_delete(l_tmp_image_id); + + if(l_rc != 0) + goto error; + + + + if(ainfo_ptr->run_mode == RUN_INTERACTIVE) + { + l_percentage += l_percentage_step; + gimp_progress_update (l_percentage); + } + + /* advance to next frame */ + if(l_cur_frame_nr == l_end) + break; + l_cur_frame_nr += l_step; + + } /* end while(1) loop foreach frame in range */ + + if(gap_debug) fprintf(stderr, "p_frames_modify End OK\n"); + + return 0; + +error: + if(gap_debug) fprintf(stderr, "gap: p_frames_modify exit with Error\n"); + + if (l_dpy_id >= 0) + { + gimp_display_delete(l_dpy_id); + l_dpy_id = -1; + } + if(l_tmp_image_id >= 0) gimp_image_delete(l_tmp_image_id); + if(l_layli_ptr != NULL) free(l_layli_ptr); + if(l_plugin_iterator != NULL) free(l_plugin_iterator); + return -1; + +} /* end p_frames_modify */ + + +/* ============================================================================ + * gap_mod_layer + * ============================================================================ + */ + +gint gap_mod_layer(GRunModeType run_mode, gint32 image_id, + gint32 range_from, gint32 range_to, + gint32 action_mode, gint32 sel_mode, + gint32 sel_case, gint32 sel_invert, + char *sel_pattern, char *new_layername) +{ + int l_rc; + t_anim_info *ainfo_ptr; + gint32 l_from; + gint32 l_to; + gint32 l_action_mode; + gint32 l_sel_mode; + gint32 l_sel_case; + gint32 l_sel_invert; + + char l_sel_pattern[MAX_LAYERNAME]; + char l_new_layername[MAX_LAYERNAME]; + + l_rc = 0; + + + ainfo_ptr = p_alloc_ainfo(image_id, run_mode); + if(ainfo_ptr != NULL) + { + + if (0 == p_dir_ainfo(ainfo_ptr)) + { + if(run_mode == RUN_INTERACTIVE) + { + l_rc = p_layer_modify_dialog (ainfo_ptr, &l_from, &l_to, + &l_action_mode, + &l_sel_mode, &sel_case, &sel_invert, + &l_sel_pattern[0], &l_new_layername[0]); + + } + else + { + l_from = range_from; + l_to = range_to; + l_action_mode = action_mode; + l_sel_mode = sel_mode; + l_sel_case = sel_case; + l_sel_invert = sel_invert; + + strncpy(&l_sel_pattern[0], sel_pattern, sizeof(l_sel_pattern) -1); + l_sel_pattern[sizeof(l_sel_pattern) -1] = '\0'; + strncpy(&l_new_layername[0], new_layername, sizeof(l_new_layername) -1); + l_new_layername[sizeof(l_new_layername) -1] = '\0'; + } + + if(l_rc >= 0) + { + l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename); + if(l_rc >= 0) + { + l_rc = p_frames_modify(ainfo_ptr, l_from, l_to, + l_action_mode, + l_sel_mode, sel_case, sel_invert, + &l_sel_pattern[0], &l_new_layername[0] + ); + p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename); + } + } + + + } + p_free_ainfo(&ainfo_ptr); + } + + + return(l_rc); +} + diff --git a/plug-ins/gap/gap_mod_layer.h b/plug-ins/gap/gap_mod_layer.h new file mode 100644 index 0000000000..9c74ef8c68 --- /dev/null +++ b/plug-ins/gap/gap_mod_layer.h @@ -0,0 +1,76 @@ +/* gap_mod_layer.h + * 1998.10.14 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * modify Layer (perform actions (like raise, set visible, apply filter) + * - foreach selected layer + * - in each frame of the selected framerange) + * + */ +/* 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: + * version 0.98.00 1998.11.27 hof: - use new module gap_pdb_calls.h + * version 0.97.00 hof: - created module (as extract gap_fileter_foreach) + */ + +#ifndef _GAP_MOD_LAYER_H +#define _GAP_MOD_LAYER_H + +#define MAX_LAYERNAME 128 + +/* action_mode values */ +#define ACM_SET_VISIBLE 0 +#define ACM_SET_INVISIBLE 1 +#define ACM_SET_LINKED 2 +#define ACM_SET_UNLINKED 3 +#define ACM_RAISE 4 +#define ACM_LOWER 5 +#define ACM_MERGE_EXPAND 6 +#define ACM_MERGE_IMG 7 +#define ACM_MERGE_BG 8 +#define ACM_APPLY_FILTER 9 +#define ACM_DUPLICATE 10 +#define ACM_DELETE 11 +#define ACM_RENAME 12 + +typedef struct +{ + gint32 layer_id; + gint visible; + gint selected; +} t_LayliElem; + +t_LayliElem *p_alloc_layli(gint32 image_id, gint32 *l_sel_cnt, gint *nlayers, + gint32 sel_mode, + gint32 sel_case, + gint32 sel_invert, + char *sel_pattern ); +int p_get_1st_selected (t_LayliElem * layli_ptr, gint nlayers); +void p_prevent_empty_image(gint32 image_id); + +gint gap_mod_layer(GRunModeType run_mode, gint32 image_id, + gint32 range_from, gint32 range_to, + gint32 action_mode, gint32 sel_mode, + gint32 sel_case, gint32 sel_invert, + char *sel_pattern, char *new_layername); + +#endif diff --git a/plug-ins/gap/gap_mov_dialog.c b/plug-ins/gap/gap_mov_dialog.c new file mode 100644 index 0000000000..125d5438d4 --- /dev/null +++ b/plug-ins/gap/gap_mov_dialog.c @@ -0,0 +1,2348 @@ +/* gap_mov_dialog.c + * by hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * Dialog Window for Move Path (gap_mov) + * + */ + +/* code was mainly inspired by SuperNova plug-in + * Copyright (C) 1997 Eiichi Takamori , + * Spencer Kimball, Federico Mena Quintero + */ +/* 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: + * version 0.99.00; 1999.03.03 hof: bugfix: update of the preview (did'nt work with gimp1.1.2) + * version 0.98.00; 1998.11.28 hof: Port to GIMP 1.1: replaced buildmenu.h, apply layermask (before rotate) + * mov_imglayer_constrain must check for drawable_id -1 + * version 0.97.00; 1998.10.19 hof: Set window title to "Move Path" + * version 0.96.02; 1998.07.30 hof: added clip to frame option and tooltips + * version 0.96.00; 1998.07.09 hof: bugfix (filesel did not reopen after cancel) + * version 0.95.00; 1998.05.12 hof: added rotatation capabilities + * version 0.94.00; 1998.04.25 hof: use only one point as default + * bugfix: initial value for src_paintmode + * fixes the problem reported in p_my_layer_copy (cant get new layer) + * version 0.90.00; 1997.12.14 hof: 1.st (pre) release + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" +#include "libgimp/gimpui.h" + +/* GAP includes */ +#include "gap_layer_copy.h" +#include "gap_lib.h" +#include "gap_mov_exec.h" +#include "gap_mov_dialog.h" +#include "gap_pdb_calls.h" + + +/* Some useful macros */ + +extern int gap_debug; /* ==0 ... dont print debug infos */ + +#define POINT_FILE_MAXLEN 512 + +#define ENTRY_WIDTH 40 +#define SCALE_WIDTH 125 +#define PREVIEW_SIZE 256 + +#define PREVIEW 0x1 +#define CURSOR 0x2 +#define ALL 0xf + +#define PREVIEW_MASK GDK_EXPOSURE_MASK | \ + GDK_BUTTON_PRESS_MASK | \ + GDK_BUTTON1_MOTION_MASK + + +typedef struct { + gint run; +} t_mov_interface; + +typedef struct { + GtkObject *adjustment; + GtkWidget *entry; + gint constraint; +} t_mov_EntryScaleData; + +typedef struct +{ + GDrawable *drawable; + gint dwidth, dheight; + gint bpp; + GtkWidget *xentry, *yentry; + GtkWidget *preview; + + gint pwidth, pheight; + gint cursor; + gint curx, cury; /* x,y of cursor in preview */ + gint oldx, oldy; + + GtkWidget *filesel; + GtkEntry *wres_ent; + GtkAdjustment *wres_adj; + GtkEntry *hres_ent; + GtkAdjustment *hres_adj; + GtkEntry *opacity_ent; + GtkAdjustment *opacity_adj; + GtkEntry *rotation_ent; + GtkAdjustment *rotation_adj; + char X_Label[20]; + char Y_Label[20]; + char Opacity_Label[30]; + char Wresize_Label[30]; + char Hresize_Label[30]; + char Rotation_Label[30]; + GtkWidget *X_LabelPtr; + GtkWidget *Y_LabelPtr; + GtkWidget *Opacity_LabelPtr; + GtkWidget *Wresize_LabelPtr; + GtkWidget *Hresize_LabelPtr; + GtkWidget *Rotation_LabelPtr; + gint p_x, p_y; + gint opacity; + gint w_resize; + gint h_resize; + gint rotation; + + gint preview_frame_nr; /* default: current frame */ + gint old_preview_frame_nr; + t_anim_info *ainfo_ptr; + + gint in_call; + char *pointfile_name; +} t_mov_path_preview; + +typedef struct { + t_mov_path_preview *path_ptr; + GtkWidget *dlg; +} t_ok_data; + + +/* p_buildmenu Structures */ + +typedef struct _MenuItem MenuItem; + +typedef void (*MenuItemCallback) (GtkWidget *widget, + gpointer user_data); +struct _MenuItem +{ + char *label; + char unused_accelerator_key; + int unused_accelerator_mods; + MenuItemCallback callback; + gpointer user_data; + MenuItem *unused_subitems; + GtkWidget *widget; +}; + +static GtkTooltips *g_tooltips = NULL; + +/* Declare a local function. + */ +GtkWidget * p_buildmenu (MenuItem *); + + long p_move_dialog (t_mov_data *mov_ptr); +static void p_update_point_labels (t_mov_path_preview *path_ptr); +static void p_points_from_tab (t_mov_path_preview *path_ptr); +static void p_points_to_tab (t_mov_path_preview *path_ptr); +static void p_point_refresh (t_mov_path_preview *path_ptr); +static void p_reset_points (); +static void p_load_points (char *filename); +static void p_save_points (char *filename); + +static GDrawable * p_get_flattened_drawable (gint32 image_id); +static GDrawable * p_get_prevw_drawable (t_mov_path_preview *path_ptr); + +static gint mov_dialog ( GDrawable *drawable, t_mov_path_preview *path_ptr, + gint min, gint max); +static GtkWidget * mov_path_prevw_create ( GDrawable *drawable, + t_mov_path_preview *path_ptr); +static GtkWidget * mov_src_sel_create (); + +static void mov_path_prevw_destroy ( GtkWidget *widget, gpointer data ); +static void mov_path_prevw_preview_init ( t_mov_path_preview *path_ptr ); +static void mov_path_prevw_draw ( t_mov_path_preview *path_ptr, gint update ); +static void mov_path_prevw_entry_update ( GtkWidget *widget, gpointer data ); +static void mov_path_prevw_cursor_update ( t_mov_path_preview *path_ptr ); +static gint mov_path_prevw_preview_expose ( GtkWidget *widget, GdkEvent *event ); +static gint mov_path_prevw_preview_events ( GtkWidget *widget, GdkEvent *event ); + +static GtkWidget* mov_int_entryscale_new ( GtkTable *table, gint x, gint y, + gchar *caption, gint *intvar, + gint min, gint max, gint constraint, + GtkEntry **entry, + GtkAdjustment **adjustment, + char *tooltip); + +static void mov_padd_callback (GtkWidget *widget,gpointer data); +static void mov_pnext_callback (GtkWidget *widget,gpointer data); +static void mov_pprev_callback (GtkWidget *widget,gpointer data); +static void mov_pres_callback (GtkWidget *widget,gpointer data); +static void mov_pload_callback (GtkWidget *widget,gpointer data); +static void mov_psave_callback (GtkWidget *widget,gpointer data); +static void p_points_load_from_file (GtkWidget *widget,gpointer data); +static void p_points_save_to_file (GtkWidget *widget,gpointer data); + +static void mov_close_callback (GtkWidget *widget,gpointer data); +static void mov_ok_callback (GtkWidget *widget,gpointer data); +static void mov_upvw_callback (GtkWidget *widget,gpointer data); +static void mov_paired_entry_destroy_callback (GtkWidget *widget, gpointer data); +static void p_filesel_close_cb (GtkWidget *widget, t_mov_path_preview *path_ptr); + +static void mov_paired_int_scale_update (GtkAdjustment *adjustment, + gpointer data); +static void mov_paired_int_entry_update (GtkWidget *widget, + gpointer data ); +static void p_paired_update (GtkEntry *entry, + GtkAdjustment *adjustment, + gpointer data); + +static gint mov_imglayer_constrain (gint32 image_id, gint32 drawable_id, gpointer data); +static void mov_imglayer_menu_callback (gint32 id, gpointer data); +static void mov_paintmode_menu_callback (GtkWidget *, gpointer); +static void mov_handmode_menu_callback (GtkWidget *, gpointer); +static void mov_stepmode_menu_callback (GtkWidget *, gpointer); +static void mov_clip_to_img_callback (GtkWidget *, gpointer); + + +/* the option menu items -- the paint modes */ +static MenuItem option_paint_items[] = +{ + { "Normal", 0, 0, mov_paintmode_menu_callback, (gpointer) NORMAL_MODE, NULL, NULL }, + { "Dissolve", 0, 0, mov_paintmode_menu_callback, (gpointer) DISSOLVE_MODE, NULL, NULL }, + { "Multiply", 0, 0, mov_paintmode_menu_callback, (gpointer) MULTIPLY_MODE, NULL, NULL }, + { "Screen", 0, 0, mov_paintmode_menu_callback, (gpointer) SCREEN_MODE, NULL, NULL }, + { "Overlay", 0, 0, mov_paintmode_menu_callback, (gpointer) OVERLAY_MODE, NULL, NULL }, + { "Difference", 0, 0, mov_paintmode_menu_callback, (gpointer) DIFFERENCE_MODE, NULL, NULL }, + { "Addition", 0, 0, mov_paintmode_menu_callback, (gpointer) ADDITION_MODE, NULL, NULL }, + { "Subtract", 0, 0, mov_paintmode_menu_callback, (gpointer) SUBTRACT_MODE, NULL, NULL }, + { "Darken Only", 0, 0, mov_paintmode_menu_callback, (gpointer) DARKEN_ONLY_MODE, NULL, NULL }, + { "Lighten Only", 0, 0, mov_paintmode_menu_callback, (gpointer) LIGHTEN_ONLY_MODE, NULL, NULL }, + { "Hue", 0, 0, mov_paintmode_menu_callback, (gpointer) HUE_MODE, NULL, NULL }, + { "Saturation", 0, 0, mov_paintmode_menu_callback, (gpointer) SATURATION_MODE, NULL, NULL }, + { "Color", 0, 0, mov_paintmode_menu_callback, (gpointer) COLOR_MODE, NULL, NULL }, + { "Value", 0, 0, mov_paintmode_menu_callback, (gpointer) VALUE_MODE, NULL, NULL }, + { NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + +/* the option menu items -- the handle modes */ +static MenuItem option_handle_items[] = +{ + { "Left Top", 0, 0, mov_handmode_menu_callback, (gpointer) GAP_HANDLE_LEFT_TOP, NULL, NULL }, + { "Left Bottom", 0, 0, mov_handmode_menu_callback, (gpointer) GAP_HANDLE_LEFT_BOT, NULL, NULL }, + { "Right Top", 0, 0, mov_handmode_menu_callback, (gpointer) GAP_HANDLE_RIGHT_TOP, NULL, NULL }, + { "Right Bottom", 0, 0, mov_handmode_menu_callback, (gpointer) GAP_HANDLE_RIGHT_BOT, NULL, NULL }, + { "Center", 0, 0, mov_handmode_menu_callback, (gpointer) GAP_HANDLE_CENTER, NULL, NULL }, + { NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + + +/* the option menu items -- the loop step modes */ +static MenuItem option_step_items[] = +{ + { "Loop", 0, 0, mov_stepmode_menu_callback, (gpointer) GAP_STEP_LOOP, NULL, NULL }, + { "Loop Reverse", 0, 0, mov_stepmode_menu_callback, (gpointer) GAP_STEP_LOOP_REV, NULL, NULL }, + { "Once", 0, 0, mov_stepmode_menu_callback, (gpointer) GAP_STEP_ONCE, NULL, NULL }, + { "OnceReverse", 0, 0, mov_stepmode_menu_callback, (gpointer) GAP_STEP_ONCE_REV, NULL, NULL }, + { "PingPong", 0, 0, mov_stepmode_menu_callback, (gpointer) GAP_STEP_PING_PONG, NULL, NULL }, + { "None", 0, 0, mov_stepmode_menu_callback, (gpointer) GAP_STEP_NONE, NULL, NULL }, + { NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + + + +static t_mov_values *pvals; + +static t_mov_interface mov_int = +{ + FALSE /* run */ +}; + + +/* ============================================================================ + ********************** + * * + * Dialog interface * + * * + ********************** + * ============================================================================ + */ + +long p_move_dialog (t_mov_data *mov_ptr) +{ + GDrawable *l_drawable_ptr; + gint l_first, l_last; + t_mov_path_preview *path_ptr; + static char l_pointfile_name[POINT_FILE_MAXLEN]; + + + if(gap_debug) printf("GAP-DEBUG: START p_move_dialog\n"); + + l_pointfile_name[POINT_FILE_MAXLEN -1 ] = '\0'; + sprintf(l_pointfile_name, "%s.path_points", mov_ptr->dst_ainfo_ptr->basename); + + pvals = mov_ptr->val_ptr; + + path_ptr = g_new( t_mov_path_preview, 1 ); + if(path_ptr == NULL) + { + printf("error cant alloc path_preview structure\n"); + return -1; + } + + l_first = mov_ptr->dst_ainfo_ptr->first_frame_nr; + l_last = mov_ptr->dst_ainfo_ptr->last_frame_nr; + + /* init parameter values */ + pvals->dst_image_id = mov_ptr->dst_ainfo_ptr->image_id; + pvals->tmp_image_id = -1; + pvals->src_image_id = -1; + pvals->src_layer_id = -1; + pvals->src_paintmode = NORMAL_MODE; + pvals->src_handle = GAP_HANDLE_LEFT_TOP; + pvals->src_stepmode = GAP_STEP_LOOP; + pvals->src_only_visible = 0; + pvals->clip_to_img = 0; + + p_reset_points(); + + /* pvals->point[1].p_x = 100; */ /* default: move from 0/0 to 100/0 */ + + pvals->dst_range_start = mov_ptr->dst_ainfo_ptr->curr_frame_nr; + pvals->dst_range_end = l_last; + pvals->dst_layerstack = 0; /* 0 ... insert layer on top of stack */ + + path_ptr->filesel = NULL; /* fileselector is not open */ + path_ptr->ainfo_ptr = mov_ptr->dst_ainfo_ptr; + path_ptr->preview_frame_nr = mov_ptr->dst_ainfo_ptr->curr_frame_nr; + path_ptr->old_preview_frame_nr = path_ptr->preview_frame_nr; + path_ptr->X_LabelPtr = NULL; + path_ptr->Y_LabelPtr = NULL; + path_ptr->Opacity_LabelPtr = NULL; + path_ptr->Wresize_LabelPtr = NULL; + path_ptr->Hresize_LabelPtr = NULL; + path_ptr->Rotation_LabelPtr = NULL; + path_ptr->pointfile_name = l_pointfile_name; + + p_points_from_tab(path_ptr); + p_update_point_labels(path_ptr); + + /* duplicate the curerent image (for flatten & preview) */ + pvals->tmp_image_id = p_gimp_channel_ops_duplicate(pvals->dst_image_id); + + /* flatten image, and get the (only) resulting drawable */ + l_drawable_ptr = p_get_prevw_drawable(path_ptr); + + /* do DIALOG window */ + mov_dialog(l_drawable_ptr, path_ptr, l_first, l_last); + p_points_to_tab(path_ptr); + + /* destroy the tmp image */ + gimp_image_delete(pvals->tmp_image_id); + +/* g_free (path_ptr); */ /* DONT free path_ptr it leads to sigsegv ERROR */ + + + if(gap_debug) printf("GAP-DEBUG: END p_move_dialog\n"); + + if(mov_int.run == TRUE) return 0; + else return -1; +} + + +/* ============================================================================ + ******************* + * * + * Main Dialog * + * * + ******************* + * ============================================================================ + */ + +static gint +mov_dialog ( GDrawable *drawable, t_mov_path_preview *path_ptr, + gint first_nr, gint last_nr ) +{ + GdkColor tips_fg, tips_bg; + GtkWidget *dlg; + GtkWidget *frame; + GtkWidget *table; + GtkWidget *button_table; + GtkWidget *button; + GtkWidget *path_prevw_frame; + GtkWidget *src_sel_frame; + GtkWidget *check_button; + guchar *color_cube; + t_ok_data ok_data; + + gchar **argv; + gint argc; + + if(gap_debug) printf("GAP-DEBUG: START mov_dialog\n"); + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("gap_move"); + + gtk_init (&argc, &argv); + gtk_rc_parse (gimp_gtkrc ()); + + + gdk_set_use_xshm (gimp_use_xshm ()); + gtk_preview_set_gamma (gimp_gamma ()); + gtk_preview_set_install_cmap (gimp_install_cmap ()); + color_cube = gimp_color_cube (); + gtk_preview_set_color_cube (color_cube[0], color_cube[1], + color_cube[2], color_cube[3]); + + gtk_widget_set_default_visual (gtk_preview_get_visual ()); + gtk_widget_set_default_colormap (gtk_preview_get_cmap ()); + + /* dialog */ + dlg = gtk_dialog_new (); + ok_data.dlg = dlg; + ok_data.path_ptr = path_ptr; + gtk_window_set_title (GTK_WINDOW (dlg), "Move Path"); + gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dlg), "destroy", + (GtkSignalFunc) mov_close_callback, + NULL); + + /* tooltips */ + g_tooltips = gtk_tooltips_new(); + /* use black as foreground: */ + tips_fg.red = 0; + tips_fg.green = 0; + tips_fg.blue = 0; + /* postit yellow (khaki) as background: */ + gdk_color_alloc (gtk_widget_get_colormap (dlg), &tips_fg); + tips_bg.red = 61669; + tips_bg.green = 59113; + tips_bg.blue = 35979; + gdk_color_alloc (gtk_widget_get_colormap (dlg), &tips_bg); + gtk_tooltips_set_colors(g_tooltips, &tips_bg, &tips_fg); + + /* Action area */ + button = gtk_button_new_with_label ("OK"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_ok_callback, + &ok_data); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) gtk_widget_destroy, + GTK_OBJECT (dlg)); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("UpdPreview"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_upvw_callback, + path_ptr); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); + gtk_tooltips_set_tip(g_tooltips, button, + "Show PreviewFame with Selected \nSrcLayer at current Controlpoint" + , NULL); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + /* parameter settings */ + frame = gtk_frame_new ("Copy moving source-layer(s) into frames"); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 6); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); + + table = gtk_table_new (8, 2, FALSE); + gtk_container_border_width (GTK_CONTAINER (table), 6); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_table_set_row_spacings (GTK_TABLE (table), 3); + gtk_table_set_col_spacings (GTK_TABLE (table), 5); + + src_sel_frame = mov_src_sel_create (); + gtk_table_attach( GTK_TABLE(table), src_sel_frame, 0, 2, 0, 1, + 0, 0, 0, 0 ); + + path_prevw_frame = mov_path_prevw_create ( drawable, path_ptr); + gtk_table_attach( GTK_TABLE(table), path_prevw_frame, 0, 2, 1, 2, + 0, 0, 0, 0 ); + + + button_table = gtk_table_new (1, 6, FALSE); + + /* Point Buttons */ + button = gtk_button_new_with_label ("Load Points"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_pload_callback, + path_ptr); + gtk_table_attach( GTK_TABLE(button_table), button, 0, 1, 0, 1, + 0, 0, 0, 0 ); + gtk_tooltips_set_tip(g_tooltips, button, + "Load Controlpoints from file" + , NULL); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Save Points"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_psave_callback, + path_ptr); + gtk_table_attach( GTK_TABLE(button_table), button, 1, 2, 0, 1, + 0, 0, 0, 0 ); + gtk_tooltips_set_tip(g_tooltips, button, + "Save Controlpoints to file" + , NULL); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Reset Points"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_pres_callback, + path_ptr); + gtk_table_attach( GTK_TABLE(button_table), button, 2, 3, 0, 1, + 0, 0, 0, 0 ); + gtk_tooltips_set_tip(g_tooltips, button, + "Reset Controlpoints \nto one Defaultpoint" + , NULL); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Add Point"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_padd_callback, + path_ptr); + gtk_table_attach( GTK_TABLE(button_table), button, 3, 4, 0, 1, + 0, 0, 0, 0 ); + gtk_tooltips_set_tip(g_tooltips, button, + "Add Controlpoint at end \n(the last Point is duplicated)" + , NULL); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Prev Point"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_pprev_callback, + path_ptr); + gtk_table_attach( GTK_TABLE(button_table), button, 4, 5, 0, 1, + 0, 0, 0, 0 ); + gtk_tooltips_set_tip(g_tooltips, button, + "Show Previous Controlpoint" + , NULL); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Next Point"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mov_pnext_callback, + path_ptr); + gtk_table_attach( GTK_TABLE(button_table), button, 5, 6, 0, 1, + 0, 0, 0, 0 ); + gtk_tooltips_set_tip(g_tooltips, button, + "Show Next Controlpoint" + , NULL); + gtk_widget_show (button); + + + gtk_widget_show (button_table); + gtk_table_attach( GTK_TABLE(table), button_table, 0, 2, 2, 3, + 0, 0, 0, 0 ); + + mov_int_entryscale_new( GTK_TABLE (table), 0, 3, + "Start Frame:", &pvals->dst_range_start, + first_nr, last_nr, TRUE, NULL, NULL, + "first handled frame" ); + mov_int_entryscale_new( GTK_TABLE (table), 0, 4, + "End Frame:", &pvals->dst_range_end, + first_nr, last_nr, TRUE, NULL, NULL, + "last handled frame" ); + mov_int_entryscale_new( GTK_TABLE (table), 0, 5, + "Preview Frame:", &path_ptr->preview_frame_nr, + first_nr, last_nr, TRUE, NULL, NULL, + "frame to show when UpdPreview\nbutton is pressed" ); + mov_int_entryscale_new( GTK_TABLE (table), 0, 6, + "Layerstack:", &pvals->dst_layerstack, + 0, 99, FALSE, NULL, NULL, + "How to insert SrcLayer into the\nDst.Frame's Layerstack\n0 means on top i.e in front" ); + + /* toggle clip_to_image */ + check_button = gtk_check_button_new_with_label ("Clip To Frame"); + gtk_table_attach ( GTK_TABLE (table), check_button, 0, 2, 7, 7+1, GTK_FILL, 0, 0, 0); + gtk_signal_connect (GTK_OBJECT (check_button), "toggled", + (GtkSignalFunc) mov_clip_to_img_callback, + path_ptr); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (check_button), pvals->clip_to_img); + gtk_tooltips_set_tip(g_tooltips, check_button, + "Clip all copied Src-Layers\nat Frame Boundaries" + , NULL); + gtk_widget_show (check_button); + + + gtk_widget_show (frame); + gtk_widget_show (table); + gtk_widget_show (dlg); + + gtk_main (); + gdk_flush (); + + if(gap_debug) printf("GAP-DEBUG: END mov_dialog\n"); + + return mov_int.run; +} + +/* ============================================================================ + * implementation of CALLBACK procedures + * ============================================================================ + */ + +static void +mov_close_callback (GtkWidget *widget, + gpointer data) +{ + gtk_main_quit (); +} + +static void +mov_ok_callback (GtkWidget *widget, + gpointer data) +{ + t_ok_data *ok_data_ptr; + + ok_data_ptr = data; + + + if(pvals != NULL) + { + if(pvals->src_layer_id < 0) + { + + p_msg_win(RUN_INTERACTIVE, + "No Source Image was selected\n(Please open a 2nd Image of the same type before opening Move Path)\n"); + return; + } + } + + mov_int.run = TRUE; + + if(pvals->point_idx_max == 0) + { + /* if we have only one point duplicate that point + * (move algorithm needs at least 2 points) + */ + mov_padd_callback(NULL, ok_data_ptr->path_ptr); + } + gtk_widget_destroy (ok_data_ptr->dlg); +} + +static void +mov_upvw_callback (GtkWidget *widget, + gpointer data) +{ + t_mov_path_preview *path_ptr; + char *l_filename; + long l_frame_nr; + gint32 l_new_tmp_image_id; + gint32 l_old_tmp_image_id; + + path_ptr = data; + + if(gap_debug) printf("mov_upvw_callback nr: %d old_nr: %d\n", + (int)path_ptr->preview_frame_nr , (int)path_ptr->old_preview_frame_nr); + +/* if( path_ptr->preview_frame_nr != path_ptr->old_preview_frame_nr) + * { + */ + l_frame_nr = (long)path_ptr->preview_frame_nr; + l_filename = p_alloc_fname(path_ptr->ainfo_ptr->basename, + l_frame_nr, + path_ptr->ainfo_ptr->extension); + if(l_filename != NULL) + { + /* replace the temporary image */ + l_new_tmp_image_id = p_load_image(l_filename); + free(l_filename); + if (l_new_tmp_image_id >= 0) + { + /* use the new loaded temporary image */ + l_old_tmp_image_id = pvals->tmp_image_id; + pvals->tmp_image_id = l_new_tmp_image_id; + + /* flatten image, and get the (only) resulting drawable */ + path_ptr->drawable = p_get_prevw_drawable(path_ptr); + + /* re initialize preview image */ + mov_path_prevw_preview_init(path_ptr); + p_point_refresh(path_ptr); + + path_ptr->old_preview_frame_nr = path_ptr->preview_frame_nr; + + gtk_widget_draw(path_ptr->preview, NULL); + gdk_flush(); + + /* destroy the old tmp image */ + gimp_image_delete(l_old_tmp_image_id); + } + + } +/* } */ +} + +static void +mov_padd_callback (GtkWidget *widget, + gpointer data) +{ + t_mov_path_preview *path_ptr = data; + gint l_idx; + + if(gap_debug) printf("mov_padd_callback\n"); + l_idx = pvals->point_idx_max; + if (l_idx < GAP_MOV_MAX_POINT -2) + { + /* advance to next point */ + p_points_to_tab(path_ptr); + pvals->point_idx_max++; + pvals->point_idx = pvals->point_idx_max; + + /* copy values from previous point to current (new) point */ + pvals->point[pvals->point_idx_max].p_x = pvals->point[l_idx].p_x; + pvals->point[pvals->point_idx_max].p_y = pvals->point[l_idx].p_y; + pvals->point[pvals->point_idx_max].opacity = pvals->point[l_idx].opacity; + pvals->point[pvals->point_idx_max].w_resize = pvals->point[l_idx].w_resize; + pvals->point[pvals->point_idx_max].h_resize = pvals->point[l_idx].h_resize; + pvals->point[pvals->point_idx_max].rotation = pvals->point[l_idx].rotation; + + p_point_refresh(path_ptr); + } +} + +static void +mov_pnext_callback (GtkWidget *widget, + gpointer data) +{ + t_mov_path_preview *path_ptr = data; + + if(gap_debug) printf("mov_pnext_callback\n"); + if (pvals->point_idx < pvals->point_idx_max) + { + /* advance to next point */ + p_points_to_tab(path_ptr); + pvals->point_idx++; + p_point_refresh(path_ptr); + } +} + + +static void +mov_pprev_callback (GtkWidget *widget, + gpointer data) +{ + t_mov_path_preview *path_ptr = data; + + if(gap_debug) printf("mov_pprev_callback\n"); + if (pvals->point_idx > 0) + { + /* advance to next point */ + p_points_to_tab(path_ptr); + pvals->point_idx--; + p_point_refresh(path_ptr); + } + + +} + + +static void +mov_pres_callback (GtkWidget *widget, + gpointer data) +{ + t_mov_path_preview *path_ptr = data; + + if(gap_debug) printf("mov_pres_callback\n"); + p_reset_points(); + p_point_refresh(path_ptr); +} + +static void +p_filesel_close_cb(GtkWidget *widget, + t_mov_path_preview *path_ptr) +{ + if(path_ptr->filesel == NULL) return; + + gtk_widget_destroy(GTK_WIDGET(path_ptr->filesel)); + path_ptr->filesel = NULL; /* now filesel is closed */ +} + +static void +mov_pload_callback (GtkWidget *widget, + gpointer data) +{ + GtkWidget *filesel; + t_mov_path_preview *path_ptr = data; + + if(path_ptr->filesel != NULL) return; /* filesel is already open */ + + filesel = gtk_file_selection_new ("Load Path Points from file"); + path_ptr->filesel = filesel; + + gtk_window_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE); + + gtk_signal_connect (GTK_OBJECT (filesel), "destroy", + (GtkSignalFunc) p_filesel_close_cb, + path_ptr); + + gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button), + "clicked", (GtkSignalFunc) p_points_load_from_file, + path_ptr); + + gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button), + "clicked", (GtkSignalFunc) p_filesel_close_cb, + (GtkObject *)path_ptr); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), + path_ptr->pointfile_name); + + gtk_widget_show (filesel); +} + + +static void +mov_psave_callback (GtkWidget *widget, + gpointer data) +{ + GtkWidget *filesel; + t_mov_path_preview *path_ptr = data; + + if(path_ptr->filesel != NULL) return; /* filesel is already open */ + + filesel = gtk_file_selection_new ("Save Path Points to file"); + path_ptr->filesel = filesel; + + gtk_window_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE); + + gtk_signal_connect (GTK_OBJECT (filesel), "destroy", + (GtkSignalFunc) p_filesel_close_cb, + path_ptr); + + gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button), + "clicked", (GtkSignalFunc) p_points_save_to_file, + path_ptr); + + gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button), + "clicked", (GtkSignalFunc) p_filesel_close_cb, + (GtkObject *)path_ptr); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), + path_ptr->pointfile_name); + + gtk_widget_show (filesel); +} + + +static void +p_points_load_from_file (GtkWidget *widget, + gpointer data) +{ + t_mov_path_preview *path_ptr = data; + char *filename; + + if(gap_debug) printf("p_points_load_from_file\n"); + if(path_ptr->filesel == NULL) return; + + filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (path_ptr->filesel)); + strncpy(path_ptr->pointfile_name, filename, POINT_FILE_MAXLEN -1); + + if(gap_debug) printf("p_points_load_from_file %s\n", path_ptr->pointfile_name); + + gtk_widget_destroy(GTK_WIDGET(path_ptr->filesel)); + path_ptr->filesel = NULL; + + p_load_points(path_ptr->pointfile_name); + p_point_refresh(path_ptr); +} + + +static void +p_points_save_to_file (GtkWidget *widget, + gpointer data) +{ + t_mov_path_preview *path_ptr = data; + char *filename; + + if(gap_debug) printf("p_points_save_to_file\n"); + if(path_ptr->filesel == NULL) return; + + filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (path_ptr->filesel)); + strncpy(path_ptr->pointfile_name, filename, POINT_FILE_MAXLEN -1); + + if(gap_debug) printf("p_points_save_to_file %s\n", path_ptr->pointfile_name); + + gtk_widget_destroy(GTK_WIDGET(path_ptr->filesel)); + path_ptr->filesel = NULL; + + p_points_to_tab(path_ptr); + p_save_points(path_ptr->pointfile_name); + p_point_refresh(path_ptr); +} + + +static void p_point_refresh(t_mov_path_preview *path_ptr) +{ + gchar buf[256]; + + p_points_from_tab(path_ptr); + p_update_point_labels(path_ptr); + + if(gap_debug) printf("p_point_refresh:newval in_call=%d\n", path_ptr->in_call ); + if( !path_ptr->in_call ) + { + mov_path_prevw_cursor_update( path_ptr ); + mov_path_prevw_draw ( path_ptr, CURSOR ); + + path_ptr->in_call = TRUE; + sprintf(buf, "%d", path_ptr->p_x ); + gtk_entry_set_text( GTK_ENTRY(path_ptr->xentry), buf ); + sprintf(buf, "%d", path_ptr->p_y ); + gtk_entry_set_text( GTK_ENTRY(path_ptr->yentry), buf ); + + p_paired_update(path_ptr->wres_ent, path_ptr->wres_adj, + (gpointer)&path_ptr->w_resize); + p_paired_update(path_ptr->hres_ent, path_ptr->hres_adj, + (gpointer)&path_ptr->h_resize); + + p_paired_update(path_ptr->opacity_ent, path_ptr->opacity_adj, + (gpointer)&path_ptr->opacity); + + p_paired_update(path_ptr->rotation_ent, path_ptr->rotation_adj, + (gpointer)&path_ptr->rotation); + + path_ptr->in_call = FALSE; + } +} /* end p_point_refresh */ + + + +static void +mov_imglayer_menu_callback(gint32 id, gpointer data) +{ + pvals->src_layer_id = id; + pvals->src_image_id = gimp_layer_get_image_id(id); + + if(gap_debug) printf("mov_imglayer_menu_callback: image_id=%ld layer_id=%ld\n", + (long)pvals->src_image_id, (long)pvals->src_layer_id); + /* TODO: + * if any remove old src layer from preview + * add this layer to preview (at current point koords) + * update_preview + */ + +} /* end mov_imglayer_menu_callback */ + +static gint +mov_imglayer_constrain(gint32 image_id, gint32 drawable_id, gpointer data) +{ + gint32 l_src_image_id; + + if(gap_debug) printf("GAP-DEBUG: mov_imglayer_constrain PROCEDURE\n"); + + if(drawable_id < 0) + { + /* gimp 1.1 makes a first call of the constraint procedure + * with drawable_id = -1, and skips the whole image if FALSE is returned + */ + return(TRUE); + } + + l_src_image_id = gimp_layer_get_image_id(drawable_id); + + /* dont accept layers from within the destination image id + * or layers within the tmp preview image + * conversions between different base_types are not supported in this version + */ + return((l_src_image_id != pvals->dst_image_id) && + (l_src_image_id != pvals->tmp_image_id) && + (gimp_image_base_type(l_src_image_id) == gimp_image_base_type(pvals->tmp_image_id)) ); +} /* end mov_imglayer_constrain */ + +static void +mov_paintmode_menu_callback (GtkWidget *w, gpointer client_data) +{ + pvals->src_paintmode = (int)client_data; +} + +static void +mov_handmode_menu_callback (GtkWidget *w, gpointer client_data) +{ + pvals->src_handle = (int)client_data; +} + +static void +mov_stepmode_menu_callback (GtkWidget *w, gpointer client_data) +{ + pvals->src_stepmode = (int)client_data; +} + +static void +mov_clip_to_img_callback(GtkWidget *w, gpointer client_data) +{ + if (GTK_TOGGLE_BUTTON (w)->active) + { + pvals->clip_to_img = 1; + } + else + { + pvals->clip_to_img = 0; + } +} + +/* ============================================================================ + * procedures to handle POINTS - TABLE + * ============================================================================ + */ +static void +p_points_from_tab(t_mov_path_preview *path_ptr) +{ + path_ptr->p_x = pvals->point[pvals->point_idx].p_x; + path_ptr->p_y = pvals->point[pvals->point_idx].p_y; + path_ptr->opacity = pvals->point[pvals->point_idx].opacity; + path_ptr->w_resize = pvals->point[pvals->point_idx].w_resize; + path_ptr->h_resize = pvals->point[pvals->point_idx].h_resize; + path_ptr->rotation = pvals->point[pvals->point_idx].rotation; +} + +static void +p_points_to_tab(t_mov_path_preview *path_ptr) +{ + pvals->point[pvals->point_idx].p_x = path_ptr->p_x; + pvals->point[pvals->point_idx].p_y = path_ptr->p_y; + pvals->point[pvals->point_idx].opacity = path_ptr->opacity; + pvals->point[pvals->point_idx].w_resize = path_ptr->w_resize; + pvals->point[pvals->point_idx].h_resize = path_ptr->h_resize; + pvals->point[pvals->point_idx].rotation = path_ptr->rotation; +} + +void p_update_point_labels(t_mov_path_preview *path_ptr) +{ + sprintf(&path_ptr->X_Label[0], "X [%d]: ", pvals->point_idx + 1); + sprintf(&path_ptr->Y_Label[0], "Y [%d]: ", pvals->point_idx + 1); + sprintf(&path_ptr->Opacity_Label[0], "Opacity [%d]: ", pvals->point_idx + 1); + sprintf(&path_ptr->Wresize_Label[0], "Width [%d]: ", pvals->point_idx + 1); + sprintf(&path_ptr->Hresize_Label[0], "Height [%d]: ", pvals->point_idx + 1); + sprintf(&path_ptr->Rotation_Label[0], "Rotate deg[%d]: ", pvals->point_idx + 1); + + + if(NULL != path_ptr->X_LabelPtr) + { gtk_label_set(GTK_LABEL(path_ptr->X_LabelPtr), + &path_ptr->X_Label[0]); + } + if(NULL != path_ptr->Y_LabelPtr) + { gtk_label_set(GTK_LABEL(path_ptr->Y_LabelPtr), + &path_ptr->Y_Label[0]); + } + if(NULL != path_ptr->Opacity_LabelPtr) + { gtk_label_set(GTK_LABEL(path_ptr->Opacity_LabelPtr), + &path_ptr->Opacity_Label[0]); + } + if(NULL != path_ptr->Wresize_LabelPtr) + { gtk_label_set(GTK_LABEL(path_ptr->Wresize_LabelPtr), + &path_ptr->Wresize_Label[0]); + } + if(NULL != path_ptr->Hresize_LabelPtr) + { gtk_label_set(GTK_LABEL(path_ptr->Hresize_LabelPtr), + &path_ptr->Hresize_Label[0]); + } + if(NULL != path_ptr->Rotation_LabelPtr) + { gtk_label_set(GTK_LABEL(path_ptr->Rotation_LabelPtr), + &path_ptr->Rotation_Label[0]); + } +} + + +/* ============================================================================ + * p_reset_points + * Init point table with identical 2 Points + * ============================================================================ + */ + +void p_reset_points() +{ + int l_idx; + + pvals->point_idx = 0; /* 0 == current point */ + pvals->point_idx_max = 0; /* 0 == there is only one valid point */ + + for(l_idx = 0; l_idx <= pvals->point_idx_max; l_idx++) + { + pvals->point[l_idx].p_x = 0; + pvals->point[l_idx].p_y = 0; + pvals->point[l_idx].opacity = 100; /* 100 percent (no transparecy) */ + pvals->point[l_idx].w_resize = 100; /* 100% no resizize (1:1) */ + pvals->point[l_idx].h_resize = 100; /* 100% no resizize (1:1) */ + pvals->point[l_idx].rotation = 0; /* no rotation (0 degree) */ + } +} /* end p_reset_points */ + +/* ============================================================================ + * p_load_points + * load point table (from named file into global pvals) + * (reset points if load failed) + * ============================================================================ + */ + +void p_load_points(char *filename) +{ +#define POINT_REC_MAX 128 + + FILE *l_fp; + int l_idx; + char l_buff[POINT_REC_MAX +1 ]; + char *l_ptr; + int l_cnt; + int l_v1, l_v2, l_v3, l_v4, l_v5, l_v6; + + if(filename == NULL) return; + + l_fp = fopen(filename, "r"); + if(l_fp != NULL) + { + l_idx = -1; + while (NULL != fgets (l_buff, POINT_REC_MAX, l_fp)) + { + /* skip leading blanks */ + l_ptr = l_buff; + while(*l_ptr == ' ') { l_ptr++; } + + /* check if line empty or comment only (starts with '#') */ + if((*l_ptr != '#') && (*l_ptr != '\n') && (*l_ptr != '\0')) + { + l_cnt = sscanf(l_ptr, "%d%d%d%d%d%d", &l_v1, &l_v2, &l_v3, &l_v4, &l_v5, &l_v6); + if(l_idx == -1) + { + if((l_cnt < 2) || (l_v2 > GAP_MOV_MAX_POINT) || (l_v1 > l_v2)) + { + break; + } + pvals->point_idx = l_v1; + pvals->point_idx_max = l_v2 -1; + l_idx = 0; + } + else + { + if(l_cnt != 6) + { + p_reset_points(); + break; + } + pvals->point[l_idx].p_x = l_v1; + pvals->point[l_idx].p_y = l_v2; + pvals->point[l_idx].w_resize = l_v3; + pvals->point[l_idx].h_resize = l_v4; + pvals->point[l_idx].opacity = l_v5; + pvals->point[l_idx].rotation = l_v6; + l_idx ++; + } + + if(l_idx > pvals->point_idx_max) break; + } + } + + fclose(l_fp); + } +} + +/* ============================================================================ + * p_save_points + * save point table (from global pvals into named file) + * ============================================================================ + */ +void p_save_points(char *filename) +{ + FILE *l_fp; + int l_idx; + + if(filename == NULL) return; + + l_fp = fopen(filename, "w+"); + if(l_fp != NULL) + { + fprintf(l_fp, "# GAP file contains saved Move Path Point Table\n"); + fprintf(l_fp, "%d %d # current_point points\n", + (int)pvals->point_idx, + (int)pvals->point_idx_max + 1); + fprintf(l_fp, "# x y width height opacity rotation\n"); + for(l_idx = 0; l_idx <= pvals->point_idx_max; l_idx++) + { + fprintf(l_fp, "%04d %04d %03d %03d %03d %d\n", + (int)pvals->point[l_idx].p_x, + (int)pvals->point[l_idx].p_y, + (int)pvals->point[l_idx].w_resize, + (int)pvals->point[l_idx].h_resize, + (int)pvals->point[l_idx].opacity, + (int)pvals->point[l_idx].rotation); + } + + fclose(l_fp); + } +} /* end p_save_points */ + + + + + +/* ============================================================================ + * Create new source selection table Frame, and return it. + * A frame that contains: + * - 2x2 menus (src_image/layer, handle, stepmode, paintmode) + * ============================================================================ + */ + +static GtkWidget * +mov_src_sel_create() +{ + GtkWidget *frame; + GtkWidget *table; + GtkWidget *option_menu; + GtkWidget *menu; + GtkWidget *label; + + + frame = gtk_frame_new ( "Source Select" ); +/* + gtk_signal_connect( GTK_OBJECT( frame ), "destroy", + (GtkSignalFunc) mov_src_sel_destroy, + path_ptr ); +*/ + gtk_frame_set_shadow_type( GTK_FRAME( frame ) ,GTK_SHADOW_ETCHED_IN ); + gtk_container_border_width( GTK_CONTAINER( frame ), 6 ); + + + table = gtk_table_new ( 2, 4, FALSE ); + gtk_container_border_width (GTK_CONTAINER (table), 6); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_table_set_row_spacings (GTK_TABLE (table), 3); + gtk_table_set_col_spacings (GTK_TABLE (table), 10); + + /* Source Layer menu */ + label = gtk_label_new("SourceImage/Layer:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + option_menu = gtk_option_menu_new(); + gtk_table_attach(GTK_TABLE(table), option_menu, 1, 2, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + gtk_tooltips_set_tip(g_tooltips, option_menu, + "Source Object to insert into Framerange" + , NULL); + + gtk_widget_show(option_menu); + + menu = gimp_layer_menu_new(mov_imglayer_constrain, + mov_imglayer_menu_callback, + NULL, + pvals->src_layer_id); + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + gtk_widget_show(option_menu); + + + /* Paintmode menu */ + + label = gtk_label_new("Mode:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + option_menu = gtk_option_menu_new(); + gtk_table_attach(GTK_TABLE(table), option_menu, 3, 4, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip(g_tooltips, option_menu, + "Paintmode" + , NULL); + gtk_widget_show(option_menu); + + menu = p_buildmenu (option_paint_items); + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + gtk_widget_show(option_menu); + + /* Loop Stepmode menu */ + + label = gtk_label_new("Stepmode:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + option_menu = gtk_option_menu_new(); + gtk_table_attach(GTK_TABLE(table), option_menu, 1, 2, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show(option_menu); + + menu = p_buildmenu (option_step_items); + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + gtk_tooltips_set_tip(g_tooltips, option_menu, + "How to fetch te next SrcLayer \nat the next handled frame" + , NULL); + gtk_widget_show(option_menu); + + /* Source Image Handle menu */ + + label = gtk_label_new("Handle:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 4, 0); + gtk_widget_show(label); + + option_menu = gtk_option_menu_new(); + gtk_table_attach(GTK_TABLE(table), option_menu, 3, 4, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show(option_menu); + + menu = p_buildmenu (option_handle_items); + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + gtk_tooltips_set_tip(g_tooltips, option_menu, + "How to place the SrcLayer at \nControlpoint Koordinates" + , NULL); + gtk_widget_show(option_menu); + + gtk_widget_show( table ); + gtk_widget_show( frame ); + + return frame; +} /* end mov_src_sel_create */ + + +/* ============================================================================ + * Create new path_preview Frame, and return it (GtkFrame). + * A frame that contains one preview and the entries of the current point + * One "Point" has: + * - 2 entrys X/Y, used for positioning + * - Resize 2x Scale + integer entry (for resizing Width + Height) + * - Opacity Scale + integr entry (0 to 100 %) + * - Rotation Scale + ineger entry (-360 to 360 degrees) + * ============================================================================ + */ + +static GtkWidget * +mov_path_prevw_create ( GDrawable *drawable, t_mov_path_preview *path_ptr) +{ + GtkWidget *frame; + GtkWidget *table; + GtkWidget *label; + GtkWidget *entry; + GtkWidget *pframe; + GtkWidget *preview; + gchar buf[256]; + + path_ptr->drawable = drawable; + path_ptr->dwidth = gimp_drawable_width(drawable->id ); + path_ptr->dheight = gimp_drawable_height(drawable->id ); + path_ptr->bpp = gimp_drawable_bpp(drawable->id); + if ( gimp_drawable_has_alpha(drawable->id) ) + path_ptr->bpp--; + path_ptr->cursor = FALSE; + path_ptr->curx = 0; + path_ptr->cury = 0; + path_ptr->oldx = 0; + path_ptr->oldy = 0; + path_ptr->in_call = TRUE; /* to avoid side effects while initialization */ + + frame = gtk_frame_new ( "Move Path Preview" ); + gtk_signal_connect( GTK_OBJECT( frame ), "destroy", + (GtkSignalFunc) mov_path_prevw_destroy, + path_ptr ); + gtk_frame_set_shadow_type( GTK_FRAME( frame ) ,GTK_SHADOW_ETCHED_IN ); + gtk_container_border_width( GTK_CONTAINER( frame ), 6 ); + + /* table = gtk_table_new ( 2, 4, FALSE ); */ + table = gtk_table_new ( 4, 4, FALSE ); + gtk_container_border_width (GTK_CONTAINER (table), 6 ); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_table_set_row_spacings (GTK_TABLE (table), 3); + gtk_table_set_col_spacings (GTK_TABLE (table), 5); + + label = gtk_label_new ( &path_ptr->X_Label[0] ); /* "X[0]: " */ + gtk_misc_set_alignment( GTK_MISC(label), 0.0, 0.5 ); + gtk_table_attach( GTK_TABLE(table), label, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0 ); + gtk_widget_show(label); + path_ptr->X_LabelPtr = label; /* store label ptr for later update */ + + path_ptr->xentry = entry = gtk_entry_new (); + gtk_object_set_user_data( GTK_OBJECT(entry), path_ptr ); + gtk_signal_connect( GTK_OBJECT(entry), "changed", + (GtkSignalFunc) mov_path_prevw_entry_update, + &path_ptr->p_x ); + gtk_widget_set_usize( GTK_WIDGET(entry), ENTRY_WIDTH,0 ); + gtk_table_attach( GTK_TABLE(table), entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0 ); + gtk_widget_show(entry); + + label = gtk_label_new ( &path_ptr->Y_Label[0] ); /* "Y[0]: " */ + gtk_misc_set_alignment( GTK_MISC(label), 0.0, 0.5 ); + gtk_table_attach( GTK_TABLE(table), label, 2, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0 ); + gtk_widget_show(label); + path_ptr->Y_LabelPtr = label; /* store label ptr for later update */ + + path_ptr->yentry = entry = gtk_entry_new (); + gtk_object_set_user_data( GTK_OBJECT(entry), path_ptr ); + gtk_signal_connect( GTK_OBJECT(entry), "changed", + (GtkSignalFunc) mov_path_prevw_entry_update, + &path_ptr->p_y ); + gtk_widget_set_usize( GTK_WIDGET(entry), ENTRY_WIDTH, 0 ); + gtk_table_attach( GTK_TABLE(table), entry, 3, 4, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0 ); + gtk_widget_show(entry); + + + + path_ptr->Wresize_LabelPtr = + mov_int_entryscale_new( GTK_TABLE (table), 0, 1, + &path_ptr->Wresize_Label[0], &path_ptr->w_resize, + 5, 200, FALSE, &path_ptr->wres_ent, &path_ptr->wres_adj, + "Scale SrcLayer's Width\nin percent"); + + path_ptr->Hresize_LabelPtr = + mov_int_entryscale_new( GTK_TABLE (table), 2, 1, + &path_ptr->Hresize_Label[0], &path_ptr->h_resize, + 5, 200, FALSE, &path_ptr->hres_ent, &path_ptr->hres_adj, + "Scale SrcLayer's Height\nin percent" ); + + path_ptr->Opacity_LabelPtr = + mov_int_entryscale_new( GTK_TABLE (table), 0, 2, + &path_ptr->Opacity_Label[0], &path_ptr->opacity, + 0, 100, TRUE, &path_ptr->opacity_ent, &path_ptr->opacity_adj, + "SrcLayer's Opacity\nin percent"); + + + /* Rotation */ + + path_ptr->Rotation_LabelPtr = + mov_int_entryscale_new( GTK_TABLE (table), 2, 2, + &path_ptr->Rotation_Label[0], &path_ptr->rotation, + -360, 360, FALSE, &path_ptr->rotation_ent, &path_ptr->rotation_adj, + "Rotate SrcLayer (in degree)"); + + + + /* frame (shadow_in) that contains preview */ + pframe = gtk_frame_new ( NULL ); + gtk_frame_set_shadow_type( GTK_FRAME( pframe ), GTK_SHADOW_IN ); + /* gtk_table_attach( GTK_TABLE(table), pframe, 0, 4, 1, 2, 0, 0, 0, 0 ); */ + gtk_table_attach( GTK_TABLE(table), pframe, 0, 4, 3, 4, 0, 0, 0, 0 ); + + /* PREVIEW */ + path_ptr->preview = preview = gtk_preview_new( path_ptr->bpp==3 ? GTK_PREVIEW_COLOR : GTK_PREVIEW_GRAYSCALE ); + gtk_object_set_user_data( GTK_OBJECT(preview), path_ptr ); + gtk_widget_set_events( GTK_WIDGET(preview), PREVIEW_MASK ); + gtk_signal_connect_after( GTK_OBJECT(preview), "expose_event", + (GtkSignalFunc) mov_path_prevw_preview_expose, + path_ptr ); + gtk_signal_connect( GTK_OBJECT(preview), "event", + (GtkSignalFunc) mov_path_prevw_preview_events, + path_ptr ); + gtk_container_add( GTK_CONTAINER( pframe ), path_ptr->preview ); + + /* + * Resize the greater one of dwidth and dheight to PREVIEW_SIZE + */ + if ( path_ptr->dwidth > path_ptr->dheight ) { + path_ptr->pheight = path_ptr->dheight * PREVIEW_SIZE / path_ptr->dwidth; + path_ptr->pwidth = PREVIEW_SIZE; + } else { + path_ptr->pwidth = path_ptr->dwidth * PREVIEW_SIZE / path_ptr->dheight; + path_ptr->pheight = PREVIEW_SIZE; + } + gtk_preview_size( GTK_PREVIEW( preview ), path_ptr->pwidth, path_ptr->pheight ); + + + + /* Draw the contents of preview, that is saved in the preview widget */ + mov_path_prevw_preview_init( path_ptr ); + gtk_widget_show(preview); + + gtk_widget_show( pframe ); + gtk_widget_show( table ); + gtk_widget_show( frame ); + + sprintf( buf, "%d", path_ptr->p_x ); + gtk_entry_set_text( GTK_ENTRY(path_ptr->xentry), buf ); + sprintf( buf, "%d", path_ptr->p_y ); + gtk_entry_set_text( GTK_ENTRY(path_ptr->yentry), buf ); + + mov_path_prevw_cursor_update( path_ptr ); + + path_ptr->cursor = FALSE; /* Make sure that the cursor has not been drawn */ + path_ptr->in_call = FALSE; /* End of initialization */ + if(gap_debug) printf("pvals path_ptr=%d,%d\n", path_ptr->p_x, path_ptr->p_y ); + if(gap_debug) printf("path_ptr cur=%d,%d\n", path_ptr->curx, path_ptr->cury ); + return frame; +} + +static void +mov_path_prevw_destroy ( GtkWidget *widget, + gpointer data ) +{ + t_mov_path_preview *path_ptr = data; + g_free( path_ptr ); +} + +static void render_preview ( GtkWidget *preview, GPixelRgn *srcrgn ); + +/* ============================================================================ + * mov_path_prevw_preview_init + * Initialize preview + * Draw the contents into the internal buffer of the preview widget + * ============================================================================ + */ +static void +mov_path_prevw_preview_init ( t_mov_path_preview *path_ptr ) +{ + GtkWidget *preview; + GPixelRgn src_rgn; + gint dwidth, dheight, pwidth, pheight, bpp; + + preview = path_ptr->preview; + dwidth = path_ptr->dwidth; + dheight = path_ptr->dheight; + pwidth = path_ptr->pwidth; + pheight = path_ptr->pheight; + bpp = path_ptr->bpp; + + gimp_pixel_rgn_init ( &src_rgn, path_ptr->drawable, 0, 0, + path_ptr->dwidth, path_ptr->dheight, FALSE, FALSE ); + render_preview( path_ptr->preview, &src_rgn ); +} + + +/* ============================================================================ + * render_preview + * Preview Rendering Util routine + * ============================================================================ + */ + +#define CHECKWIDTH 4 +#define LIGHTCHECK 192 +#define DARKCHECK 128 +#ifndef OPAQUE +#define OPAQUE 255 +#endif + +static void +render_preview ( GtkWidget *preview, GPixelRgn *srcrgn ) +{ + guchar *src_row, *dest_row, *src, *dest; + gint row, col; + gint dwidth, dheight, pwidth, pheight; + gint *src_col; + gint bpp, alpha, has_alpha, b; + guchar check; + + dwidth = srcrgn->w; + dheight = srcrgn->h; + if( GTK_PREVIEW(preview)->buffer ) + { + pwidth = GTK_PREVIEW(preview)->buffer_width; + pheight = GTK_PREVIEW(preview)->buffer_height; + } + else + { + pwidth = preview->requisition.width; + pheight = preview->requisition.height; + } + + bpp = srcrgn->bpp; + alpha = bpp; + has_alpha = gimp_drawable_has_alpha( srcrgn->drawable->id ); + if( has_alpha ) alpha--; + /* printf("render_preview: %d %d %d", bpp, alpha, has_alpha); + printf(" (%d %d %d %d)\n", dwidth, dheight, pwidth, pheight); */ + + src_row = g_new ( guchar, dwidth * bpp ); + dest_row = g_new ( guchar, pwidth * bpp ); + src_col = g_new ( gint, pwidth ); + + for ( col = 0; col < pwidth; col++ ) + src_col[ col ] = ( col * dwidth / pwidth ) * bpp; + + for ( row = 0; row < pheight; row++ ) + { + gimp_pixel_rgn_get_row ( srcrgn, src_row, + 0, row * dheight / pheight, dwidth ); + dest = dest_row; + for ( col = 0; col < pwidth; col++ ) + { + src = &src_row[ src_col[col] ]; + if( !has_alpha || src[alpha] == OPAQUE ) + { + /* no alpha channel or opaque -- simple way */ + for ( b = 0; b < alpha; b++ ) + dest[b] = src[b]; + } + else + { + /* more or less transparent */ + if( ( col % (CHECKWIDTH*2) < CHECKWIDTH ) ^ + ( row % (CHECKWIDTH*2) < CHECKWIDTH ) ) + check = LIGHTCHECK; + else + check = DARKCHECK; + + if ( src[alpha] == 0 ) + { + /* full transparent -- check */ + for ( b = 0; b < alpha; b++ ) + dest[b] = check; + } + else + { + /* middlemost transparent -- mix check and src */ + for ( b = 0; b < alpha; b++ ) + dest[b] = ( src[b]*src[alpha] + check*(OPAQUE-src[alpha]) ) / OPAQUE; + } + } + dest += alpha; + } + gtk_preview_draw_row( GTK_PREVIEW( preview ), dest_row, + 0, row, pwidth ); + } + + g_free ( src_col ); + g_free ( src_row ); + g_free ( dest_row ); +} /* end render_preview */ + +/* ============================================================================ + * mov_path_prevw_draw + * Preview Rendering Util routine End + * if update & PREVIEW, draw preview + * if update & CURSOR, draw cross cursor + * ============================================================================ + */ + +static void +mov_path_prevw_draw ( t_mov_path_preview *path_ptr, gint update ) +{ + if( update & PREVIEW ) + { + path_ptr->cursor = FALSE; + if(gap_debug) printf("draw-preview\n"); + } + + if( update & CURSOR ) + { + if(gap_debug) printf("draw-cursor %d old=%d,%d cur=%d,%d\n", + path_ptr->cursor, path_ptr->oldx, path_ptr->oldy, path_ptr->curx, path_ptr->cury); + gdk_gc_set_function ( path_ptr->preview->style->black_gc, GDK_INVERT); + if( path_ptr->cursor ) + { + gdk_draw_line ( path_ptr->preview->window, + path_ptr->preview->style->black_gc, + path_ptr->oldx, 1, path_ptr->oldx, path_ptr->pheight-1 ); + gdk_draw_line ( path_ptr->preview->window, + path_ptr->preview->style->black_gc, + 1, path_ptr->oldy, path_ptr->pwidth-1, path_ptr->oldy ); + } + gdk_draw_line ( path_ptr->preview->window, + path_ptr->preview->style->black_gc, + path_ptr->curx, 1, path_ptr->curx, path_ptr->pheight-1 ); + gdk_draw_line ( path_ptr->preview->window, + path_ptr->preview->style->black_gc, + 1, path_ptr->cury, path_ptr->pwidth-1, path_ptr->cury ); + /* current position of cursor is updated */ + path_ptr->oldx = path_ptr->curx; + path_ptr->oldy = path_ptr->cury; + path_ptr->cursor = TRUE; + gdk_gc_set_function ( path_ptr->preview->style->black_gc, GDK_COPY); + } +} + + +/* + * CenterFrame entry callback + */ + +static void +mov_path_prevw_entry_update ( GtkWidget *widget, + gpointer data ) +{ + t_mov_path_preview *path_ptr; + gint *val, new_val; + + if(gap_debug) printf("entry\n"); + val = data; + new_val = atoi ( gtk_entry_get_text( GTK_ENTRY(widget) ) ); + + if( *val != new_val ) + { + *val = new_val; + path_ptr = gtk_object_get_user_data( GTK_OBJECT(widget) ); + if(gap_debug) printf("entry:newval in_call=%d\n", path_ptr->in_call ); + if( !path_ptr->in_call ) + { + mov_path_prevw_cursor_update( path_ptr ); + mov_path_prevw_draw ( path_ptr, CURSOR ); + } + } +} + + +/* + * Update the cross cursor's coordinates accoding to pvals->[xy]path_prevw + * but not redraw it + */ + +static void +mov_path_prevw_cursor_update ( t_mov_path_preview *path_ptr ) +{ + path_ptr->curx = path_ptr->p_x * path_ptr->pwidth / path_ptr->dwidth; + path_ptr->cury = path_ptr->p_y * path_ptr->pheight / path_ptr->dheight; + + if( path_ptr->curx < 0 ) path_ptr->curx = 0; + else if( path_ptr->curx >= path_ptr->pwidth ) path_ptr->curx = path_ptr->pwidth-1; + if( path_ptr->cury < 0 ) path_ptr->cury = 0; + else if( path_ptr->cury >= path_ptr->pheight) path_ptr->cury = path_ptr->pheight-1; + +} + +/* + * Handle the expose event on the preview + */ +static gint +mov_path_prevw_preview_expose( GtkWidget *widget, + GdkEvent *event ) +{ + t_mov_path_preview *path_ptr; + + path_ptr = gtk_object_get_user_data( GTK_OBJECT(widget) ); + mov_path_prevw_draw( path_ptr, ALL ); + return FALSE; +} + + +/* + * Handle other events on the preview + */ + +static gint +mov_path_prevw_preview_events ( GtkWidget *widget, + GdkEvent *event ) +{ + t_mov_path_preview *path_ptr; + GdkEventButton *bevent; + GdkEventMotion *mevent; + gchar buf[256]; + + path_ptr = gtk_object_get_user_data ( GTK_OBJECT(widget) ); + + switch (event->type) + { + case GDK_EXPOSE: + break; + + case GDK_BUTTON_PRESS: + bevent = (GdkEventButton *) event; + path_ptr->curx = bevent->x; + path_ptr->cury = bevent->y; + goto mouse; + + case GDK_MOTION_NOTIFY: + mevent = (GdkEventMotion *) event; + if ( !mevent->state ) break; + path_ptr->curx = mevent->x; + path_ptr->cury = mevent->y; + mouse: + mov_path_prevw_draw( path_ptr, CURSOR ); + path_ptr->in_call = TRUE; + sprintf(buf, "%d", path_ptr->curx * path_ptr->dwidth / path_ptr->pwidth ); + gtk_entry_set_text( GTK_ENTRY(path_ptr->xentry), buf ); + sprintf(buf, "%d", path_ptr->cury * path_ptr->dheight / path_ptr->pheight ); + gtk_entry_set_text( GTK_ENTRY(path_ptr->yentry), buf ); + path_ptr->in_call = FALSE; + break; + + default: + break; + } + + return FALSE; +} + +/* ============================================================================ + * Entry - Scale Pair + * ============================================================================ + */ + + +/***********************************************************************/ +/* */ +/* Create new entry-scale pair with label. (int) */ +/* 1 row and 2 cols of table are needed. */ +/* */ +/* `x' and `y' means starting row and col in `table'. */ +/* */ +/* `caption' is label string. */ +/* */ +/* `min', `max' are boundary of scale. */ +/* */ +/* `constraint' means whether value of *intvar should be constraint */ +/* by scale adjustment, e.g. between `min' and `max'. */ +/* */ +/***********************************************************************/ + +static GtkWidget* +mov_int_entryscale_new ( GtkTable *table, gint x, gint y, + gchar *caption, gint *intvar, + gint min, gint max, gint constraint, + GtkEntry **entry_pptr, + GtkAdjustment **adjustment_pptr, + char *tooltip) +{ + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *entry; + GtkWidget *scale; + GtkObject *adjustment; + t_mov_EntryScaleData *userdata; + gchar buffer[256]; + + + label = gtk_label_new (caption); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + + adjustment = gtk_adjustment_new ( *intvar, min, max, 1.0, 1.0, 0.0); + scale = gtk_hscale_new ( GTK_ADJUSTMENT(adjustment) ); + gtk_widget_set_usize (scale, SCALE_WIDTH, 0); + gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE); + if(adjustment_pptr != NULL) *adjustment_pptr = GTK_ADJUSTMENT(adjustment); + + entry = gtk_entry_new (); + gtk_widget_set_usize (entry, ENTRY_WIDTH, 0); + sprintf( buffer, "%d", *intvar ); + gtk_entry_set_text( GTK_ENTRY (entry), buffer ); + if(entry_pptr != NULL) *entry_pptr = GTK_ENTRY(entry); + + + userdata = g_new ( t_mov_EntryScaleData, 1 ); + userdata->entry = entry; + userdata->adjustment = adjustment; + userdata->constraint = constraint; + gtk_object_set_user_data (GTK_OBJECT(entry), userdata); + gtk_object_set_user_data (GTK_OBJECT(adjustment), userdata); + + gtk_signal_connect (GTK_OBJECT (entry), "changed", + (GtkSignalFunc) mov_paired_int_entry_update, + intvar); + gtk_signal_connect ( adjustment, "value_changed", + (GtkSignalFunc) mov_paired_int_scale_update, + intvar); + gtk_signal_connect ( GTK_OBJECT( entry ), "destroy", + (GtkSignalFunc) mov_paired_entry_destroy_callback, + userdata ); + + + hbox = gtk_hbox_new ( FALSE, 5 ); + gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, TRUE, 0); + + gtk_table_attach (GTK_TABLE (table), label, x, x+1, y, y+1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_table_attach (GTK_TABLE (table), hbox, x+1, x+2, y, y+1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + if(tooltip) + { + gtk_tooltips_set_tip(g_tooltips, entry, tooltip , NULL); + gtk_tooltips_set_tip(g_tooltips, scale, tooltip , NULL); + } + + gtk_widget_show (label); + gtk_widget_show (entry); + gtk_widget_show (scale); + gtk_widget_show (hbox); + + return label; +} + + +/* + when destroyed, userdata is destroyed too +*/ +static void +mov_paired_entry_destroy_callback (GtkWidget *widget, + gpointer data) +{ + t_mov_EntryScaleData *userdata; + userdata = data; + g_free ( userdata ); +} + +/* scale callback (int) */ +/* ==================== */ + +static void +mov_paired_int_scale_update (GtkAdjustment *adjustment, + gpointer data) +{ + t_mov_EntryScaleData *userdata; + GtkEntry *entry; + gchar buffer[256]; + gint *val, new_val; + + val = data; + new_val = (gint) adjustment->value; + + *val = new_val; + + userdata = gtk_object_get_user_data (GTK_OBJECT (adjustment)); + entry = GTK_ENTRY( userdata->entry ); + sprintf (buffer, "%d", (int) new_val ); + + /* avoid infinite loop (scale, entry, scale, entry ...) */ + gtk_signal_handler_block_by_data ( GTK_OBJECT(entry), data ); + gtk_entry_set_text ( entry, buffer); + gtk_signal_handler_unblock_by_data ( GTK_OBJECT(entry), data ); +} + +/* entry callback (int) */ +/* ==================== */ + + +static void +mov_paired_int_entry_update (GtkWidget *widget, + gpointer data) +{ + t_mov_EntryScaleData *userdata; + GtkAdjustment *adjustment; + int new_val, constraint_val; + int *val; + + val = data; + new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget))); + *val = new_val; + + userdata = gtk_object_get_user_data (GTK_OBJECT (widget)); + adjustment = GTK_ADJUSTMENT( userdata->adjustment ); + + constraint_val = new_val; + if ( constraint_val < adjustment->lower ) + constraint_val = adjustment->lower; + if ( constraint_val > adjustment->upper ) + constraint_val = adjustment->upper; + + if ( userdata->constraint ) + *val = constraint_val; + else + *val = new_val; + + adjustment->value = constraint_val; + gtk_signal_handler_block_by_data ( GTK_OBJECT(adjustment), data ); + gtk_signal_emit_by_name ( GTK_OBJECT(adjustment), "value_changed"); + gtk_signal_handler_unblock_by_data ( GTK_OBJECT(adjustment), data ); + +} + + +/* ============================================================================ + * p_paired_update + * update for both entry and adjustment (without constrint checks) + * (used when index of point table is changed) + * ============================================================================ + */ +void +p_paired_update(GtkEntry *entry, + GtkAdjustment *adjustment, + gpointer data) +{ + gchar l_buffer[30]; + int *val_ptr; + + val_ptr = (int *)data; + sprintf (l_buffer, "%d", *val_ptr ); + + /* avoid infinite loop (scale, entry, scale, entry ...) */ + gtk_signal_handler_block_by_data ( GTK_OBJECT(entry), data ); + gtk_entry_set_text ( entry, l_buffer); + gtk_signal_handler_unblock_by_data ( GTK_OBJECT(entry), data ); + + + adjustment->value = *val_ptr; + gtk_signal_handler_block_by_data ( GTK_OBJECT(adjustment), data ); + gtk_signal_emit_by_name ( GTK_OBJECT(adjustment), "value_changed"); + gtk_signal_handler_unblock_by_data ( GTK_OBJECT(adjustment), data ); +} /* end p_paired_update */ + +/* ============================================================================ + * p_get_flattened_drawable + * flatten the given image and return pointer to the + * (only) remaining drawable. + * ============================================================================ + */ +GDrawable * +p_get_flattened_drawable(gint32 image_id) +{ + GDrawable *l_drawable_ptr ; + gint l_nlayers; + gint32 *l_layers_list; + + gimp_image_flatten (image_id); + + /* get a list of layers for this image_ID */ + l_layers_list = gimp_image_get_layers (image_id, &l_nlayers); + /* use top layer for preview (should be the onnly layer after flatten) */ + l_drawable_ptr = gimp_drawable_get (l_layers_list[0]); + + g_free (l_layers_list); + + return l_drawable_ptr; +} /* end p_get_flattened_drawable */ + + + +/* ============================================================================ + * add the selected source layer to the temp. preview image + * (modified accordung to current settings) + * then flatten the temporary preview image, + * and return pointer to the (only) remaining drawable. + * ============================================================================ + */ + +GDrawable * p_get_prevw_drawable (t_mov_path_preview *path_ptr) +{ + t_mov_current l_curr; + int l_nlayers; + + + /* check if we have a source layer (to add to the preview) */ + if((pvals->src_layer_id >= 0) && (pvals->src_image_id >= 0)) + { + p_points_to_tab(path_ptr); + + /* calculate current settings */ + l_curr.dst_frame_nr = 0; + l_curr.deltaX = 0.0; + l_curr.deltaY = 0.0; + l_curr.deltaOpacity = 0.0; + l_curr.deltaWidth = 0.0; + l_curr.deltaHeight = 0.0; + l_curr.deltaRotation = 0.0; + + + l_curr.currX = (gdouble)path_ptr->p_x; + l_curr.currY = (gdouble)path_ptr->p_y; + l_curr.currOpacity = (gdouble)path_ptr->opacity; + l_curr.currWidth = (gdouble)path_ptr->w_resize; + l_curr.currHeight = (gdouble)path_ptr->h_resize; + l_curr.currRotation = (gdouble)path_ptr->rotation; + + l_curr.src_layer_idx = 0; + l_curr.src_layers = gimp_image_get_layers (pvals->src_image_id, &l_nlayers); + if((l_curr.src_layers != NULL) && (l_nlayers > 0)) + { + l_curr.src_last_layer = l_nlayers -1; + /* findout index of src_layer_id */ + for(l_curr.src_layer_idx = 0; + l_curr.src_layer_idx < l_nlayers; + l_curr.src_layer_idx++) + { + if(l_curr.src_layers[l_curr.src_layer_idx] == pvals->src_layer_id) + break; + } + + } + /* set offsets (in cur_ptr) + * according to handle_mode and src_img dimension (pvals) + */ + p_set_handle_offsets(pvals, &l_curr); + + /* render: add source layer to (temporary) preview image */ + p_mov_render(pvals->tmp_image_id, pvals, &l_curr); + + if(l_curr.src_layers != NULL) g_free(l_curr.src_layers); + } + + /* flatten image, and get the (only) resulting drawable */ + return(p_get_flattened_drawable(pvals->tmp_image_id)); + +} /* end p_get_prevw_drawable */ + + +/* ============================================================================ + * p_set_handle_offsets + * set handle offsets according to handle mode and src image dimensions + * ============================================================================ + */ +void p_set_handle_offsets(t_mov_values *val_ptr, t_mov_current *cur_ptr) +{ + guint l_src_width, l_src_height; /* dimensions of the source image */ + + /* get dimensions of source image */ + l_src_width = gimp_image_width(val_ptr->src_image_id); + l_src_height = gimp_image_height(val_ptr->src_image_id); + + cur_ptr->l_handleX = 0.0; + cur_ptr->l_handleY = 0.0; + switch(val_ptr->src_handle) + { + case GAP_HANDLE_LEFT_BOT: + cur_ptr->l_handleY += l_src_height; + break; + case GAP_HANDLE_RIGHT_TOP: + cur_ptr->l_handleX += l_src_width; + break; + case GAP_HANDLE_RIGHT_BOT: + cur_ptr->l_handleX += l_src_width; + cur_ptr->l_handleY += l_src_height; + break; + case GAP_HANDLE_CENTER: + cur_ptr->l_handleX += (l_src_width / 2); + cur_ptr->l_handleY += (l_src_height / 2); + break; + case GAP_HANDLE_LEFT_TOP: + default: + break; + } +} /* end p_set_handle_offsets */ + + +#define BOUNDS(a,x,y) ((a < x) ? x : ((a > y) ? y : a)) + +/* ============================================================================ + * p_mov_render + * insert current source layer into image + * at current settings (position, size opacity ...) + * ============================================================================ + */ + + /* why dont use: l_cp_layer_id = gimp_layer_copy(src_id); + * ==> Sorry this procedure works only for layers within the same image !! + * Workaround: + * use my 'private' version of layercopy + */ + +int p_mov_render(gint32 image_id, t_mov_values *val_ptr, t_mov_current *cur_ptr) +{ + gint32 l_cp_layer_id; + gint32 l_cp_layer_mask_id; + gint l_offset_x, l_offset_y; /* new offsets within dest. image */ + gint l_src_offset_x, l_src_offset_y; /* layeroffsets as they were in src_image */ + guint l_new_width; + guint l_new_height; + guint l_orig_width; + guint l_orig_height; + gint l_resized_flag; + gint32 l_interpolation; + int lx1, ly1, lx2, ly2; + guint l_image_width; + guint l_image_height; + + if(gap_debug) printf("p_mov_render: frame/layer: %ld/%ld X=%f, Y=%f + Width=%f Height=%f + Opacity=%f Rotate=%f clip_to_img = %d\n", + cur_ptr->dst_frame_nr, cur_ptr->src_layer_idx, + cur_ptr->currX, cur_ptr->currY, + cur_ptr->currWidth, + cur_ptr->currHeight, + cur_ptr->currOpacity, + cur_ptr->currRotation, + val_ptr->clip_to_img ); + + + + /* make a copy of the current source layer + * (using current opacity & paintmode values) + */ + l_cp_layer_id = p_my_layer_copy(image_id, + cur_ptr->src_layers[cur_ptr->src_layer_idx], + cur_ptr->currOpacity, + val_ptr->src_paintmode, + &l_src_offset_x, + &l_src_offset_y); + + /* add the copied layer to current destination image */ + if(gap_debug) printf("p_mov_render: after layer copy layer_id=%d\n", (int)l_cp_layer_id); + if(l_cp_layer_id < 0) + { + return -1; + } + + gimp_image_add_layer(image_id, l_cp_layer_id, + val_ptr->dst_layerstack); + + if(gap_debug) printf("p_mov_render: after add layer\n"); + + /* check for layermask */ + l_cp_layer_mask_id = gimp_layer_get_mask_id(l_cp_layer_id); + if(l_cp_layer_mask_id >= 0) + { + /* apply the layermask + * some transitions (especially rotate) cant operate proper on + * layers with masks ! + * (tests with gimp-rotate resulted in trashed images, + * even if the mask was rotated too) + */ + gimp_image_remove_layer_mask(image_id, l_cp_layer_id, 0 /* 0==APPLY */ ); + } + + l_resized_flag = 0; + l_orig_width = gimp_layer_width(l_cp_layer_id); + l_orig_height = gimp_layer_height(l_cp_layer_id); + l_new_width = l_orig_width; + l_new_height = l_orig_height; + + if((cur_ptr->currWidth > 100.01) || (cur_ptr->currWidth < 99.99) + || (cur_ptr->currHeight > 100.01) || (cur_ptr->currHeight < 99.99)) + { + /* have to scale layer */ + l_resized_flag = 1; + + l_new_width = (l_orig_width * cur_ptr->currWidth) / 100; + l_new_height = (l_orig_height * cur_ptr->currHeight) / 100; + gimp_layer_scale(l_cp_layer_id, l_new_width, l_new_height, 0); + + } + + if((cur_ptr->currRotation > 0.5) || (cur_ptr->currRotation < -0.5)) + { + l_resized_flag = 1; + l_interpolation = 1; /* rotate always with smoothing option turned on */ + + /* have to rotate the layer (rotation also changes size as needed) */ + p_gimp_rotate(image_id, l_cp_layer_id, l_interpolation, cur_ptr->currRotation); + + + l_new_width = gimp_layer_width(l_cp_layer_id); + l_new_height = gimp_layer_height(l_cp_layer_id); + } + + if(l_resized_flag == 1) + { + /* adjust offsets according to handle and change of size */ + switch(val_ptr->src_handle) + { + case GAP_HANDLE_LEFT_BOT: + l_src_offset_y += ((gint)l_orig_height - (gint)l_new_height); + break; + case GAP_HANDLE_RIGHT_TOP: + l_src_offset_x += ((gint)l_orig_width - (gint)l_new_width); + break; + case GAP_HANDLE_RIGHT_BOT: + l_src_offset_x += ((gint)l_orig_width - (gint)l_new_width); + l_src_offset_y += ((gint)l_orig_height - (gint)l_new_height); + break; + case GAP_HANDLE_CENTER: + l_src_offset_x += (((gint)l_orig_width - (gint)l_new_width) / 2); + l_src_offset_y += (((gint)l_orig_height - (gint)l_new_height) / 2); + break; + case GAP_HANDLE_LEFT_TOP: + default: + break; + } + } + + /* calculate offsets in destination image */ + l_offset_x = (cur_ptr->currX - cur_ptr->l_handleX) + l_src_offset_x; + l_offset_y = (cur_ptr->currY - cur_ptr->l_handleY) + l_src_offset_y; + + /* modify koordinate offsets of the copied layer within dest. image */ + gimp_layer_set_offsets(l_cp_layer_id, l_offset_x, l_offset_y); + + /* clip the handled layer to image size if desired */ + if(val_ptr->clip_to_img != 0) + { + l_image_width = gimp_image_width(image_id); + l_image_height = gimp_image_height(image_id); + + lx1 = BOUNDS (l_offset_x, 0, l_image_width); + ly1 = BOUNDS (l_offset_y, 0, l_image_height); + lx2 = BOUNDS ((l_new_width + l_offset_x), 0, l_image_width); + ly2 = BOUNDS ((l_new_height + l_offset_y), 0, l_image_height); + + l_new_width = lx2 - lx1; + l_new_height = ly2 - ly1; + + if (l_new_width && l_new_height) + { + gimp_layer_resize(l_cp_layer_id, l_new_width, l_new_height, + -(lx1 - l_offset_x), + -(ly1 - l_offset_y)); + } + else + { + /* no part of the layer is inside of the current frame (this image) + * instead of removing we make the layer small and move him outside + * the image. + * (that helps to keep the layerstack position of the inserted layer(s) + * constant in all handled anim_frames) + */ + gimp_layer_resize(l_cp_layer_id, 2, 2, -3, -3); + } + } + + if(gap_debug) printf("GAP p_mov_render: exit OK\n"); + + return 0; +} /* end p_mov_render */ + +/* ============================================================================ + * p_buildmenu + * build menu widget for all Items passed in the MenuItems Parameter + * MenuItems is an array of Pointers to Structure MenuItem. + * The End is marked by a Structure Member where the label is a NULL pointer + * (simplifyed version of GIMP 1.0.2 bulid_menu procedur) + * ============================================================================ + */ + +GtkWidget * +p_buildmenu (MenuItem *items) +{ + GtkWidget *menu; + GtkWidget *menu_item; + + menu = gtk_menu_new (); + + while (items->label) + { + menu_item = gtk_menu_item_new_with_label (items->label); + gtk_container_add (GTK_CONTAINER (menu), menu_item); + + if (items->callback) + gtk_signal_connect (GTK_OBJECT (menu_item), "activate", + (GtkSignalFunc) items->callback, + items->user_data); + + gtk_widget_show (menu_item); + items->widget = menu_item; + + items++; + } + + return menu; +} /* end p_buildmenu */ diff --git a/plug-ins/gap/gap_mov_dialog.h b/plug-ins/gap/gap_mov_dialog.h new file mode 100644 index 0000000000..7c353c1a06 --- /dev/null +++ b/plug-ins/gap/gap_mov_dialog.h @@ -0,0 +1,127 @@ +/* gap_mov_dialog.h + * 1997.11.06 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * Dialog Window for gap_mov + * + */ +/* 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. + */ +#ifndef _GAP_MOV_DIALOG_H +#define _GAP_MOV_DIALOG_H + +/* revision history: + * version 0.96.02; 1998.07.25 hof: added clip_to_img + */ + +typedef enum +{ + GAP_STEP_LOOP = 0, + GAP_STEP_LOOP_REV = 1, + GAP_STEP_ONCE = 2, + GAP_STEP_ONCE_REV = 3, + GAP_STEP_PING_PONG = 4, + GAP_STEP_NONE = 5 +} t_mov_stepmodes; + +typedef enum +{ + GAP_HANDLE_LEFT_TOP = 0, + GAP_HANDLE_LEFT_BOT = 1, + GAP_HANDLE_RIGHT_TOP = 2, + GAP_HANDLE_RIGHT_BOT = 3, + GAP_HANDLE_CENTER = 4 +} t_mov_handle; + +typedef struct { + long dst_frame_nr; /* current destination frame_nr */ + long src_layer_idx; /* index of current layer */ + gint32 *src_layers; /* array of source images layer id's */ + long src_last_layer; /* index of last layer 0 upto n-1 */ + gdouble currX, currY; + gdouble deltaX, deltaY; + gint l_handleX; + gint l_handleY; + + gdouble currOpacity; + gdouble deltaOpacity; + gdouble currWidth; + gdouble deltaWidth; + gdouble currHeight; + gdouble deltaHeight; + gdouble currRotation; + gdouble deltaRotation; +} t_mov_current; + +typedef struct { + gint p_x, p_y; /* +- koordinates */ + gint opacity; /* 0 upto 100% */ + gint w_resize; /* width resize 10 upto 300% */ + gint h_resize; /* height resize 10 upto 300% */ + gint rotation; /* rotatation +- degrees */ +} t_mov_point; + +#define GAP_MOV_MAX_POINT 256 + +/* + * Notes: + * - exchange of frames (load replace) + * keeps an images id, but all its + * layers were allocated new. + * this results in new layer_id's + * For this reason the source image MUST NOT be one of the Frames + * of the destination Animation !! + * - Rotation is now supported (since gap 0.95) (data structs are prepared for) + */ + +typedef struct { + gint32 src_image_id; /* source image */ + gint32 src_layer_id; /* id of layer (to begin with) */ + int src_handle; + int src_stepmode; + int src_paintmode; + gint src_only_visible; /* TRUE FALSE */ + gint clip_to_img; /* TRUE FALSE */ + + gint point_idx; /* 0 upto MAX_POINT -1 */ + gint point_idx_max; /* 0 upto MAX_POINT -1 */ + t_mov_point point[GAP_MOV_MAX_POINT]; + + gint dst_range_start; /* use current frame as default */ + gint dst_range_end; + gint dst_layerstack; + gint dst_combination_mode; /* GLayerMode */ + + /* for dialog only */ + gint32 dst_image_id; /* frame image */ + gint32 tmp_image_id; /* temp. flattened preview image */ + + +} t_mov_values; + +typedef struct { + t_anim_info *dst_ainfo_ptr; /* destination frames */ + t_mov_values *val_ptr; +} t_mov_data; + +long p_move_dialog (t_mov_data *mov_ptr); +void p_set_handle_offsets(t_mov_values *val_ptr, t_mov_current *cur_ptr); +int p_mov_render(gint32 image_id, t_mov_values *val_ptr, t_mov_current *cur_ptr); + +#endif diff --git a/plug-ins/gap/gap_mov_exec.c b/plug-ins/gap/gap_mov_exec.c new file mode 100644 index 0000000000..6fc51524fb --- /dev/null +++ b/plug-ins/gap/gap_mov_exec.c @@ -0,0 +1,453 @@ +/* gap_mov_exec.c + * 1997.11.06 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * Move : procedures for copying source layer(s) to multiple frames + * (varying Koordinates, opacity, size ...) + * + */ +/* 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: + * version 0.93.04 hof: Window with Info Message if no Source Image was selected in MovePath + * version 0.90.00; hof: 1.st (pre) release 14.Dec.1997 + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_layer_copy.h" +#include "gap_lib.h" +#include "gap_mov_dialog.h" +#include "gap_mov_exec.h" + +extern int gap_debug; /* ==0 ... dont print debug infos */ + +static int p_mov_call_render(t_mov_data *mov_ptr, t_mov_current *cur_ptr); +static void p_mov_advance_src_layer(t_mov_current *cur_ptr, int src_stepmode); +static long p_mov_execute(t_mov_data *mov_ptr); + +/* ============================================================================ + * p_mov_call_render + * load current frame, render and save back to disk + * ============================================================================ + */ + +int p_mov_call_render(t_mov_data *mov_ptr, t_mov_current *cur_ptr) +{ + t_anim_info *ainfo_ptr; + gint32 l_tmp_image_id; + int l_rc; + + l_rc = 0; + ainfo_ptr = mov_ptr->dst_ainfo_ptr; + + + if(ainfo_ptr->new_filename != NULL) free(ainfo_ptr->new_filename); + ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename, + cur_ptr->dst_frame_nr, + ainfo_ptr->extension); + if(ainfo_ptr->new_filename == NULL) + return -1; + + /* load next frame to render */ + l_tmp_image_id = p_load_image(ainfo_ptr->new_filename); + if(l_tmp_image_id < 0) + return -1; + + /* call render procedure for current image */ + if(0 == p_mov_render(l_tmp_image_id, mov_ptr->val_ptr, cur_ptr)) + { + /* if OK: save the rendered frame back to disk */ + if(p_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename) < 0) + l_rc = -1; + } + else l_rc = -1; + + + /* destroy the tmp image */ + gimp_image_delete(l_tmp_image_id); + + return l_rc; + +} /* end p_mov_call_render */ + + + +/* ============================================================================ + * p_mov_advance_src_layer + * advance layer index according to stepmode + * ============================================================================ + */ +void p_mov_advance_src_layer(t_mov_current *cur_ptr, int src_stepmode) +{ + static int l_ping = -1; + + if(gap_debug) printf("p_mov_advance_src_layer: stepmode=%d last_layer=%d idx=%d\n", + (int)src_stepmode, + (int)cur_ptr->src_last_layer, + (int)cur_ptr->src_layer_idx + ); + + /* note: top layer has index 0 + * therfore reverse loops have to count up + */ + if((cur_ptr->src_last_layer > 0 ) && (src_stepmode != GAP_STEP_NONE)) + { + switch(src_stepmode) + { + case GAP_STEP_ONCE_REV: + cur_ptr->src_layer_idx++; + if(cur_ptr->src_layer_idx > cur_ptr->src_last_layer) + { + cur_ptr->src_layer_idx = cur_ptr->src_last_layer; + } + break; + case GAP_STEP_ONCE: + cur_ptr->src_layer_idx--; + if(cur_ptr->src_layer_idx < 0) + { + cur_ptr->src_layer_idx = 0; + } + break; + case GAP_STEP_PING_PONG: + cur_ptr->src_layer_idx += l_ping; + if(l_ping < 0) + { + if(cur_ptr->src_layer_idx < 0) + { + cur_ptr->src_layer_idx = 1; + l_ping = 1; + } + } + else + { + if(cur_ptr->src_layer_idx > cur_ptr->src_last_layer) + { + cur_ptr->src_layer_idx = cur_ptr->src_last_layer - 1; + l_ping = -1; + } + } + + break; + case GAP_STEP_LOOP_REV: + cur_ptr->src_layer_idx++; + if(cur_ptr->src_layer_idx > cur_ptr->src_last_layer) + { + cur_ptr->src_layer_idx = 0; + } + break; + case GAP_STEP_LOOP: + default: + cur_ptr->src_layer_idx--; + if(cur_ptr->src_layer_idx < 0) + { + cur_ptr->src_layer_idx = cur_ptr->src_last_layer; + } + break; + + } + } +} /* end p_advance_src_layer */ + +/* ============================================================================ + * p_mov_execute + * Copy layer(s) from Sourceimage to given destination frame range, + * varying koordinates and opacity of the copied layer. + * To each affected destination frame exactly one is added. + * The source layer is iterated through all layers of the sourceimage + * according to stemmode parameter. + * For the placement the layers act as if their size is equal to their + * Sourceimages size. + * ============================================================================ + */ + +long p_mov_execute(t_mov_data *mov_ptr) +{ + int l_idx; + t_mov_current l_current_data; + t_mov_current *cur_ptr; + t_mov_values *val_ptr; + + gdouble l_percentage; + gdouble l_fpl; /* frames_per_line */ + long l_fpl2; /* frames_per_line rounded to integer*/ + long l_frame_step; + gdouble l_frames; + long l_cnt; + long l_points; + long l_ptidx; + long l_fridx; + gdouble l_flt_count; + int l_rc; + int l_nlayers; + + if(mov_ptr->val_ptr->src_image_id < 0) + { + p_msg_win(mov_ptr->dst_ainfo_ptr->run_mode, + "No Source Image was selected\n(Please open a 2nd Image of the same type before opening Move Path)"); + return -1; + } + + + l_percentage = 0.0; + if(mov_ptr->dst_ainfo_ptr->run_mode == RUN_INTERACTIVE) + { + gimp_progress_init("Copying Layers into Frames .."); + } + + if(gap_debug) + { + printf("p_mov_execute: values got from dialog:\n"); + printf("src_image_id :%ld\n", (long)mov_ptr->val_ptr->src_image_id); + printf("src_layer_id :%ld\n", (long)mov_ptr->val_ptr->src_layer_id); + printf("src_handle :%d\n", mov_ptr->val_ptr->src_handle); + printf("src_stepmode :%d\n", mov_ptr->val_ptr->src_stepmode); + printf("src_paintmode :%d\n", mov_ptr->val_ptr->src_paintmode); + printf("clip_to_img :%d\n", mov_ptr->val_ptr->clip_to_img); + printf("dst_range_start :%d\n", (int)mov_ptr->val_ptr->dst_range_start); + printf("dst_range_end :%d\n", (int)mov_ptr->val_ptr->dst_range_end); + printf("dst_layerstack :%d\n", (int)mov_ptr->val_ptr->dst_layerstack); + for(l_idx = 0; l_idx <= mov_ptr->val_ptr->point_idx_max; l_idx++) + { + printf("p_x[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_x); + printf("p_y[%d] : :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_y); + printf("opacity[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].opacity); + printf("w_resize[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].w_resize); + printf("h_resize[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].h_resize); + printf("rotation[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].rotation); + } + printf("\n"); + } + + l_rc = 0; + cur_ptr = &l_current_data; + val_ptr = mov_ptr->val_ptr; + + /* set offsets (in cur_ptr) according to handle mode and src_img dimension */ + p_set_handle_offsets(val_ptr, cur_ptr); + + + /* test for invers range */ + if(val_ptr->dst_range_start > val_ptr->dst_range_end) + { + /* step down */ + l_frame_step = -1; + l_cnt = 1 + (val_ptr->dst_range_start - val_ptr->dst_range_end); + } + else + { + l_frame_step = 1; + l_cnt = 1 + (val_ptr->dst_range_end - val_ptr->dst_range_start); + } + + l_frames = (gdouble)l_cnt; /* nr. of affected frames */ + l_points = val_ptr->point_idx_max +1; /* nr. of available points */ + + if(l_points > l_frames) + { + /* cut off some points if we got more than frames */ + val_ptr->point[l_cnt].p_x = val_ptr->point[l_cnt].p_x; + val_ptr->point[l_cnt].p_y = val_ptr->point[l_cnt].p_y; + val_ptr->point[l_cnt].opacity = val_ptr->point[l_cnt].opacity; + val_ptr->point[l_cnt].w_resize = val_ptr->point[l_cnt].w_resize; + val_ptr->point[l_cnt].h_resize = val_ptr->point[l_cnt].h_resize; + val_ptr->point[l_cnt].rotation = val_ptr->point[l_cnt].rotation; + l_points = l_cnt; + } + + + cur_ptr->dst_frame_nr = val_ptr->dst_range_start; + + cur_ptr->src_layers = gimp_image_get_layers (val_ptr->src_image_id, &l_nlayers); + if(cur_ptr->src_layers == NULL) + { + printf("ERROR (in p_mov_execute): Got no layers from SrcImage\n"); + return -1; + } + if(l_nlayers < 1) + { + printf("ERROR (in p_mov_execute): Source Image has no layers\n"); + return -1; + } + cur_ptr->src_last_layer = l_nlayers -1; + + /* findout index of src_layer_id */ + for(cur_ptr->src_layer_idx = 0; + cur_ptr->src_layer_idx < l_nlayers; + cur_ptr->src_layer_idx++) + { + if(cur_ptr->src_layers[cur_ptr->src_layer_idx] == val_ptr->src_layer_id) + break; + } + cur_ptr->src_last_layer = l_nlayers -1; /* index of last layer */ + + cur_ptr->currX = (gdouble)val_ptr->point[0].p_x; + cur_ptr->currY = (gdouble)val_ptr->point[0].p_y; + cur_ptr->currOpacity = (gdouble)val_ptr->point[0].opacity; + cur_ptr->currWidth = (gdouble)val_ptr->point[0].w_resize; + cur_ptr->currHeight = (gdouble)val_ptr->point[0].h_resize; + cur_ptr->currRotation = (gdouble)val_ptr->point[0].rotation; + + /* RENDER add current src_layer to current frame */ + l_rc = p_mov_call_render(mov_ptr, cur_ptr); + + + /* how many frames are affected from one line of the moving path */ + l_fpl = ((gdouble)l_frames - 1.0) / ((gdouble)(l_points -1)); + l_fpl2 = (l_fpl + 0.5); + + l_ptidx = 1; + l_flt_count = 0.0; + + + /* loop for each frame within the range (may step up or down) */ + cur_ptr->dst_frame_nr = val_ptr->dst_range_start; + for(l_fridx = 1; l_fridx < l_cnt; l_fridx++) + { + + if(gap_debug) printf("p_mov_execute: l_fridx=%ld, l_flt_count=%f, l_rc=%d\n", + l_fridx, l_flt_count, (int)l_rc); + + if(l_rc != 0) break; + + /* advance frame_nr, (1st frame was done outside this loop) */ + cur_ptr->dst_frame_nr += l_frame_step; /* +1 or -1 */ + + if((gdouble)l_fridx > l_flt_count) + { + /* re-adjust current values. (only to avoid rounding errors) + * current values should already contain the point values[l_ptidx] + */ + cur_ptr->currX = (gdouble)val_ptr->point[l_ptidx -1].p_x; + cur_ptr->currY = (gdouble)val_ptr->point[l_ptidx -1].p_y; + cur_ptr->currOpacity = (gdouble)val_ptr->point[l_ptidx -1].opacity; + cur_ptr->currWidth = (gdouble)val_ptr->point[l_ptidx -1].w_resize; + cur_ptr->currHeight = (gdouble)val_ptr->point[l_ptidx -1].h_resize; + cur_ptr->currRotation = (gdouble)val_ptr->point[l_ptidx -1].rotation; + + /* change deltas for next line of the move path */ + cur_ptr->deltaX = ((gdouble)val_ptr->point[l_ptidx].p_x - (gdouble)val_ptr->point[l_ptidx -1].p_x) / (gdouble)l_fpl2; + cur_ptr->deltaY = ((gdouble)val_ptr->point[l_ptidx].p_y - (gdouble)val_ptr->point[l_ptidx -1].p_y) / (gdouble)l_fpl2; + cur_ptr->deltaOpacity = ((gdouble)val_ptr->point[l_ptidx].opacity - (gdouble)val_ptr->point[l_ptidx -1].opacity) / (gdouble)l_fpl2; + cur_ptr->deltaWidth = ((gdouble)val_ptr->point[l_ptidx].w_resize - (gdouble)val_ptr->point[l_ptidx -1].w_resize) / (gdouble)l_fpl2; + cur_ptr->deltaHeight = ((gdouble)val_ptr->point[l_ptidx].h_resize - (gdouble)val_ptr->point[l_ptidx -1].h_resize) / (gdouble)l_fpl2; + cur_ptr->deltaRotation = ((gdouble)val_ptr->point[l_ptidx].rotation - (gdouble)val_ptr->point[l_ptidx -1].rotation) / (gdouble)l_fpl2; + + l_ptidx++; + l_flt_count += l_fpl; + } + + /* advance settings for next frame */ + p_mov_advance_src_layer(cur_ptr, val_ptr->src_stepmode); + + cur_ptr->currX += cur_ptr->deltaX; + cur_ptr->currY += cur_ptr->deltaY; + cur_ptr->currOpacity += cur_ptr->deltaOpacity; + cur_ptr->currWidth += cur_ptr->deltaWidth; + cur_ptr->currHeight += cur_ptr->deltaHeight; + cur_ptr->currRotation += cur_ptr->deltaRotation; + + /* RENDER add current src_layer to current frame */ + l_rc = p_mov_call_render(mov_ptr, cur_ptr); + + /* show progress */ + if(mov_ptr->dst_ainfo_ptr->run_mode == RUN_INTERACTIVE) + { + l_percentage = (gdouble)l_fridx / (gdouble)(l_cnt -1); + gimp_progress_update (l_percentage); + } + + } + + if(cur_ptr->src_layers != NULL) g_free(cur_ptr->src_layers); + + return l_rc; + +} /* end p_mov_execute */ + + + +/* ============================================================================ + * gap_move + * ============================================================================ + */ +int gap_move(GRunModeType run_mode, gint32 image_id) +{ + int l_rc; + t_anim_info *ainfo_ptr; + t_mov_data l_mov_data; + + l_rc = -1; + ainfo_ptr = p_alloc_ainfo(image_id, run_mode); + if(ainfo_ptr != NULL) + { + l_mov_data.val_ptr = malloc(sizeof(t_mov_values)); + if(NULL != l_mov_data.val_ptr) + { + if (0 == p_dir_ainfo(ainfo_ptr)) + { + if(0 != p_chk_framerange(ainfo_ptr)) return -1; + + l_mov_data.dst_ainfo_ptr = ainfo_ptr; + if(run_mode == RUN_INTERACTIVE) + { + l_rc = p_move_dialog (&l_mov_data); + if(0 != p_chk_framechange(ainfo_ptr)) + { + l_rc = -1; + } + } + else + { + l_rc = -1; /* run NON_INTERACTIVE not implemented (define args) */ + } + + if(l_rc >= 0) + { + l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename); + if(l_rc >= 0) + { + l_rc = p_mov_execute(&l_mov_data); + + /* go back to the frame_nr where move operation was started from */ + p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename); + } + } + } + free(l_mov_data.val_ptr); + } + + p_free_ainfo(&ainfo_ptr); + } + + return(l_rc); +} /* end gap_move */ + diff --git a/plug-ins/gap/gap_mov_exec.h b/plug-ins/gap/gap_mov_exec.h new file mode 100644 index 0000000000..3fcefa38f0 --- /dev/null +++ b/plug-ins/gap/gap_mov_exec.h @@ -0,0 +1,43 @@ +/* gap_lib.h + * 1997.11.01 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * basic anim functions + * + */ +/* 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: + * 0.96.00; 1998/06/27 hof: added gap animation sizechange plugins + * (moved range_ops to seperate .h file) + * 0.94.01; 1998/04/27 hof: added flatten_mode to plugin: gap_range_to_multilayer + * 0.90.00; hof: 1.st (pre) release + */ + +#ifndef _GAP_MOV_EXEC_H +#define _GAP_MOV_EXEC_H + +#include "libgimp/gimp.h" + +int gap_move(GRunModeType run_mode, gint32 image_id); + +#endif + + diff --git a/plug-ins/gap/gap_mpege.c b/plug-ins/gap/gap_mpege.c new file mode 100644 index 0000000000..c45b008a73 --- /dev/null +++ b/plug-ins/gap/gap_mpege.c @@ -0,0 +1,1029 @@ +/* gap_mpege.c + * 1998.07.04 hof (Wolfgang Hofer) + * + * GAP ... Gimp Animation Plugins + * + * This Module contains: + * - gap_mpeg_encode + * GIMP/GAP-frontend interfaces for 2 MPEG encoder Programs + * + * 1) mpeg_encode 1.5 + * the free Berkeley MPEG-1 encoder + * 2) mpeg2encode + * MPEG-2 and MPEG-1 Encoder / Decoder, Version 1.2 + * (MPEG Software Simulation Group) + * Web: http://www.mpeg.org/MSSG/ + * FTP: ftp://ftp.mpeg.org/pub/mpeg/mssg/ + * E-mail: mssg@mpeg.org (author contact) + * + */ +/* 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 + * 0.99.00; 1999/03/15 hof: prepared for win/dos filename conventions + * 0.96.00; 1998/07/08 hof: first release + */ + +/* SYTEM (UNIX) includes */ +#include +#include +#include +#include + +/* GIMP includes */ +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +/* GAP includes */ +#include "gap_lib.h" +#include "gap_arr_dialog.h" +#include "gap_mpege.h" + +extern int gap_debug; /* ==0 ... dont print debug infos */ + +#define MBUF_SIZE 128 + +typedef struct t_mpg_par +{ + gint from; + gint to; + + gint bitrate; + + char *outfile; + char *parfile; + char *startscript; + char *ext; + + /* members used in mpeg_encode only: */ + gint const_bitrate; + gint iqscale; + gint pqscale; + gint bqscale; + char *pattern; + char *framerate; + char *psearch; + char *bsearch; + + /* members used in mpeg2encode only: */ + gint frate; + gint videoformat; + gint mpegtype; +} t_mpg_par; + + +/* ============================================================================ + * p_mpege_info + * ============================================================================ + */ +static +int p_mpege_info(t_anim_info *ainfo_ptr, char *errlist, t_gap_mpeg_encoder encoder) +{ + t_arr_arg argv[13]; + t_but_arg b_argv[2]; + + int l_idx; + int l_rc; + + + l_idx = 0; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = "Conditions to run mpeg_encode 1.5:"; + if(encoder == MPEG2ENCODE) + { + argv[l_idx].label_txt = "Conditions to run mpeg2encode 1.2:"; + } + + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = ""; + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = "1.) mpeg_encode 1.5 must be installed"; + if(encoder == MPEG2ENCODE) + { + argv[l_idx].label_txt = "1.) mpeg2encode 1.2 must be installed"; + } + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = " you can get mpeg_encode at"; + if(encoder == MPEG2ENCODE) + { + argv[l_idx].label_txt = " you can get mpeg2encode at http://www.mpeg.org/MSSG"; + } + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = " ftp://mm-ftp.cs.berkeley.edu/pub/multimedia/mpeg/bmt1r1.tar.gz"; + if(encoder == MPEG2ENCODE) + { + argv[l_idx].label_txt = " or at ftp://ftp.mpeg.org/pub/mpeg/mssg "; + } + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = "2.) You need a series of single Images on disk (AnimFrames)"; + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = " all with fileformat JPEG (or YUV or PNM or PPM)"; + if(encoder == MPEG2ENCODE) + { + argv[l_idx].label_txt = " all with fileformat PPM (or YUV)"; + } + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = " (use 'Frames Convert' from the AnimFrames Menu"; + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = " or 'Split Img to Frames' from the AnimFrames Menu)"; + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = "3.) All Images must have the same size,"; + + if(encoder == MPEG_ENCODE) + { + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = " width and height must be a multiple of 16"; + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = " (use Scale or Crop from the AnimFrames Menu)"; + } + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_LABEL); + argv[l_idx].label_txt = errlist; + + l_idx++; + + /* the Action Button */ + b_argv[0].but_txt = "CANCEL"; + b_argv[0].but_val = -1; + b_argv[1].but_txt = "OK"; + b_argv[1].but_val = 0; + + l_rc = p_array_std_dialog("MPEG_ENCODE Information", + "", + l_idx, argv, /* widget array */ + 2, b_argv, /* button array */ + -1); + + return (l_rc); +} /* end p_mpege_info */ + + +/* ============================================================================ + * p_mpege_dialog + * retcode -1 ... on cancel + * 0 ... Generate Paramfile + * 1 ... Generate Paramfile and start mpeg_encode + * ============================================================================ + */ +static +int p_mpege_dialog(t_anim_info *ainfo_ptr, t_mpg_par *mp_ptr, t_gap_mpeg_encoder encoder) +{ + static t_arr_arg argv[15]; + static t_but_arg b_argv[3]; + gint l_rc; + gint l_idx; + + static char l_buf[MBUF_SIZE]; + static char l_startscript[MBUF_SIZE]; + static char l_parfile[MBUF_SIZE]; + static char l_outfile[MBUF_SIZE]; + static char l_pattern[MBUF_SIZE]; + + static char *frate_args[8] = {"23.976", "24", "25", "29.97", "30", "50", "59.94", "60" }; + static char *psearch_args[3] = {"EXHAUSTIVE", "SUBSAMPLE", "LOGARITHMIC" }; + static char *bsearch_args[3] = {"SIMPLE", "CROSS2", "EXHAUSTIVE" }; + static char *video_args[5] = {"Comp", "PAL", "NTSC", "SECAM", "MAC" }; + static char *mpeg_args[2] = {"MPEG1", "MPEG2" }; + static char *mpeg_help[2] = {"generate MPEG1 (ISO/IEC 11172-2) stream", + "generate MPEG2 (ISO/IEC DIS 13818-2) stream" }; + + l_rc = -1; + + /* the 3 Action Buttons */ + b_argv[0].but_txt = "Cancel"; + b_argv[0].but_val = -1; + b_argv[1].but_txt = "GenParams"; + b_argv[1].but_val = 0; + b_argv[2].but_txt = "Gen + Encode"; + b_argv[2].but_val = 1; + + sprintf (l_outfile, "%s.mpg", ainfo_ptr->basename); + sprintf (l_parfile, "%s.par_mpg", ainfo_ptr->basename); + sprintf (l_startscript, "%s.sh", ainfo_ptr->basename); + + p_init_arr_arg(&argv[0], WGT_LABEL); + argv[0].label_txt = &l_buf[0]; + + p_init_arr_arg(&argv[1], WGT_INT_PAIR); + argv[1].constraint = TRUE; + argv[1].label_txt = "From Frame:"; + argv[1].help_txt = "first handled frame"; + 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; + + p_init_arr_arg(&argv[2], WGT_INT_PAIR); + argv[2].constraint = TRUE; + argv[2].label_txt = "To Frame:"; + argv[2].help_txt = "last handled frame"; + argv[2].int_min = (gint)ainfo_ptr->first_frame_nr; + argv[2].int_max = (gint)ainfo_ptr->last_frame_nr; + argv[2].int_ret = (gint)ainfo_ptr->last_frame_nr; + + p_init_arr_arg(&argv[3], WGT_OPTIONMENU); + argv[3].label_txt = "Framerate :"; + argv[3].help_txt = "framerate in frames/second"; + argv[3].radio_argc = 8; + argv[3].radio_argv = frate_args; + argv[3].radio_ret = 4; + + p_init_arr_arg(&argv[4], WGT_INT_PAIR); + argv[4].constraint = FALSE; + argv[4].label_txt = "Bitrate:"; + argv[4].help_txt = "used for constant bitrates (bit/sec) \n(low rate gives good compression + bad quality)"; + argv[4].int_min = 500000; + argv[4].int_step = 100000; + argv[4].int_max = 9000000; + argv[4].int_ret = 3000000; + + if(encoder == MPEG_ENCODE) l_idx = 12; + else l_idx = 7; + + + p_init_arr_arg(&argv[l_idx], WGT_FILESEL); + argv[l_idx].label_txt ="Outputfile:"; + argv[l_idx].entry_width = 140; /* pixel */ + argv[l_idx].help_txt ="Name of the resulting MPEG outputfile"; + argv[l_idx].text_buf_len = sizeof(l_outfile); + argv[l_idx].text_buf_ret = &l_outfile[0]; + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_FILESEL); + argv[l_idx].label_txt ="Paramfile:"; + argv[l_idx].entry_width = 140; /* pixel */ + argv[l_idx].help_txt ="Name of the Encoder-Parameterfile\n(is generated)"; + argv[l_idx].text_buf_len = sizeof(l_parfile); + argv[l_idx].text_buf_ret = &l_parfile[0]; + + l_idx++; + p_init_arr_arg(&argv[l_idx], WGT_FILESEL); + argv[l_idx].label_txt ="Startscript:"; + argv[l_idx].entry_width = 140; /* pixel */ + argv[l_idx].help_txt ="Name of the Startscript \n(is generated/executed)"; + argv[l_idx].text_buf_len = sizeof(l_startscript); + argv[l_idx].text_buf_ret = &l_startscript[0]; + + if(encoder == MPEG_ENCODE) + { + sprintf(l_buf, + "Generate parameterfile for mpeg_encode 1.5\n(the freely distributed Berkeley MPEG-1 Video Encoder.)\n"); + + + p_init_arr_arg(&argv[5], WGT_TOGGLE); + argv[5].label_txt = "Constant Bitrate :"; + argv[5].help_txt ="Iqnore I/P/QSCALE values and use constant bit-rate)"; + argv[5].int_ret = 1; + + sprintf (l_pattern, "IBBPBBPBBPBBPBBP"); + p_init_arr_arg(&argv[6], WGT_TEXT); + argv[6].label_txt ="Pattern:"; + argv[6].entry_width = 140; /* pixel */ + argv[6].help_txt ="How to encode MPEG framesequence (I/P/B frames)"; + argv[6].text_buf_len = sizeof(l_pattern); + argv[6].text_buf_ret = &l_pattern[0]; + + p_init_arr_arg(&argv[7], WGT_INT_PAIR); + argv[7].constraint = TRUE; + argv[7].label_txt = "IQSCALE:"; + argv[7].help_txt = "Quality scale for I-Frames \n(1 = best quality, 31 = best comression)"; + argv[7].int_min = 1; + argv[7].int_max = 31; + argv[7].int_ret = 2; + + p_init_arr_arg(&argv[8], WGT_INT_PAIR); + argv[8].constraint = TRUE; + argv[8].label_txt = "PQSCALE:"; + argv[8].help_txt = "Quality scale for P-Frames \n(1 = best quality, 31 = best comression)"; + argv[8].int_min = 1; + argv[8].int_max = 31; + argv[8].int_ret = 5; + + p_init_arr_arg(&argv[9], WGT_INT_PAIR); + argv[9].constraint = TRUE; + argv[9].label_txt = "BQSCALE:"; + argv[9].help_txt = "Quality scale for B-Frames \n(1 = best quality, 31 = best comression)"; + argv[9].int_min = 1; + argv[9].int_max = 31; + argv[9].int_ret = 9; + + + p_init_arr_arg(&argv[10], WGT_OPTIONMENU); + argv[10].label_txt = "P-Search :"; + argv[10].help_txt = "Search Algorithmus used for P-frames"; + argv[10].radio_argc = 3; + argv[10].radio_argv = psearch_args; + argv[10].radio_ret = 1; + + p_init_arr_arg(&argv[11], WGT_OPTIONMENU); + argv[11].label_txt = "B-Search :"; + argv[11].help_txt = "Search Algorithmus used for B-frames"; + argv[11].radio_argc = 3; + argv[11].radio_argv = bsearch_args; + argv[11].radio_ret = 1; + + l_rc = p_array_std_dialog("Gen MPEG_ENCODE Parameters", + "Encode Values", + 15, argv, /* widget array */ + 3, b_argv, /* button array */ + 0); + + mp_ptr->const_bitrate = argv[5].int_ret; + mp_ptr->iqscale = argv[7].int_ret; + mp_ptr->pqscale = argv[8].int_ret; + mp_ptr->bqscale = argv[9].int_ret; + mp_ptr->psearch = psearch_args[argv[10].radio_ret]; + mp_ptr->bsearch = bsearch_args[argv[11].radio_ret]; + } + + + if(encoder == MPEG2ENCODE) + { + sprintf(l_buf, + "Generate parameterfile for mpeg2encode 1.2\n(MPEG-2 Video Encoder.)\n"); + + p_init_arr_arg(&argv[5], WGT_RADIO); + argv[5].label_txt = "MPEG-type :"; + argv[5].radio_argc = 2; + argv[5].radio_argv = mpeg_args; + argv[5].radio_help_argv = mpeg_help; + argv[5].radio_ret = 1; + + + + p_init_arr_arg(&argv[6], WGT_OPTIONMENU); + argv[6].label_txt = "Videoformat :"; + argv[6].help_txt = "Videoformat"; + argv[6].radio_argc = 5; + argv[6].radio_argv = video_args; + argv[6].radio_ret = 1; + + + argv[3].radio_argc = 5; /* framerates above 30 ar not allowed in mpeg2encode */ + + l_rc = p_array_std_dialog("Gen MPEG2ENCODE Parameters", + "Encode Values", + 10, argv, /* widget array */ + 3, b_argv, /* button array */ + 0); + mp_ptr->mpegtype = argv[5].radio_ret; + mp_ptr->videoformat = argv[6].radio_ret; + + } + + if(argv[1].int_ret <= argv[2].int_ret) + { + mp_ptr->from = argv[1].int_ret; + mp_ptr->to = argv[2].int_ret; + } + else + { + mp_ptr->from = argv[2].int_ret; + mp_ptr->to = argv[1].int_ret; + } + + mp_ptr->frate = argv[3].radio_ret +1; + mp_ptr->framerate = frate_args[argv[3].radio_ret]; + mp_ptr->bitrate = argv[4].int_ret; + + + mp_ptr->pattern = &l_pattern[0]; + mp_ptr->outfile = &l_outfile[0]; + mp_ptr->parfile = &l_parfile[0]; + mp_ptr->startscript = &l_startscript[0]; + + return (l_rc); +} /* end p_mpege_dialog */ + +/* ============================================================================ + * p_mpege_extension_check + * ============================================================================ + */ + +static +char *p_mpege_extension_check(t_anim_info *ainfo_ptr) +{ + /* list of direct supported fileformats of mpeg_encode */ + static char *base_file_formats [7] = { "\0", "JPEG", "PNM", "PPM", "JMOVIE", "Y" "YUV"}; + + int l_ffidx; + l_ffidx = 0; /* undefined */ + if(ainfo_ptr->extension != NULL) + { + if ( strcmp(ainfo_ptr->extension, ".jpg" ) == 0) l_ffidx = 1; + if ( strcmp(ainfo_ptr->extension, ".jpeg") == 0) l_ffidx = 1; + if ( strcmp(ainfo_ptr->extension, ".JPG" ) == 0) l_ffidx = 1; + if ( strcmp(ainfo_ptr->extension, ".JPEG") == 0) l_ffidx = 1; + + if ( strcmp(ainfo_ptr->extension, ".pnm" ) == 0) l_ffidx = 2; + if ( strcmp(ainfo_ptr->extension, ".PNM" ) == 0) l_ffidx = 2; + + if ( strcmp(ainfo_ptr->extension, ".ppm" ) == 0) l_ffidx = 3; + if ( strcmp(ainfo_ptr->extension, ".PPM" ) == 0) l_ffidx = 3; + + if ( strcmp(ainfo_ptr->extension, ".yuv" ) == 0) l_ffidx = 6; + if ( strcmp(ainfo_ptr->extension, ".YUV" ) == 0) l_ffidx = 6; + } + + return(base_file_formats[l_ffidx]); +} /* end p_mpege_extension_check */ + +static +int p_mpeg2_extension_check(t_anim_info *ainfo_ptr) +{ + int l_ffidx; + l_ffidx = -1; /* format not supported */ + + if(ainfo_ptr->extension != NULL) + { + if ( strcmp(ainfo_ptr->extension, ".ppm" ) == 0) l_ffidx = 2; + if ( strcmp(ainfo_ptr->extension, ".PPM" ) == 0) l_ffidx = 2; + + if ( strcmp(ainfo_ptr->extension, ".yuv" ) == 0) l_ffidx = 1; + if ( strcmp(ainfo_ptr->extension, ".YUV" ) == 0) l_ffidx = 1; + } + + return(l_ffidx); +} /* end p_mpege_extension_check */ + + +/* ============================================================================ + * p_mpeg2encode_gen_parfile + * ============================================================================ + */ +static +int p_mpeg2encode_gen_parfile(t_anim_info *ainfo_ptr, t_mpg_par *mp_ptr) +{ + FILE *l_fp; + int l_idx; + int l_base_ffidx; + gint l_width; + gint l_height; + char *l_dirname_ptr; + char *l_basename_ptr; + char l_basename_buff[1024]; + + l_fp = fopen(mp_ptr->parfile, "w"); + if(l_fp == NULL) + { + fprintf(stderr, "cant open MPEG Paramfile %s for write\n", mp_ptr->parfile); + return -1; + } + + l_base_ffidx = p_mpeg2_extension_check(ainfo_ptr); + + /* check if ainfo_ptr->basename contains directory part */ + strcpy(l_basename_buff, ainfo_ptr->basename); + l_dirname_ptr = "."; + l_basename_ptr = &l_basename_buff[0]; + + for(l_idx = strlen(l_basename_buff) -1; l_idx >= 0; l_idx--) + { + if(l_basename_buff[l_idx] == G_DIR_SEPARATOR) + { + l_basename_buff[l_idx] = '\0'; + l_basename_ptr = &l_basename_buff[l_idx +1]; + l_dirname_ptr = &l_basename_buff[0]; + break; + } + } + + /* get info about the image (size is common to all frames) */ + l_width = gimp_image_width(ainfo_ptr->image_id); + l_height = gimp_image_height(ainfo_ptr->image_id); + + if(mp_ptr->mpegtype == 0) + { + fprintf(l_fp, "MPEG-1 stream %s frames/sec\n", mp_ptr->framerate); + } + else + { + fprintf(l_fp, "MPEG-2 stream %s frames/sec\n", mp_ptr->framerate); + } + + fprintf(l_fp, "%s_%%04d /* name of source files */\n", ainfo_ptr->basename); + + fprintf(l_fp, "- /* name of reconstructed images (\"-\": don't store) */\n"); + fprintf(l_fp, "- /* name of intra quant matrix file (\"-\": default matrix) */\n"); + fprintf(l_fp, "- /* name of non intra quant matrix file (\"-\": default matrix) */\n"); + fprintf(l_fp, "- /* name of statistics file (\"-\": stdout ) */\n"); + + fprintf(l_fp, "%d /* input picture file format: 0=*.Y,*.U,*.V, 1=*.yuv, 2=*.ppm */\n", l_base_ffidx); + fprintf(l_fp, "%d /* number of frames */\n", (int)(mp_ptr->to - mp_ptr->from)+1); + fprintf(l_fp, "%d /* number of first frame */\n",(int)mp_ptr->from); + + fprintf(l_fp, "00:00:00:00 /* timecode of first frame */\n"); + fprintf(l_fp, "12 /* N (# of frames in GOP) */\n"); + fprintf(l_fp, "3 /* M (I/P frame distance) */\n"); + + if(mp_ptr->mpegtype == 0) + { + fprintf(l_fp, "1 /* ISO/IEC 11172-2 stream */\n"); /* MPEG1 */ + } + else + { + fprintf(l_fp, "0 /* ISO/IEC 11172-2 stream */\n"); /* MPEG2 */ + } + fprintf(l_fp, "0 /* 0:frame pictures, 1:field pictures */\n"); + + fprintf(l_fp, "%d /* horizontal_size */\n", (int)l_width); + fprintf(l_fp, "%d /* vertical_size */\n", (int)l_height); + + fprintf(l_fp, "2 /* aspect_ratio_information 1=square pel, 2=4:3, 3=16:9, 4=2.11:1 */\n"); + + fprintf(l_fp, "%d /* frame_rate_code 1=23.976, 2=24, 3=25, 4=29.97, 5=30 frames/sec. */\n", (int)mp_ptr->frate); + fprintf(l_fp, "%d.0 /* bit_rate (bits/s) */\n", (int)mp_ptr->bitrate); + fprintf(l_fp, "112 /* vbv_buffer_size (in multiples of 16 kbit) */\n"); + fprintf(l_fp, "0 /* low_delay */\n"); + fprintf(l_fp, "0 /* constrained_parameters_flag */\n"); + fprintf(l_fp, "4 /* Profile ID: Simple = 5, Main = 4, SNR = 3, Spatial = 2, High = 1 */\n"); + fprintf(l_fp, "8 /* Level ID: Low = 10, Main = 8, High 1440 = 6, High = 4 */\n"); + fprintf(l_fp, "0 /* progressive_sequence */\n"); + fprintf(l_fp, "1 /* chroma_format: 1=4:2:0, 2=4:2:2, 3=4:4:4 */\n"); + + fprintf(l_fp, "%d /* video_format: 0=comp., 1=PAL, 2=NTSC, 3=SECAM, 4=MAC, 5=unspec. */\n", (int)mp_ptr->videoformat); + + fprintf(l_fp, "5 /* color_primaries */\n"); + fprintf(l_fp, "5 /* transfer_characteristics */\n"); + fprintf(l_fp, "5 /* matrix_coefficients */\n"); + + fprintf(l_fp, "%d /* display_horizontal_size */\n", (int)l_width); + fprintf(l_fp, "%d /* display_vertical_size */\n", (int)l_height); + + fprintf(l_fp, "0 /* intra_dc_precision (0: 8 bit, 1: 9 bit, 2: 10 bit, 3: 11 bit */\n"); + fprintf(l_fp, "1 /* top_field_first */\n"); + fprintf(l_fp, "0 0 0 /* frame_pred_frame_dct (I P B) */\n"); + fprintf(l_fp, "0 0 0 /* concealment_motion_vectors (I P B) */\n"); + fprintf(l_fp, "1 1 1 /* q_scale_type (I P B) */\n"); + fprintf(l_fp, "1 0 0 /* intra_vlc_format (I P B)*/\n"); + fprintf(l_fp, "0 0 0 /* alternate_scan (I P B) */\n"); + fprintf(l_fp, "0 /* repeat_first_field */\n"); + fprintf(l_fp, "0 /* progressive_frame */\n"); + fprintf(l_fp, "0 /* P distance between complete intra slice refresh */\n"); + fprintf(l_fp, "0 /* rate control: r (reaction parameter) */\n"); + fprintf(l_fp, "0 /* rate control: avg_act (initial average activity) */\n"); + fprintf(l_fp, "0 /* rate control: Xi (initial I frame global complexity measure) */\n"); + fprintf(l_fp, "0 /* rate control: Xp (initial P frame global complexity measure) */\n"); + fprintf(l_fp, "0 /* rate control: Xb (initial B frame global complexity measure) */\n"); + fprintf(l_fp, "0 /* rate control: d0i (initial I frame virtual buffer fullness) */\n"); + fprintf(l_fp, "0 /* rate control: d0p (initial P frame virtual buffer fullness) */\n"); + fprintf(l_fp, "0 /* rate control: d0b (initial B frame virtual buffer fullness) */\n"); + fprintf(l_fp, "2 2 11 11 /* P: forw_hor_f_code forw_vert_f_code search_width/height */\n"); + fprintf(l_fp, "1 1 3 3 /* B1: forw_hor_f_code forw_vert_f_code search_width/height */\n"); + fprintf(l_fp, "1 1 7 7 /* B1: back_hor_f_code back_vert_f_code search_width/height */\n"); + fprintf(l_fp, "1 1 7 7 /* B2: forw_hor_f_code forw_vert_f_code search_width/height */\n"); + fprintf(l_fp, "1 1 3 3 /* B2: back_hor_f_code back_vert_f_code search_width/height */\n"); + + + fclose(l_fp); + + /* generate a startscript */ + + l_fp = fopen(mp_ptr->startscript, "w"); + if(l_fp == NULL) + { + fprintf(stderr, "cant open Startscript %s for write\n", mp_ptr->startscript); + return -1; + } + + fprintf(l_fp, "#!/bin/sh\n"); + fprintf(l_fp, "mpeg2encode %s %s\n", mp_ptr->parfile, mp_ptr->outfile); + fprintf(l_fp, "echo 'mpeg2encode done.'\n"); + fprintf(l_fp, "read DUMMY\n"); + fclose(l_fp); + + + return 0; +} + +/* ============================================================================ + * p_mpeg_encode_gen_parfile + * ============================================================================ + */ + +static +int p_mpeg_encode_gen_parfile(t_anim_info *ainfo_ptr, t_mpg_par *mp_ptr) +{ + FILE *l_fp; + int l_idx; + gint l_width; + gint l_height; + char *l_dirname_ptr; + char *l_basename_ptr; + char l_basename_buff[1024]; + char *l_base_file_format; + + l_fp = fopen(mp_ptr->parfile, "w"); + if(l_fp == NULL) + { + fprintf(stderr, "cant open MPEG Paramfile %s for write\n", mp_ptr->parfile); + return -1; + } + + l_base_file_format = p_mpege_extension_check(ainfo_ptr); + + /* check if ainfo_ptr->basename contains directory part */ + strcpy(l_basename_buff, ainfo_ptr->basename); + l_dirname_ptr = "."; + l_basename_ptr = &l_basename_buff[0]; + + for(l_idx = strlen(l_basename_buff) -1; l_idx >= 0; l_idx--) + { + if(l_basename_buff[l_idx] == G_DIR_SEPARATOR) + { + l_basename_buff[l_idx] = '\0'; + l_basename_ptr = &l_basename_buff[l_idx +1]; + l_dirname_ptr = &l_basename_buff[0]; + break; + } + } + + /* get info about the image (size is common to all frames) */ + l_width = gimp_image_width(ainfo_ptr->image_id); + l_height = gimp_image_height(ainfo_ptr->image_id); + + fprintf(l_fp, "# MPEG_ENCODE Parameterfile (generated by GIMP Plugin gap_mpege)\n"); + fprintf(l_fp, "#\n"); + fprintf(l_fp, "# any line beginning with # is a comment\n"); + fprintf(l_fp, "#\n"); + fprintf(l_fp, "# no line should be longer than 255 characters\n"); + fprintf(l_fp, "#\n"); + fprintf(l_fp, "#\n"); + fprintf(l_fp, "# general format of each line is:\n"); + fprintf(l_fp, "#