[gimp-gap] new plug-ins to render water and fire animations. fixes in filtermacro processing
- From: Wolfgang Hofer <wolfgangh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp-gap] new plug-ins to render water and fire animations. fixes in filtermacro processing
- Date: Thu, 24 Feb 2011 19:35:47 +0000 (UTC)
commit e84fa467484420e6e72a0bb457c81bb3246d3746
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date: Thu Feb 24 20:33:54 2011 +0100
new plug-ins to render water and fire animations. fixes in filtermacro processing
ChangeLog | 76 +
NEWS | 61 +-
docs/reference/txt/Makefile.am | 3 +
docs/reference/txt/plug-in-filter-macro.txt | 51 +-
docs/reference/txt/plug-in-firepattern.txt | 247 +++
docs/reference/txt/plug-in-waterpattern.txt | 111 ++
gap/Makefile.am | 16 +
gap/gap_filter_iterators.c | 107 +-
gap/gap_filter_pdb.c | 50 +-
gap/gap_fire_pattern.c | 2733 +++++++++++++++++++++++++++
gap/gap_fmac_base.c | 159 ++-
gap/gap_layer_copy.c | 2 +-
gap/gap_pdb_calls.c | 159 ++-
gap/gap_pdb_calls.h | 16 +-
gap/gap_story_file.c | 7 +-
gap/gap_story_render_processor.c | 28 +-
gap/gap_water_pattern.c | 1837 ++++++++++++++++++
gap/gimplastvaldesc.h | 12 +-
po/POTFILES.in | 2 +
19 files changed, 5512 insertions(+), 165 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 57da4c7..d744457 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,79 @@
+2011-02-24 Wolfgang Hofer <hof gimp org>
+
+- added new plug-ins to render an animated water pattern and flame effect,
+ based on the algorithms
+ by William Morrison (his original wave tank scripts sf-will-wavetank.scm
+ and sf-will-flames.scm can be found in the plugin registry)
+ The GAP variant supports the GIMP-GAP typical iterator interface that
+ allows to apply the same effect on already existing multilayer images or frames.
+
+- fixed a bug in storyboard processor (processing failed in case
+ the processed layer_id was changed during filtermacro processing)
+
+- fixed bug in handling of persistent drawable ids.
+ Filtermacros that call filters with additional drawable references
+ (such as the plug-in-bump-map that uses the bumpmap DRAWABLE
+ in addition to the input drawable) used a flattened copy of the
+ image that contained the refered additional drawable (e.g. the bumpmap)
+
+- filtermacro processing now can handle the case when one of the called
+ filters makes the processed layer invalid.
+ (this is typicall the case after merge operations
+ where the merged reult gets a new drawable id)
+ The implemented new behavior checks for valid layer
+ after each single filtercall. In case the processed layer
+ gets invalid it tries to find a newly created layer_id,
+ preferrable at same stackposition. If such a newly created layer
+ is found the next filtercall (in the list of filters in the macro)
+ will be executed on this newly created layer.
+
+- GIMP-GAP typical animated filtercalls now provide
+ information about total_steps and current step of the animated call
+ intended for query by the called plug-in where necessary.
+ (The new firepatern plug-in makes use of this feature
+ for varying colors when 2 gradients are used in aminated calls)
+
+ This information is available during the call of the iterator procedure
+ and during the call of the filter via gimp_get_data
+ code example:
+
+ // typedef struct
+ // {
+ // gboolean animatedCallInProgress;
+ // gint32 total_steps;
+ // gdouble current_step; /* current step respecting acceleration characteristics */
+ // } GapLastvalAnimatedCallInfo;
+
+ #include "gimplastvalsdesc.h"
+ GapLastvalAnimatedCallInfo animCallInfo;
+ gimp_get_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO, &animCallInfo);
+
+ * NEWS
+ * docs/reference/txt/Makefile.am
+ * docs/reference/txt/plug_in_filter_macro.txt
+ * docs/reference/txt/plug-in-firepattern.txt # new file
+ * docs/reference/txt/plug-in-waterpattern.txt # new file
+
+ * po/POTFILES.in
+ * gap/gap_filter_iterators.c
+ * gap/gap_story_render_processor.c
+ * gap/gimplastvaldesc.h
+ * gap/Makefile.am
+ * gap/gap_fmac_base.c
+ * gap/gap_filter_pdb.c
+ * gap/gap_pdb_calls.c [.h]
+ * gap/gap_layer_copy.c
+ * gap/gap_fire_pattern.c # new file
+ * gap/gap_water_pattern.c # new file
+
+
+2010-12-18 Wolfgang Hofer <hof gimp org>
+
+- storyboard rounding at offest calcultion
+
+ * gap/gap_story_file.c
+
+
2010-12-14 Wolfgang Hofer <hof gimp org>
- GIMP-GAP has swichted its main video encoder/decoder engine
diff --git a/NEWS b/NEWS
index 89e868c..e7f324d 100644
--- a/NEWS
+++ b/NEWS
@@ -2,36 +2,37 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
-----------------------------------------------------
(compared to gimp-gap release 2.6.0)
-- fixed a bug in the GAP ffmpeg encoder that resulted in lower quality
- of the encoded video.
- (disabled buggy pre conversion to yuv420P colormodel for the ffmpeg encoder.
- now feeding RGB to the codec or convert with functions of the encoding engine)
+- New Plug-Ins added:
+
+ - plug-ins to render animated Water and Fire effects.
+
+ - New colormask plug-in added
+ The colormask filter can apply transparency for pixels matching the colormask image.
+ intended for processing frames where moving objects can be isolated
+ from the non-moving constant background by aplying the constant background
+ as colormask on all frames.
+ the colormask feature is also available as new mask anchor mode
+ when processing stroyboard clips.
+ (anchor modes are: ClipColormask, Clip, Master)
+
+ - support to run gimp_color_balance tool as animated filter
+ (added wrapper plug-in).
+
- GIMP-GAP now supports speed control of movements and other transitions
- via Acceleation characteristic presets. Those presets are available
+ via Acceleration characteristic presets. Those presets are available
- in the MovePath tool
- in Storyboard transitions
and Storyboard filtemacro pair calls when applied with varying values.
- in animated filter calls with varying values.
relevant in Filter All Layers
- and Modify Drames feature when applying a filter
+ and Modify Frames feature when applying a filter
Th GAP dbbrowser now supports acceleration characteristic graph and spinbutton
(that replaces the "Apply Varying" button of older GIMP_GAP releases)
-- New colormask plug-in added
- The colormask filter can apply transparency for pixels matching the colormask image.
- intended for processing frames where moving objects can be isolated
- from the non-moving constant background by aplying the constant background
- as colormask on all frames.
- the colormask feature is also available as new mask anchor mode
- when processing stroyboard clips.
- (anchor modes are: ClipColormask, Clip, Master)
-- The Storyboard now supports rotatation of the prcessed clips by any angle.
- bugfixes in the storyboard processing now use less memory resources
- and produces smoother movent.
- (the old code did run out of memory when processing multiple HD videoclips)
+- The Storyboard now supports rotatation of the processed clips by any angle.
- A new storyboard processing feature allows adding external transparency
for clip type movie. This is done via format string that refers to
@@ -54,12 +55,7 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
individual workpointfiles per processed frame are located via
name convention (extension .morphpoints) and used for morphing when they exist.
(such files can be created with the help of the plug-in "Morph Workpoint Generator").
-/home/hof/gimp_devel/gap/gimp-gap-ffmpeg/master-patched/NEWS
-- support to run gimp_color_balance tool as animated filter
- (added wrapper plug-in).
-- significant better performance for ffmpeg based video encoder
- on single and multiprocessor machines.
- updated gimp-gap video API and ffmpeg-based video encoder
to support the libraries provided with the ffmpeg-0.6.1 release
@@ -68,8 +64,25 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
in videofiles that do not start with a keyframe).
.. see ChangeLog for details,
+
+- better performance for ffmpeg based video encoder
+ on single and multiprocessor machines.
+
-- some bugfixes (See ChangeLog for details)
+- Major Bugfixes
+
+ - fixed a bug in the GAP ffmpeg video encoder that resulted in lower quality
+ of the encoded video.
+ (disabled buggy pre conversion to yuv420P colormodel for the ffmpeg encoder.
+ now feeding RGB to the codec or convert with functions of the encoding engine)
+
+ - fixed video encoder crash on 64bit Systems.
+
+ - bugfixes in the storyboard processing now use less memory resources
+ and produces smoother movent.
+ (the old code did run out of memory when processing multiple HD videoclips)
+
+ (See ChangeLog for details on further bugfixes)
Here is a short overview whats new in GIMP-GAP-2.6.0:
diff --git a/docs/reference/txt/Makefile.am b/docs/reference/txt/Makefile.am
index dc0e987..553987a 100644
--- a/docs/reference/txt/Makefile.am
+++ b/docs/reference/txt/Makefile.am
@@ -6,7 +6,9 @@ EXTRA_DIST = \
gap-filterall-db-browser.txt \
gap_gimprc_params.txt \
plug-in-bluebox.txt \
+ plug-in-plug-in-colormask.txt.txt \
plug-in-filter-macro.txt \
+ plug-in-firepattern.txt \
plug-in-gap-anim-crop.txt \
plug-in-gap-anim-resize.txt \
plug-in-gap-anim-scale.txt \
@@ -42,6 +44,7 @@ EXTRA_DIST = \
plug-in-gap-videoframes-player.txt \
plug-in-gap-xanim-decode.txt \
plug-in-name2layer.txt \
+ plug-in-waterpattern.txt \
plug-in-wr-color-levels.txt \
plug-in-wr-curves.txt \
plug-in-wr-huesat.txt
diff --git a/docs/reference/txt/plug-in-filter-macro.txt b/docs/reference/txt/plug-in-filter-macro.txt
index 4d5b3ef..ab35e8f 100644
--- a/docs/reference/txt/plug-in-filter-macro.txt
+++ b/docs/reference/txt/plug-in-filter-macro.txt
@@ -44,12 +44,57 @@ Filtermacro Script:
was invoked from)
+ Recording of a filtermacro will automatically create an additional
+ filtermacro reference file in case one of the recorded filters
+ has references to additional drawables (e.g. layers).
+ One such filter example is the plug_in_bump_map that uses the bumpmap DRAWABLE
+ in addition to the processed input drawable.
+
+
+ A filtermacro reference file has the same name as the filtermacro scriptfile
+ with extension .fmref
+ This file records filename, type and position of such additional drawables
+ to allow applying the filtermacro in another gimp session.
+
+ Currently supported types are:
+ - 3: GAP_AINFO_ANIMIMAGE layer in a multilayer image,
+ - 4: GAP_AINFO_FRAMES: frame (flattened representation in a sequence of numbered images)
+ - 5: GAP_AINFO_MOVIE: and movie (frame extracted from a videofile)
+
+ Where the type is automatically detected at recording like this:
+
+ - In case the drawable is the layer of an image that was extracted from a videofile
+ within the same GIMP-session
+ (by click on the GAP Playback dialog preview)
+ the type 5: GAP_AINFO_MOVIE is recorded.
+ Such layers are marked (with a non-persitent layer parasite)
+ at extraction time to provide videofilename and position in the video.
+
+ - In case the drawable is a layer in an image with GIMP-GAP typical number part
+ and another frame image with next or previous number is already existent
+ the type 4: GAP_AINFO_FRAMES is assumed.
+
+ - All other cases use type 3: GAP_AINFO_ANIMIMAGE .
+
+ Restrictions:
+ -------------
+ - Note that persistent drawable references will NOT work or give unexpected
+ results in case the image that contains the refered drawable
+ was not saved, or was changed and saved after the filtermacro was recored.
+ - Furthermore the filter must use the GIMP-GAP standard iterator implementation
+ that supports the recording of persistent drawable id's for its additional
+ drawable parameters.
+
+
+
Tip:
Filtermacro execution can be used in the same way as a single
filtercall, together with the
'Filter All Layers' and the
- 'Frames Modify'
+ 'Frames Modify
features of gimp-gap. This way you can apply a set of filtercalls
on all layers of a multilyer image
- (or on all selected layers in multiple frames)
- at once
+ (or on all selected layers in multiple frames) with one call.
+
+ Filtermacro file execution is also available in Storyboard scripts.
+ See STORYBOARD_FILE_DOC.txt chapter Macrofiles for more details.
diff --git a/docs/reference/txt/plug-in-firepattern.txt b/docs/reference/txt/plug-in-firepattern.txt
new file mode 100644
index 0000000..0866dc7
--- /dev/null
+++ b/docs/reference/txt/plug-in-firepattern.txt
@@ -0,0 +1,247 @@
+"plug-in-firepattern"
+
+Fire Pattern Filter:
+
+ Start from Menu:
+
+ "<Image>/Video/Layer/Render/Fire Pattern..."
+
+ This Plugin generates an animated fire effect.
+ It can render the animation in one call, where the input drawable is copied n-times to a newly created image.
+ Optional this Plugin can be called to render just one frame/phase of the animated effect.
+ This type of animated call on multiple already existent layers (or frames) with varying shiftPhase parameter
+ is supported by the GIMP-GAP filter all layers or by applying as filter via GIMP-GAP modify frames feature.
+ Note that the render one frame per call style offers more flexibility where you can
+ apply the flame with varying shape, color and opacity for each rendered frame atomatically.
+
+ Note that this Plugin will create an additional image when the options
+ for creating fire pattern or fire shape are selected.
+ If you intend to record the Plugin using GIMP-GAP filtermacro feature
+ or want to be able to reproduce the exactly same same results in another GIMP session
+ You have to save this image to disc (using the XCF file format)
+
+ Animation options:
+
+ Create Image: (checkbutton)
+
+ ON: create a new image with n copies of the input drawable
+ and render complete animation effect on those copies.
+ OFF: render only one frame/phase of the animation effect on
+ the input drawable.
+ (This type of call must be selected in case you call this plug-in
+ via the GIMP-GAP filter all layers feature)
+
+
+ N-Frames: (spinbutton)
+ Number of frames to be rendered as layer in the newly created image.
+ In this mode the vertical shift per frame is calculated automatically
+ for each frame varying from 0.0 to Phase Shift value.
+ Select 1.0 (or multiples of 1.0) to create an animation
+ that can be played in seamless loop.
+ (disabled in case Create Image: OFF is selected)
+
+ Phase shift: (spinbutton)
+ Vertical phase shift (movement of the fire pattern)
+ where 1.0 refers to image height.
+
+ In animated calls via (filter all layers) it is recommanded to
+ set Phase shift to value 0.0 for the first and to value 1.0
+ for the last processed frame to create a seamless animation effect
+ when playback of the rendered frame is done in a loop.
+
+
+
+ Pattern options:
+ Widgets to control the fire pattern cloud layer that is used as base
+ for rendering the animated fire effect.
+
+ Create Pattern: (checkbutton)
+ ON: create firepattern cloud layer with tileable solid noise
+ according to options.
+ (Creating a new pattern should be osed only in case
+ rendering the first frame of an animation sequence.
+ for each further frame it is recomannded to turn this option OFF)
+ OFF: Use external pattern layer.
+ Typically you may select a fire pattern layer that was created
+ for rendering the first frame of the animation.
+
+ Stretch Height (spinbutton)
+ vertical stretch factor for the fire pattern.
+
+ Scale Pattern X/Y: (2 spinbuttons)
+ Horizontal/Vertical scaling of the random pattern that is
+ created for rendering the fire pattern (cloud layer)
+
+ Seed Pattern: (spinbutton)
+ Seed for creating random pattern (cloud1 layer)
+ use 0 for random value.
+
+ Detail: (spinbutton)
+ Detail level for creating random solid noise pattern (cloud layer)
+
+
+ Layer Pattern: (combobox)
+ Select an already existing pattern layer
+ (typically from previous run, when rendering in one frame per call mode)
+
+
+ Fireshape options:
+ Widgets to control the fire shape layer that is used to control the affected
+ area where to render the flames.
+
+ Create Fireshape: (checkbutton)
+ ON: create fire shape layer according to options.
+ OFF: Use external fire shape layer.
+
+ Trapezoid: (checkbutton)
+ ON: Render trapezoid shaped fire.
+ OFF: render fire at full image width.
+
+ Flame Height: (spinbutton)
+ Height of the flame (1.0 refers to full image height)
+
+ Flame Border: (spinbutton)
+ border of the flame (used for horizontally soft fade of the shape at the borders)
+
+ FlameWidth: (2 spinbuttons)
+ width of the flame at base line and
+ width of the flame at flame height
+ (1.0 for full image width)
+
+ Flame Center: (spinbutton)
+ horizontal offset of the flame center
+ (0 for center, -0.5 left border +0.5 at right border of the image)
+
+ Fire Shape:
+ Select an already existing fire shape layer
+ (typically from previous run, when rendering in one frame per call mode)
+
+ Render options:
+
+ Create FireLayer: (checkbutton)
+ ON: Render fire pattern effect as separate layer.
+ OFF: merge rendered effect onto processed layer.
+ (it is recommanded to set the Transparent BG
+ to ON too)
+
+ (In case this filter is called via the filter all layers
+ feature it is recommanded to turn this option OFF)
+
+ Blend Mode: (radio buttons)
+ Selects the blend mode to be used to combine the fire pattern (cloud)
+ layer with the fire shape layer. Follwing modes are available:
+ "Burn"
+ "Subtract"
+ "Multiply"
+
+ Transparent BG: (checkbutton)
+ ON: Render fire layer with transparent background.
+ OFF: render with black background.
+
+ Opacity: (spinbutton)
+ The opacity of the rendered flames.
+
+ Reverse Gradient: (checkbutton)
+ ON: use reverse gradient colors.
+ OFF: use gradient colors.
+
+ Gradient: (gradient button)
+ Select the gradient that should be used to colorize the rendered flames.
+
+
+ Action Buttons:
+
+ Reset
+ Reset all parameters to default values.
+
+ Cancel
+ Close the window without any further action.
+
+ OK
+ Close the window and render the fire effect accoring to the
+ selected options.
+
+
+
+ Usage Examples:
+
+ Rendering fire with varying colors:
+ For this example we use a multilayer image where each layer
+ represents one frame of an animation.
+ (in case this multilayer image was loaded from an animated GIF
+ the mode has to be changed from Indexed to RGB before we continue)
+
+ From this multilayer image start the menu Filter/filter all Layers.
+ This opens a browser dialog window where the plug-in-firepattern
+ can be selected in the list of available filters.
+ Set acceleration characteristcs to value 1 for linear varying values
+ with constant speed and press the APPLY button in the browser dialog.
+
+ This starts the Fire-Pattern dialog window of the selected plug-in-firepattern
+ where you can specifiy the options to be applied for processing the first
+ layer of the multilayer image.
+
+ 1. st dialog step
+
+ Press the reset button to init all options with default values.
+ Make sure to set Phase Shift to value 0 and
+ Also both the Create Pattern and the Create Fireshape checkbuttons
+ shall be turned on.
+
+ Press OK in the Fire Pattern dialog. This renders the fire on the
+ first (e.g. the background) layer of your multilayer image.
+
+ After rendering a dialog window with title "Animated Filter Aplly"
+ pops up.
+ Click the "Continue" button in this dialog.
+
+ 2. nd dialog step
+
+ The Fire-Pattern diaolg window appears again where you can enter
+ the options for the last layer to be processed (the top layer
+ of your multilayer image)
+
+ Uncheck the "Create Fireshape" checkbutton to use the same
+ Fire Shape in all further frames.
+
+ Enter the value 1.0 in the Phase shift spinbutton entry widget.
+ Click on the gradient and select another gradient name in
+ the dialog that opens on this click
+
+ Press OK in the Fire Pattern dialog. This renders the fire on the
+ last (e.g. the top) layer of your multilayer image.
+
+ After rendering a dialog window with title "Animated Filter Aplly"
+ pops up.
+ Click the "Continue" button in this dialog.
+
+ This triggers rendering the fire effect for all further layers
+ (between bg and top) of your multilayer image.
+
+
+ Rendering fire with varying external shape:
+ In case you want render the fire with another shape (than the built in trapezoid)
+ you can provide the fire shape layer in another image.
+
+ For varying external shape this other image should be a multilayer image
+ where each layer represents another step of the animated fire outline shape.
+ (typically white at base fading to black until the desired flame height)
+ It is recommanded to use the same number and size for the "external"
+ fireshape multilayer image and for the multilayer image where the
+ fire effect shall be rendered onto.
+
+
+
+
+
+ How it works:
+
+ This filter generates a fire pattern (cloud) layer with tileable solid noise, and a fire shape layer.
+ The fire effect is made by placing the fire patteren above the fire shape layer using "Burn" (or "subtract")
+ combination mode. The animation is done by vertically shifting the fire pattern upwards slightly
+ on each frame.
+ The flames are built by merging the fire pattern at its shifted state with the fire shape.
+ The resulting (gray flame) layer is colorized with the colors of the selcted gradient.
+ The mapping of the colors (and optional opacity) is done by luminance.
+
+
diff --git a/docs/reference/txt/plug-in-waterpattern.txt b/docs/reference/txt/plug-in-waterpattern.txt
new file mode 100644
index 0000000..9acd933
--- /dev/null
+++ b/docs/reference/txt/plug-in-waterpattern.txt
@@ -0,0 +1,111 @@
+"plug-in-waterpattern"
+
+Water Pattern Filter:
+
+ Start from Menu:
+
+ "<Image>/Video/Layer/Render/Water Pattern..."
+
+
+ This plug-in generates an animated water effect that looks like the bottom of a ripple tank.
+ It can render the animation in one call,
+ where the input drawable is copied n-times to a newly created image
+ and renders the water pattern with slightly shifted on each copy.
+ (in this case the amount of phase shifting
+
+
+ Note that this Plugin will create an additional image when the option
+ for creating patterns is selected.
+ If you intend to record the Plugin using GIMP-GAP filtermacro feature
+ or want to be able to reproduce the exactly same same results in another GIMP session
+ You have to save this image to disc (using the XCF file format)
+
+ Animation options:
+
+ Create Image: (checkbutton)
+
+ ON: create a new image with n copies of the input drawable
+ and render complete animation effect on those copies.
+ OFF: render only one frame/phase of the animation effect on
+ the input drawable.
+ (This type of call must be selected in case you call this plug-in
+ via the GIMP-GAP filter all layers feature)
+
+ N-Frames: (spinbutton)
+ Number of frames to be rendered as layer in the newly created image.
+ In this mode the vertical and horizontal shift value
+ per frame is calculated automatically
+ for each frame varying from 0.0 to Phase Shift X (Y) value.
+ Select 1.0 (or multiples of 1.0) to create an animation
+ that can be played in seamless loop.
+ (disabled in case Create Image: OFF is selected)
+
+ Phase shift X / Y: (2 spinbuttons)
+ Horizontal (Vertical) shift phase where 1.0 refers to image width (height)
+
+ Pattern options:
+ Widgets to control the water pattern cloud layers that are used as base
+ for rendering the animated water effect.
+
+ Create Pattern: (checkbutton)
+ ON: create waterpattern cloud layers according to pattern options.
+ OFF: Use (already existing) external pattern layers.
+
+
+ Layer Pattern 1 / 2:
+ Select an already existing pattern layer (from previous run)
+
+ Scale Pattern X/Y: (2 spinbuttons)
+ Horizontal/Vertical scaling of the random patterns that is
+ created for rendering the water highlights and displacement effects
+ (cloud layer1 and 2)
+
+ Seed Pattern 1 / 2: (2 spinbuttons)
+ Seed values for creating random patterns (cloud1 and cloud2 layers)
+ use 0 for random value.
+
+ Render options:
+
+ Use Highlights: (checkbutton)
+ ON: Render water pattern highlight effect
+ OFF: Disable higlight effect.
+
+ Opacity: (spinbutton)
+ The highlight strength (e.g. opacity)
+
+ Blend Mode: (radio buttons)
+ Selects the blend mode to be used to combine the patterns (cloud1 and cloud2 layers)
+ Follwing modes are available:
+ "Overlay"
+ "Addition"
+ "Screen"
+ "Dodge"
+
+ Use Displace Map: (checkbutton)
+ ON: Render water pattern distortion effect by applying a displace map
+ that is genarated by combining both patterns (cload1 and cloud2 layer)
+ OFF: Disable distortion rendering.
+
+ Displace Strength: (spinbutton)
+ The distortion displace strength.
+ Note that values grater than 0.02 results in heavy
+ distrotions (that does not look naturally).
+
+
+
+ How it works:
+
+ This filter generates two layers with tileable solid noise,
+ and sets the top layer to difference mode. This creates dark "bands" where the values of each layer cross.
+ This result is normalized and has the curves filter applied to bring out the bands in white instead of black.
+ This result is blended over the base layer (the one that was active when the script is called)
+ with the chosen blend mode.
+ With create Image option swithced ON this entire procedure is repeated for each frame,
+ with slight offsets of the solid noise (e.g cloud 1 & 2 ) layers, so the bands are shifted slightly each time.
+ The offsets are the size of the image multiplied by phase value and divided by the number of frames.
+ In case phase value 1 (or multiples of 1) is used at the end (processing of last layer)
+ the solid noise layers are back where they started, and the entire animation loops seamlessly.
+ Note that phase values control control the speed of the animation effect where higher values
+ result in faster speed.
+
+
diff --git a/gap/Makefile.am b/gap/Makefile.am
index fd13cbc..4eedebb 100644
--- a/gap/Makefile.am
+++ b/gap/Makefile.am
@@ -121,6 +121,8 @@ libexec_PROGRAMS = \
gap_storyboard \
$(GAP_VIDEO_EXTRACT) \
$(GAP_VIDEO_INDEX) \
+ gap_fire_pattern \
+ gap_water_pattern \
gap_wr_color_curve \
gap_wr_color_levels \
gap_wr_color_huesat \
@@ -366,6 +368,18 @@ gap_video_index_SOURCES = \
gap_libgapstory.h \
gap_libgimpgap.h
+gap_fire_pattern_SOURCES = \
+ gap_lastvaldesc.c \
+ gap_lastvaldesc.h \
+ gap_fire_pattern.c \
+ gap_libgimpgap.h
+
+gap_water_pattern_SOURCES = \
+ gap_lastvaldesc.c \
+ gap_lastvaldesc.h \
+ gap_water_pattern.c \
+ gap_libgimpgap.h
+
gap_wr_opacity_SOURCES = \
gap_lastvaldesc.c \
gap_lastvaldesc.h \
@@ -439,6 +453,8 @@ gap_onion_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_storyboard_LDADD = $(GAPVIDEOAPI) $(WAVPLAYCLIENT) ${LIBGAPSTORY} $(LIBGAPBASE) $(GIMP_LIBS)
gap_video_extract_LDADD = $(GAPVIDEOAPI) $(WAVPLAYCLIENT) ${LIBGAPSTORY} $(LIBGAPBASE) $(GIMP_LIBS)
gap_video_index_LDADD = $(GAPVIDEOAPI) $(LIBGAPSTORY) $(LIBGAPBASE) $(GIMP_LIBS)
+gap_fire_pattern_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
+gap_water_pattern_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_wr_opacity_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_wr_trans_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_wr_color_curve_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
diff --git a/gap/gap_filter_iterators.c b/gap/gap_filter_iterators.c
index 7c591ef..7b5cdc4 100644
--- a/gap/gap_filter_iterators.c
+++ b/gap/gap_filter_iterators.c
@@ -434,83 +434,6 @@ p_delta_drawable(gint32 *val, gint32 val_from, gint32 val_to, gint32 total_steps
} /* end p_delta_drawable */
-/* ------------------------------------
- * p_drawable_is_alive
- * ------------------------------------
- * current implementation checks only for layers and layermasks.
- * TODO check other drawable types such as channels ....
- *
- * return TRUE if OK (drawable is still valid)
- * return FALSE if drawable is NOT valid
- */
-gboolean
-p_drawable_is_alive(gint32 drawable_id)
-{
- gint32 *images;
- gint nimages;
- gint l_idi;
- gboolean l_found;
-
- if(drawable_id < 0)
- {
- return FALSE;
- }
-
- images = gimp_image_list(&nimages);
- l_idi = nimages -1;
- l_found = FALSE;
- while((l_idi >= 0) && images)
- {
- gint l_nlayers;
- gint32 *l_layers_list;
-
- l_layers_list = gimp_image_get_layers(images[l_idi], &l_nlayers);
- if(l_layers_list != NULL)
- {
- gint l_idx;
- l_idx = l_nlayers;
- for(l_idx = 0; l_idx < l_nlayers; l_idx++)
- {
- gint32 l_layer_id;
- gint32 l_layermask_id;
-
- l_layer_id = l_layers_list[0];
- if (l_layer_id == drawable_id)
- {
- l_found = TRUE;
- break;
- }
- l_layermask_id = gimp_layer_get_mask(l_layer_id);
- if (l_layermask_id == drawable_id)
- {
- l_found = TRUE;
- break;
- }
- }
- g_free (l_layers_list);
- }
-
- if (l_found == TRUE)
- {
- break;
- }
- l_idi--;
- }
-
- if(images) g_free(images);
- if(l_found)
- {
- return TRUE; /* OK */
- }
-
- if(gap_debug)
- {
- printf("p_drawable_is_alive: drawable_id %d is not VALID\n", (int)drawable_id);
- }
-
- return FALSE ; /* INVALID image id */
-} /* end p_drawable_is_alive */
-
/* ---------------------------
* p_delta_drawable_simple
@@ -538,11 +461,11 @@ p_delta_drawable_simple(gint32 *val, gint32 val_from, gint32 val_to, gint32 tota
{
return;
}
- if(p_drawable_is_alive(val_from) != TRUE)
+ if(gimp_drawable_is_valid(val_from) != TRUE)
{
return;
}
- if(p_drawable_is_alive(val_to) != TRUE)
+ if(gimp_drawable_is_valid(val_to) != TRUE)
{
return;
}
@@ -611,10 +534,10 @@ p_capture_image_name_and_assign_pesistent_id(GapFmacContext *fmacContext, gint32
);
}
- if(p_drawable_is_alive(drawable_id) != TRUE)
+ if(gimp_drawable_is_valid(drawable_id) != TRUE)
{
/* drawable is no longer valid and can not be mapped.
- * This may happen if the layer was removed or the refered image was close
+ * This may happen if the layer was removed or the refered image was closed
* in the time since the plugin has stored the last values buffer
* and the time when filtermacro starts recording filtercalls.
*/
@@ -660,7 +583,27 @@ p_capture_image_name_and_assign_pesistent_id(GapFmacContext *fmacContext, gint32
l_ainfo_ptr = gap_lib_alloc_ainfo(image_id, GIMP_RUN_NONINTERACTIVE);
if(l_ainfo_ptr != NULL)
{
- ainfo_type = l_ainfo_ptr->ainfo_type;
+ /* refere to GAP_AINFO_FRAMES in case the drawable is part of a gimp-gap typical frame image
+ * with a name that ends up in numberpart.extension (something like frame_000006.xcf)
+ * and there is one additional frame in sequence (previous or next number)
+ */
+ if (l_ainfo_ptr->ainfo_type == GAP_AINFO_FRAMES)
+ {
+ gboolean isAnotherFrameExistent;
+ long has_digits;
+
+ isAnotherFrameExistent = gap_lib_exists_frame_nr(l_ainfo_ptr, l_ainfo_ptr->curr_frame_nr +1, &has_digits);
+ if (!isAnotherFrameExistent)
+ {
+ isAnotherFrameExistent = gap_lib_exists_frame_nr(l_ainfo_ptr, l_ainfo_ptr->curr_frame_nr -1, &has_digits);
+ }
+
+ if((isAnotherFrameExistent) && (has_digits > 1))
+ {
+ ainfo_type = GAP_AINFO_FRAMES;
+ }
+
+ }
frame_nr = l_ainfo_ptr->frame_nr;
track = -1; /* video track (not relevant for frames and single images) */
diff --git a/gap/gap_filter_pdb.c b/gap/gap_filter_pdb.c
index 381d8d3..a3617e8 100644
--- a/gap/gap_filter_pdb.c
+++ b/gap/gap_filter_pdb.c
@@ -80,6 +80,34 @@ static char *global_plugin_data = NULL;
static char * p_filt_pdb_get_alternative_iterator_proc(char *plugin_name, gint *count);
static gint p_count_iterable_params(gchar *key_description, gint desc_size);
+static void
+p_setGapLastvalAnimatedCallInfo(gint32 total_steps, gdouble current_step)
+{
+ GapLastvalAnimatedCallInfo animCallInfo;
+
+ animCallInfo.animatedCallInProgress = TRUE;
+ animCallInfo.total_steps = total_steps;
+ animCallInfo.current_step = current_step;
+ gimp_set_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO
+ , &animCallInfo
+ , sizeof(animCallInfo)
+ );
+}
+
+static void
+p_clearGapLastvalAnimatedCallInfo()
+{
+ GapLastvalAnimatedCallInfo animCallInfo;
+
+ animCallInfo.animatedCallInProgress = FALSE;
+ animCallInfo.total_steps = 0;
+ animCallInfo.current_step = 0.0;
+ gimp_set_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO
+ , &animCallInfo
+ , sizeof(animCallInfo)
+ );
+}
+
/* ------------------------
* gap_filt_pdb_call_plugin
* ------------------------
@@ -184,14 +212,23 @@ gap_filt_pdb_call_plugin(char *plugin_name, gint32 image_id, gint32 layer_id, Gi
l_rc = -1;
if (l_ret_params[0].data.d_status != GIMP_PDB_SUCCESS)
{
- printf("ERROR: gap_filt_pdb_call_plugin %s failed.\n", plugin_name);
+ printf("ERROR: gap_filt_pdb_call_plugin %s failed with status:%d\n"
+ , plugin_name
+ ,(int)l_ret_params[0].data.d_status
+ );
}
else
{
- if(gap_debug) printf("DEBUG: gap_filt_pdb_call_plugin: %s successful.\n", plugin_name);
+ if(gap_debug)
+ {
+ printf("DEBUG: gap_filt_pdb_call_plugin: %s successful.\n", plugin_name);
+ }
l_rc = 0; /* OK */
}
gimp_destroy_params(l_ret_params, l_retvals);
+
+ p_clearGapLastvalAnimatedCallInfo();
+
return(l_rc);
} /* end gap_filt_pdb_call_plugin */
@@ -749,6 +786,15 @@ gap_filter_iterator_call(const char *iteratorname
l_rc = TRUE;
+ /* set information about the current processed step of an animated filtercall
+ * This information is provided to be queried from called plugins
+ * so those filters can detect that they are called in animated style
+ * (most plugins do not need this information. one example is the
+ * gap_fire_pattern filter that uses this information to implement
+ * iteration of colorsamples of 2 different gradients)
+ */
+ p_setGapLastvalAnimatedCallInfo(total_steps, current_step);
+
/* call plugin-specific iterator (or the common iterator), to modify
* the plugin's last_values
*/
diff --git a/gap/gap_fire_pattern.c b/gap/gap_fire_pattern.c
new file mode 100644
index 0000000..9caef1e
--- /dev/null
+++ b/gap/gap_fire_pattern.c
@@ -0,0 +1,2733 @@
+/* gap_fire_pattern.c
+ * 2011.02.11 hof (Wolfgang Hofer)
+ *
+ * Fire pattern - This is a filter for The GIMP to generate an animated fire effect.
+ * This implementation was ported from the script sf-will-flames.scm Copyright (C) 2010 William Morrison
+ * to C and was adapted to run on a single layer as animated filter, using GIMP-GAP typical
+ * iterator capabilities for animated apply. This provides the ability to apply the effect on already
+ * existing layers or frames and adds the capability of varying shape and opacity of the flames
+ * for each rendered frame.
+ *
+ */
+/* 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
+ * (2011/02/11) v1.0 hof: - created
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gap-intl.h"
+#include "gap_lastvaldesc.h"
+#include "gap_pdb_calls.h"
+#include "gap_libgapbase.h"
+
+/* Defines */
+#define PLUG_IN_NAME "plug-in-firepattern"
+#define PLUG_IN_IMAGE_TYPES "RGB*, GRAY*"
+#define PLUG_IN_AUTHOR "Wolfgang Hofer (hof gimp org)"
+#define PLUG_IN_COPYRIGHT "Wolfgang Hofer"
+#define PLUG_IN_DESCRIPTION "Renders an animated fire effect as new layer(s) or optional onto the processed layer(s)."
+
+#define PLUG_IN_DATA_ITER_FROM "plug-in-firepattern-ITER-FROM"
+#define PLUG_IN_DATA_ITER_TO "plug-in-firepattern-ITER-TO"
+#define PLUG_IN_HELP_ID "plug-in-firepattern"
+
+#define BLEND_NUM_BURN 0
+#define BLEND_NUM_SUBTRACT 1
+#define BLEND_NUM_MULTIPLY 2
+
+#define N_RET_VALUES 4
+
+#define GAP_FIREPATTERN_RESPONSE_RESET 1
+#define DEFAULT_GRADIENT_NAME "Incandescent"
+#define ACTIVE_GRADIENT_NAME "<ACTIVE_GRADIENT>"
+
+#define N_GRADIENT_COLORSAMPLES 256
+#define BPP_SAMPLE_COLOR 4
+#define LUMINOSITY(X) (GIMP_RGB_LUMINANCE (X[0], X[1], X[2]) + 0.5)
+
+
+
+#define MAX_GRADIENT_NAME 300
+
+typedef struct
+{
+ /* params for creating the cloud layer (the pattern where flaes are base on) */
+ gdouble scalex;
+ gdouble scaley;
+ gint32 detail;
+
+ /* params for creating the fireShapeLayer (trapezoid) */
+
+ gboolean useTrapezoidShape;
+ gdouble flameHeight;
+ gdouble flameWidthBase;
+ gdouble flameWidthTop;
+ gdouble flameBorder;
+ gdouble flameOffestX;
+
+ /* params to control rendering of the layer(s) */
+ gint32 blendNum;
+ gdouble stretchHeight;
+ gdouble shiftPhaseY;
+
+ gboolean createImage;
+ gint32 nframes;
+ gboolean createFireLayer; /* TRUE: keep the newly created rendered fire layer(s) FALSE: merge fire layer with processed layer(s) */
+ gboolean useTransparentBg; /* TRUE derive alpha from luminosity, FALSE derive alpha from gradient */
+ gdouble fireOpacity; /* opacity of the fire layer 0.0 to 100.0 */
+
+ gint32 seed1;
+ gint32 cloudLayer1;
+ gint32 fireShapeLayer;
+ gboolean forceShapeLayer;
+ gboolean reverseGradient;
+ gchar gradientName[MAX_GRADIENT_NAME];
+
+
+} firepattern_val_t;
+
+
+
+
+typedef struct _FirePatternDialog FirePatternDialog;
+
+struct _FirePatternDialog
+{
+ gint run;
+ gint show_progress;
+ gint32 drawable_id;
+
+ gboolean createNewPattern; /* FALSE: use cloud layer entered via dialog */
+ gboolean createNewPatternDefault; /* FALSE: use cloud layer entered via dialog */
+ gboolean createNewShapeDefault; /* FALSE: use cloud layer entered via dialog */
+ gint32 existingCloud1Id;
+ gint32 existingFireShapeLayerId;
+ gint32 countPotentialCloudLayers;
+
+ GtkWidget *shell;
+ GtkWidget *radio_blend_burn;
+ GtkWidget *radio_blend_subtract;
+ GtkWidget *radio_blend_multiply;
+ GtkObject *nframes_spinbutton_adj;
+ GtkWidget *nframes_spinbutton;
+ GtkObject *fireOpacity_spinbutton_adj;
+ GtkWidget *fireOpacity_spinbutton;
+ GtkObject *stretchHeight_spinbutton_adj;
+ GtkWidget *stretchHeight_spinbutton;
+
+ GtkObject *flameHeight_spinbutton_adj;
+ GtkWidget *flameHeight_spinbutton;
+ GtkObject *flameWidthBase_spinbutton_adj;
+ GtkWidget *flameWidthBase_spinbutton;
+ GtkObject *flameWidthTop_spinbutton_adj;
+ GtkWidget *flameWidthTop_spinbutton;
+ GtkObject *flameBorder_spinbutton_adj;
+ GtkWidget *flameBorder_spinbutton;
+ GtkObject *flameOffestX_spinbutton_adj;
+ GtkWidget *flameOffestX_spinbutton;
+
+ GtkObject *shiftPhaseY_spinbutton_adj;
+ GtkWidget *shiftPhaseY_spinbutton;
+ GtkObject *scaleX_spinbutton_adj;
+ GtkWidget *scaleX_spinbutton;
+ GtkObject *scaleY_spinbutton_adj;
+ GtkWidget *scaleY_spinbutton;
+ GtkObject *detail_spinbutton_adj;
+ GtkWidget *detail_spinbutton;
+ GtkObject *seed1_spinbutton_adj;
+ GtkWidget *seed1_spinbutton;
+
+ GtkWidget *cloud1Combo;
+ GtkWidget *fireShapeCombo;
+ GtkWidget *cloud1Label;
+ GtkWidget *fireShapeLabel;
+ GtkWidget *patternCheckbutton;
+ GtkWidget *patternLabel;
+ GtkWidget *shapeCheckbutton;
+ GtkWidget *shapeLabel;
+ GtkWidget *createImageCheckbutton;
+ GtkWidget *createFireLayerCheckbutton;
+ GtkWidget *useTransparentBgCheckbutton;
+ GtkWidget *reverseGradientCheckbutton;
+ GtkWidget *useTrapezoidShapeCheckbutton;
+
+ GtkWidget *gradient_select;
+
+ firepattern_val_t *vals;
+};
+
+
+typedef struct
+{
+ GimpRunMode run_mode;
+ gint32 image_id;
+ gint32 ref_image_id;
+
+ gint32 width;
+ gint32 height;
+ gint32 strechedHeight;
+ GimpLayerModeEffects blend_mode;
+ gboolean add_display_to_ref_image;
+
+ guchar *byte_samples; /* holds 256 colorsamples (bpp=4) from the selected gradient */
+
+ gdouble flameOffestXInPixels;
+ gdouble flameBorderInPixels;
+ gdouble flameHeightInPixels;
+ gdouble flameWidthTopInPixels;
+ gdouble flameWidthBaseInPixels;
+
+ gboolean forceFireShapeLayerCreationPerFrame;
+} firepattern_context_t;
+
+
+typedef struct
+{
+ guchar *samples;
+ gboolean is_rgb;
+ gboolean has_alpha;
+ gboolean useTransparentBg;
+} MapParam;
+
+
+
+
+static void p_init_default_cuvals(firepattern_val_t *cuvals);
+static void p_check_for_valid_cloud_layers(firepattern_val_t *cuvals);
+static void p_init_cuvals(firepattern_val_t *cuvals);
+
+static FirePatternDialog *do_dialog (FirePatternDialog *wcd, firepattern_val_t *);
+static void query (void);
+static void run(const gchar *name
+ , gint nparams
+ , const GimpParam *param
+ , gint *nreturn_vals
+ , GimpParam **return_vals);
+
+/* Global Variables */
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+
+gint gap_debug = 0; /* 0.. no debug, 1 .. print debug messages */
+
+/* --------------
+ * procedures
+ * --------------
+ */
+
+/* -----------------------------------------
+ * p_init_default_cuvals
+ * p_init_default_cuvals_without_cloud_layers
+ * -----------------------------------------
+ *
+ */
+static void
+p_init_default_cuvals_without_cloud_layers(firepattern_val_t *cuvals)
+{
+ cuvals->scalex = 16.0;
+ cuvals->scaley = 7.0;
+ cuvals->detail = 2;
+ cuvals->useTrapezoidShape = 1;
+ cuvals->flameHeight = 1.0;
+ cuvals->flameWidthBase = 0.7;
+ cuvals->flameWidthTop = 0.5;
+ cuvals->flameBorder = 0.2;
+ cuvals->flameOffestX = 0.0;
+ cuvals->blendNum = 0;
+
+ cuvals->stretchHeight = 2.0;
+ cuvals->shiftPhaseY = 0.0;
+
+ cuvals->createImage = FALSE;
+ cuvals->nframes = 50;
+ cuvals->createFireLayer = FALSE;
+ cuvals->useTransparentBg = TRUE;
+ cuvals->fireOpacity = 90.0;
+ cuvals->seed1 = 0;
+ cuvals->reverseGradient = FALSE;
+ g_snprintf (cuvals->gradientName, MAX_GRADIENT_NAME -1, "%s", DEFAULT_GRADIENT_NAME);
+
+} /* end p_init_default_cuvals_without_cloud_layers */
+
+static void
+p_init_default_cuvals(firepattern_val_t *cuvals)
+{
+ p_init_default_cuvals_without_cloud_layers(cuvals);
+ cuvals->cloudLayer1 = -1;
+ cuvals->fireShapeLayer = -1;
+
+} /* end p_init_default_cuvals */
+
+/* --------------------------------------
+ * p_check_for_valid_cloud_layers
+ * --------------------------------------
+ *
+ */
+static void
+p_check_for_valid_cloud_layers(firepattern_val_t *cuvals)
+{
+ if(gimp_drawable_is_valid(cuvals->cloudLayer1) != TRUE)
+ {
+ cuvals->cloudLayer1 = -1;
+ }
+ if(gimp_drawable_is_valid(cuvals->fireShapeLayer) != TRUE)
+ {
+ cuvals->fireShapeLayer = -1;
+ }
+} /* end p_check_for_valid_cloud_layers */
+
+
+/* --------------------------------------
+ * p_init_cuvals
+ * --------------------------------------
+ *
+ */
+static void
+p_init_cuvals(firepattern_val_t *cuvals)
+{
+ p_init_default_cuvals(cuvals);
+
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_NAME, cuvals);
+
+ p_check_for_valid_cloud_layers(cuvals);
+
+} /* end p_init_cuvals */
+
+/* --------------------------------------
+ * p_caclulate_trapezoid_blend
+ * --------------------------------------
+ * calculate opacity blend factor (in the range 0.0 for fully black
+ * upto 1.0 for keep full original colorchannel e.g. white) for the specified coordinate px/py
+ * Coordinates within the trapezoid shape return value 1.0
+ * Coorinates left or right outside the shape will result 1.0 > value > 0 (e.g. a shade of gray value)
+ * depending on their distance to the tapezoid core shape.
+ * Coordinates where distance is greater than the flameBorder will return 0.0.
+ * Note that Coordinates above flameHeight immediate switch to full black (e.g. 0.0)
+ * because the processed fire shape layer is already initally filled with a soft vertical blend
+ * from white at the base line to black at flame height.
+ */
+static gdouble
+p_caclulate_trapezoid_blend(gint32 px, gint32 py, firepattern_val_t *cuvals, firepattern_context_t *ctxt)
+{
+ gdouble widthAtCurrentHeight;
+ gdouble mixFactor;
+ gdouble borderWidth;
+ gdouble halfImageWidth;
+ gdouble borderDistance;
+ gdouble blendFactor;
+ gint32 ppy; /* flipped y coord where 0 is on bottom of the image */
+ gint32 ppx; /* x coord shifted by flameOffestX */
+
+ ppy = ctxt->height - py; /* flip y coord */
+ ppx = px - ctxt->flameOffestXInPixels;
+
+ if((ppy > ctxt->flameHeightInPixels)
+ || (ctxt->flameHeightInPixels == 0))
+ {
+ /* fully black for all pixels above flame height */
+ return (0.0);
+ }
+
+ mixFactor = (gdouble)ppy / ctxt->flameHeightInPixels;
+ widthAtCurrentHeight = GAP_BASE_MIX_VALUE(mixFactor, ctxt->flameWidthBaseInPixels, ctxt->flameWidthTopInPixels);
+
+ borderWidth = (ctxt->width - widthAtCurrentHeight) / 2.0;
+
+
+ halfImageWidth = (gdouble)ctxt->width / 2.0;
+ if(ppx > halfImageWidth)
+ {
+ borderDistance = ctxt->width - ppx;
+ }
+ else
+ {
+ borderDistance = ppx;
+ }
+
+ if(borderDistance > borderWidth)
+ {
+ /* keep full original colorchannel e.g. white for pixel within the trapezoid core area */
+ return (1.0);
+ }
+
+ borderDistance = borderWidth - borderDistance;
+ if(borderDistance >= ctxt->flameBorderInPixels)
+ {
+ /* fully transparent for pixels outside trapezoid shape + flameBorder */
+ return (0.0);
+ }
+
+ blendFactor = (gdouble)(ctxt->flameBorderInPixels - borderDistance) / (gdouble)ctxt->flameBorderInPixels;
+
+ return (blendFactor);
+
+} /* p_caclulate_trapezoid_blend */
+
+
+/* ---------------------------------
+ * p_rgn_render_fireshape_trapezoid
+ * ---------------------------------
+ * render trapezoid shape on the fireshape layer.
+ */
+static void
+p_rgn_render_fireshape_trapezoid (const GimpPixelRgn *destPR
+ , firepattern_val_t *cuvals, firepattern_context_t *ctxt)
+{
+ guint row;
+ guint ii;
+ guint colorChannels;
+ gint32 px;
+ gint32 py;
+ gdouble blendFactor; /* 0 .. black, 1.. keep original color channel value */
+ guchar *dest; /* the destination drawable */
+
+ dest = destPR->data; /* the destination drawable */
+
+ if(destPR->bpp > 2)
+ {
+ colorChannels = 3;
+ }
+ else
+ {
+ colorChannels = 1;
+ }
+
+ for (row = 0; row < destPR->h; row++)
+ {
+ guint col;
+ guint idxDest;
+
+ idxDest = 0;
+ for(col = 0; col < destPR->w; col++)
+ {
+ px = destPR->x + col;
+ py = destPR->y + row;
+
+
+ blendFactor = p_caclulate_trapezoid_blend(px, py, cuvals, ctxt);
+ for(ii=0; ii < colorChannels; ii++)
+ {
+ gdouble value;
+
+ value = (gdouble)dest[idxDest + ii];
+ value = (value * blendFactor);
+ dest[idxDest + ii] = value;
+ }
+
+ idxDest += destPR->bpp;
+ }
+
+ dest += destPR->rowstride;
+ }
+
+} /* end p_rgn_render_fireshape_trapezoid */
+
+
+
+/* --------------------------------------
+ * p_render_fireshape_layer
+ * --------------------------------------
+ * render fire shape layer either as rectangle
+ * as blend from white at the base line to black on flameHeight
+ * or as trapezoid shape like this:
+ *
+ * flameWidthTop
+ * +-------------------+
+ * | |
+ * .....oo black oo..... -------------+
+ * .....ooooooooooooo..... | flameHeight
+ * .....ooooooooooooooo..... |
+ * .....oooo white ooooooo..... -------------+
+ * | | |
+ * | +----+
+ * | flameBorder
+ * | |
+ * +--------------------------+
+ * flameWidthBase
+ *
+ */
+static gboolean
+p_render_fireshape_layer(firepattern_context_t *ctxt, firepattern_val_t *cuvals
+ ,gint32 drawableId
+ )
+{
+ gdouble x1;
+ gdouble y1;
+ gdouble x2;
+ gdouble y2;
+ gboolean success;
+
+ ctxt->flameOffestXInPixels = (gdouble)ctxt->width * cuvals->flameOffestX;
+ ctxt->flameBorderInPixels = (gdouble)ctxt->width * cuvals->flameBorder;
+ ctxt->flameHeightInPixels = (gdouble)ctxt->height * cuvals->flameHeight;
+ ctxt->flameWidthTopInPixels = (gdouble)ctxt->width * cuvals->flameWidthTop;
+ ctxt->flameWidthBaseInPixels = (gdouble)ctxt->width * cuvals->flameWidthBase;
+
+ x1 = 0.0;
+ x2 = 0.0;
+ y1 = ctxt->height - ctxt->flameHeightInPixels;
+ y2 = ctxt->height;
+
+ success = gimp_edit_blend( drawableId
+ , GIMP_FG_BG_RGB_MODE /* GimpBlendMode */
+ , GIMP_NORMAL_MODE /* GimpLayerModeEffects paint_mode */
+ , GIMP_GRADIENT_LINEAR /* GimpGradientType gradient_type */
+ , 100.0 /* opacity */
+ , 0.0 /* gdouble offset */
+ , GIMP_REPEAT_NONE /* GimpRepeatMode repeat */
+ , FALSE /* gboolean reverse */
+ , FALSE /* gboolean supersample */
+ , 1 /* gint max_depth */
+ , 0.0 /* gdouble threshold */
+ , FALSE /* gboolean dither */
+ , x1
+ , y1
+ , x2
+ , y2
+ );
+
+ if ((success) && (cuvals->useTrapezoidShape))
+ {
+ GimpPixelRgn dstPR;
+ GimpDrawable *fireShapeDrawable;
+ gpointer pr;
+
+ fireShapeDrawable = gimp_drawable_get(drawableId);
+ gimp_pixel_rgn_init (&dstPR, fireShapeDrawable, 0, 0
+ , fireShapeDrawable->width, fireShapeDrawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+ for (pr = gimp_pixel_rgns_register (1, &dstPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_rgn_render_fireshape_trapezoid (&dstPR, cuvals, ctxt);
+ }
+
+ gimp_drawable_detach(fireShapeDrawable);
+
+ }
+
+ return (success);
+
+} /* end p_render_fireshape_layer */
+
+
+/* --------------------------------------
+ * p_convertBlendNum_to_BlendMode
+ * --------------------------------------
+ */
+static GimpLayerModeEffects
+p_convertBlendNum_to_BlendMode(gint32 blendNum)
+{
+ GimpLayerModeEffects blendMode = GIMP_BURN_MODE;
+ switch(blendNum)
+ {
+ case BLEND_NUM_BURN:
+ blendMode = GIMP_BURN_MODE;
+ break;
+ case BLEND_NUM_SUBTRACT:
+ blendMode = GIMP_SUBTRACT_MODE;
+ break;
+ case BLEND_NUM_MULTIPLY:
+ blendMode = GIMP_MULTIPLY_MODE;
+ break;
+ default:
+ printf("unsupported blend mode, using default value Burn\n");
+ break;
+ }
+
+ return (blendMode);
+}
+
+/* --------------------------------------
+ * p_get_samples_gradient
+ * --------------------------------------
+ *
+ * Allocate 256 color samples
+ * according to the selected gradient. (or the active gradient)
+ * Each sample has 4 bytes (RGBA).
+ * returns the allocated byte_samples
+ */
+static guchar *
+p_get_samples_gradient (const gchar *selectedGradient, gboolean reverseGradient)
+{
+ const gchar *gradient_name;
+ gint n_f_samples;
+ gdouble *f_samples, *f_samp; /* float samples */
+ guchar *byte_samples;
+ guchar *b_samp; /* byte samples */
+ gint bpp;
+ gint i, j;
+
+ gradient_name = gimp_context_get_gradient ();
+ if (selectedGradient[0] != '\0')
+ {
+ if (strcmp(selectedGradient, ACTIVE_GRADIENT_NAME) != 0)
+ {
+ gradient_name = selectedGradient; /* use the selected gradient name */
+ }
+ }
+
+
+ gimp_gradient_get_uniform_samples (gradient_name, N_GRADIENT_COLORSAMPLES, reverseGradient,
+ &n_f_samples, &f_samples);
+
+ bpp = BPP_SAMPLE_COLOR;
+ byte_samples = g_new (guchar, N_GRADIENT_COLORSAMPLES * bpp);
+
+ for (i = 0; i < N_GRADIENT_COLORSAMPLES; i++)
+ {
+ b_samp = &byte_samples[i * bpp];
+ f_samp = &f_samples[i * 4];
+ for (j = 0; j < bpp; j++)
+ {
+ b_samp[j] = f_samp[j] * 255;
+ }
+ }
+
+ g_free (f_samples);
+ return (byte_samples);
+
+} /* end p_get_samples_gradient */
+
+
+
+/* --------------------------------------
+ * p_delta_guchar_color
+ * --------------------------------------
+ */
+static void
+p_delta_guchar_color(guchar *val, guchar *val_from, guchar *val_to, gint32 total_steps, gdouble current_step)
+{
+ double delta;
+ guint l_idx;
+
+ if(total_steps < 1) return;
+
+ for(l_idx = 0; l_idx < BPP_SAMPLE_COLOR; l_idx++)
+ {
+ delta = ((double)(val_to[l_idx] - val_from[l_idx]) / (double)total_steps) * ((double)total_steps - current_step);
+ val[l_idx] = val_from[l_idx] + delta;
+
+ }
+} /* end p_delta_guchar_color */
+
+
+
+/* --------------------------------------
+ * p_get_samples_from_gradients
+ * --------------------------------------
+ * Allocate 256 color samples
+ * according to the selected gradient. (or the active gradient)
+ * Each sample has 4 bytes (RGBA).
+ * returns the allocated byte_samples
+ *
+ * in case when the plug-in is called in animated call style (multiple calls to render
+ * one frame) it checks if from and to values with different gradients were used
+ * and returns a mix of both gradients when 2 gradients are available.
+ */
+static guchar *
+p_get_samples_from_gradients(firepattern_val_t *cuvals, firepattern_context_t *ctxt)
+{
+ GapLastvalAnimatedCallInfo animCallInfo;
+ firepattern_val_t cval_from;
+ firepattern_val_t cval_to;
+ gint32 dataSize;
+ gboolean haveTwoGradients;
+ guchar *byte_samples;
+
+ haveTwoGradients = FALSE;
+
+ dataSize = gimp_get_data_size(PLUG_IN_DATA_ITER_FROM);
+
+
+ if(dataSize == sizeof(cval_from))
+ {
+ dataSize = gimp_get_data_size(PLUG_IN_DATA_ITER_TO);
+ if(dataSize == sizeof(cval_to))
+ {
+ dataSize = gimp_get_data_size(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO);
+ gimp_get_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO, &animCallInfo);
+
+ if(gap_debug)
+ {
+ printf("p_get_samples_from_gradients: dataSize:%d animatedCallInProgress:%d\n"
+ , (int)dataSize
+ , (int)animCallInfo.animatedCallInProgress
+ );
+ }
+
+
+ if((dataSize == sizeof(GapLastvalAnimatedCallInfo))
+ && (animCallInfo.animatedCallInProgress))
+ {
+ gimp_get_data(PLUG_IN_DATA_ITER_FROM, &cval_from);
+ gimp_get_data(PLUG_IN_DATA_ITER_TO, &cval_to);
+
+ if(gap_debug)
+ {
+ printf("p_get_samples_from_gradients: gradient:%s FROM:%s TO:%s\n"
+ , cuvals->gradientName
+ , cval_from.gradientName
+ , cval_to.gradientName
+ );
+ }
+
+ if ((strcmp(cval_from.gradientName, cuvals->gradientName) == 0)
+ || (strcmp(cval_to.gradientName, cuvals->gradientName) == 0))
+ {
+ if (strcmp(cval_from.gradientName, cval_to.gradientName) != 0)
+ {
+ haveTwoGradients = TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ byte_samples = p_get_samples_gradient (cuvals->gradientName, cuvals->reverseGradient);
+
+ if (haveTwoGradients)
+ {
+
+ /* 2 gradients are available, therefore get samples from both and mix
+ * all color samples according to current frame phase
+ */
+ guchar *byte_samples_FROM;
+ guchar *byte_samples_TO;
+ gint ii;
+
+ byte_samples_FROM = p_get_samples_gradient (cval_from.gradientName, cval_from.reverseGradient);
+ byte_samples_TO = p_get_samples_gradient (cval_to.gradientName, cval_to.reverseGradient);
+
+
+ for(ii=0; ii < 256; ii++)
+ {
+ gint idx;
+ idx = ii * BPP_SAMPLE_COLOR;
+ p_delta_guchar_color(&byte_samples[idx]
+ ,&byte_samples_FROM[idx]
+ ,&byte_samples_TO[idx]
+ ,animCallInfo.total_steps
+ ,animCallInfo.current_step
+ );
+
+ }
+
+ g_free(byte_samples_FROM);
+ g_free(byte_samples_TO);
+
+ }
+
+ return (byte_samples);
+
+} /* end p_get_samples_from_gradients */
+
+
+/* -----------------------------------------
+ * p_init_context_and_cloud_and_shape_layers
+ * -----------------------------------------
+ *
+ */
+static gboolean
+p_init_context_and_cloud_and_shape_layers(gint32 drawable_id, firepattern_val_t *cuvals, firepattern_context_t *ctxt)
+{
+ gboolean success;
+ gdouble strechedHeight;
+
+ ctxt->image_id = gimp_drawable_get_image(drawable_id);
+ ctxt->blend_mode = p_convertBlendNum_to_BlendMode(cuvals->blendNum);
+ ctxt->width = gimp_image_width(ctxt->image_id);
+ ctxt->height = gimp_image_height(ctxt->image_id);
+ strechedHeight = (gdouble)gimp_image_height(ctxt->image_id) * cuvals->stretchHeight;
+ ctxt->strechedHeight = strechedHeight;
+ ctxt->add_display_to_ref_image = FALSE;
+ ctxt->forceFireShapeLayerCreationPerFrame = cuvals->forceShapeLayer;
+
+ success = TRUE;
+ ctxt->ref_image_id = -1;
+
+ /* check if cloud layer (the pattern) is already available */
+ if(!gimp_drawable_is_valid(cuvals->cloudLayer1))
+ {
+ /* create both cloud layers */
+ GRand *gr;
+ gint32 seed;
+ gr = g_rand_new ();
+
+ ctxt->ref_image_id = gimp_image_new(ctxt->width, ctxt->height, GIMP_RGB);
+ gimp_image_set_filename(ctxt->ref_image_id, "FirePattern");
+
+ cuvals->cloudLayer1 = gimp_layer_new(ctxt->ref_image_id
+ , "Frame"
+ , ctxt->width
+ , ctxt->strechedHeight
+ , GIMP_RGBA_IMAGE
+ , 100.0 /* full opaque */
+ , GIMP_NORMAL_MODE /* 0 */
+ );
+ cuvals->fireShapeLayer = gimp_layer_new(ctxt->ref_image_id
+ , "FireShape"
+ , ctxt->width
+ , ctxt->height
+ , GIMP_RGBA_IMAGE
+ , 100.0 /* full opaque */
+ , GIMP_NORMAL_MODE /* 0 */
+ );
+ gimp_image_add_layer(ctxt->ref_image_id, cuvals->fireShapeLayer, -1);
+ gimp_image_add_layer(ctxt->ref_image_id, cuvals->cloudLayer1, -1);
+
+
+ /* Adds the solid noise */
+ seed = cuvals->seed1;
+ if (seed == 0)
+ {
+ seed = g_rand_int_range(gr, 0, 2100000000);
+ }
+ success = gap_pdb_call_solid_noise(ctxt->ref_image_id
+ ,cuvals->cloudLayer1
+ ,1 /* 1 create tileable output */
+ ,0 /* turbulent noise 0 = no, 1 = yes */
+ ,seed /* rand(2100000000) */
+ ,cuvals->detail /* detail level */
+ ,cuvals->scalex /* horizontal texture size */
+ ,cuvals->scaley /* vertical texture size */
+ );
+ if(success)
+ {
+ /* render an inital fireshape layer
+ * (intended to be used when shape shall shall not change)
+ */
+ success = p_render_fireshape_layer(ctxt, cuvals
+ ,cuvals->fireShapeLayer
+ );
+ }
+
+ g_rand_free (gr);
+ ctxt->add_display_to_ref_image = TRUE;
+
+ }
+
+ if(!gimp_drawable_is_valid(cuvals->fireShapeLayer))
+ {
+ if((cuvals->createImage != TRUE) || (ctxt->ref_image_id < 0))
+ {
+ /* set flag that triggers fire shape rendering for each processed frame */
+ ctxt->forceFireShapeLayerCreationPerFrame = TRUE;
+ }
+ else
+ {
+ /* render an inital fireshape layer
+ * (intended to be used when shape shall shall not change)
+ */
+ success = p_render_fireshape_layer(ctxt, cuvals
+ ,cuvals->fireShapeLayer
+ );
+ }
+ }
+
+ if(success)
+ {
+ ctxt->byte_samples = p_get_samples_from_gradients (cuvals, ctxt);
+ }
+
+ return (success);
+
+} /* end p_init_context_and_cloud_and_shape_layers */
+
+
+/* --------------------------------------
+ * p_cloud_size_check
+ * --------------------------------------
+ * check if layer is same size as processed image size (as specified in the context)
+ */
+static void
+p_cloud_size_check(gint32 layer_id, firepattern_context_t *ctxt)
+{
+ if( (gimp_drawable_width(layer_id) != ctxt->width)
+ || (gimp_drawable_height(layer_id) != ctxt->strechedHeight) )
+ {
+ gimp_layer_scale(layer_id, ctxt->width, ctxt->strechedHeight, TRUE);
+ gimp_layer_set_offsets(layer_id, 0, 0);
+ }
+} /* end p_cloud_size_check */
+
+
+/* --------------------------------------
+ * p_shape_size_check
+ * --------------------------------------
+ * check if layer is same size as processed image size (as specified in the context)
+ */
+static void
+p_shape_size_check(gint32 layer_id, firepattern_context_t *ctxt)
+{
+ if( (gimp_drawable_width(layer_id) != ctxt->width)
+ || (gimp_drawable_height(layer_id) != ctxt->height) )
+ {
+ gimp_layer_scale(layer_id, ctxt->width, ctxt->height, TRUE);
+ gimp_layer_set_offsets(layer_id, 0, 0);
+ }
+} /* end p_shape_size_check */
+
+
+
+/* --------------------------------------
+ * p_map_func
+ * --------------------------------------
+ * map color for one pixel by its luminosity that
+ * is used as index (range 0 to 255) to the table of
+ * given sample colors provided in MapParam data.
+ * in case the processed drawable has an alpha channel
+ * calculate the opacity according to useTransparentBg
+ * as specified in MapParam.
+ */
+static void
+p_map_func (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gpointer data)
+{
+ MapParam *param = data;
+ guint lum;
+ guint lumSample;
+ gint b;
+ guchar samp[BPP_SAMPLE_COLOR];
+
+ lum = (param->is_rgb) ? LUMINOSITY (src) : src[0];
+ samp[0] = param->samples[lum * BPP_SAMPLE_COLOR];
+ samp[1] = param->samples[(lum * BPP_SAMPLE_COLOR) +1];
+ samp[2] = param->samples[(lum * BPP_SAMPLE_COLOR) +2];
+ samp[3] = param->samples[(lum * BPP_SAMPLE_COLOR) +3];
+ lumSample = LUMINOSITY (samp);
+ if(bpp < 3)
+ {
+ samp[0] = lumSample;
+ }
+
+ if (param->has_alpha)
+ {
+ for (b = 0; b < bpp - 1; b++)
+ {
+ dest[b] = samp[b];
+ }
+ if (param->useTransparentBg)
+ {
+ guint lumSample2;
+
+ lumSample2 = MIN((lumSample * 64), 255);
+
+ /* render ALPHA_FROM_LUMINOSITY */
+ dest[b] = (lumSample2 * lum) / 255;
+ }
+ else
+ {
+ /* render ALPHA_FROM_GRADIENT_SAMPLE */
+ dest[b] = ((guint)samp[BPP_SAMPLE_COLOR -1] * (guint)src[b]) / 255;
+ }
+ }
+ else
+ {
+ for (b = 0; b < bpp; b++)
+ {
+ dest[b] = samp[b];
+ }
+ }
+} /* end p_map_func */
+
+
+
+/* --------------------------------------
+ * p_remap_drawable_with_gradient_colors
+ * --------------------------------------
+ * colorize the specified drawable with gradient colors
+ * where the luminosity is used to both fetch the relevant gradient color sample
+ * and to define the alpha channel.
+ */
+static void
+p_remap_drawable_with_gradient_colors (gint32 drawable_id, firepattern_val_t *cuvals, firepattern_context_t *ctxt)
+{
+ MapParam param;
+
+ GimpDrawable *drawable;
+
+ drawable = gimp_drawable_get (drawable_id);
+
+ param.useTransparentBg = cuvals->useTransparentBg;
+ param.samples = ctxt->byte_samples;
+ param.is_rgb = gimp_drawable_is_rgb (drawable->drawable_id);
+ param.has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ gimp_rgn_iterate2 (drawable, 0 /* unused */, p_map_func, ¶m);
+
+ gimp_drawable_detach(drawable);
+
+} /* end p_remap_drawable_with_gradient_colors */
+
+
+
+/* --------------------------------------
+ * p_drawable_get_name
+ * --------------------------------------
+ */
+static const char*
+p_drawable_get_name(gint32 drawable_id)
+{
+ const char *invalidName = "(invalid)";
+
+ if(gimp_drawable_is_valid(drawable_id))
+ {
+ return(gimp_drawable_get_name(drawable_id));
+ }
+
+ return (invalidName);
+}
+
+/* --------------------------------------
+ * p_run_renderFirePattern
+ * --------------------------------------
+ * renders the fire pattern effect onto the specified drawable.
+ * returns TRUE on success
+ */
+static gboolean
+p_run_renderFirePattern(gint32 drawable_id, firepattern_val_t *cuvals, firepattern_context_t *ctxt)
+{
+ gint32 templayer_id;
+ gint32 newlayer1_id = -1;
+ gint32 newlayer2_id = -1;
+ gboolean isVisible;
+ gboolean success;
+ gint32 count;
+ gint32 nframesToProcess;
+
+
+
+ if (!gimp_drawable_is_layer(drawable_id))
+ {
+ g_message(_("drawable:%d is not a layer\n"), drawable_id);
+ return (FALSE);
+ }
+
+
+ /* save visibility status of processed layer .. */
+ isVisible = gimp_drawable_get_visible(drawable_id);
+ /* .. and force visibility (required for merge down fire pattern to the active layer) */
+ gimp_drawable_set_visible(drawable_id, TRUE);
+
+ templayer_id = drawable_id;
+ nframesToProcess = 1;
+
+ gimp_layer_resize_to_image_size(drawable_id);
+ success = p_init_context_and_cloud_and_shape_layers(drawable_id, cuvals, ctxt);
+
+ if (cuvals->createImage)
+ {
+ ctxt->image_id = gimp_image_new(ctxt->width, ctxt->height, GIMP_RGB);
+ /* dont save history during the creation of the animation layers */
+ gimp_image_undo_freeze(ctxt->image_id);
+ templayer_id = -1;
+
+ if (cuvals->nframes > 1)
+ {
+ nframesToProcess = cuvals->nframes;
+ }
+ }
+
+
+
+ if(gap_debug)
+ {
+ printf("p_run_renderFirePattern: drawable_id :%d (%s)\n", (int)drawable_id, p_drawable_get_name(drawable_id));
+ printf("p_run_renderFirePattern: scalex:%f\n", (float)cuvals->scalex);
+ printf("p_run_renderFirePattern: scaley:%f\n", (float)cuvals->scaley);
+ printf("p_run_renderFirePattern: detail:%d\n", (int)cuvals->detail);
+ printf("p_run_renderFirePattern: useTrapezoidShape:%d\n", (int)cuvals->useTrapezoidShape);
+ printf("p_run_renderFirePattern: flameHeight:%f\n", (float)cuvals->flameHeight);
+ printf("p_run_renderFirePattern: flameWidthBase:%f\n", (float)cuvals->flameWidthBase);
+ printf("p_run_renderFirePattern: flameWidthTop:%f\n", (float)cuvals->flameWidthTop);
+ printf("p_run_renderFirePattern: flameBorder:%f\n", (float)cuvals->flameBorder);
+ printf("p_run_renderFirePattern: flameOffestX:%f\n", (float)cuvals->flameOffestX);
+ printf("p_run_renderFirePattern: blendNum:%d\n", (int)cuvals->blendNum);
+ printf("p_run_renderFirePattern: stretchHeight:%f\n", (float)cuvals->stretchHeight);
+ printf("p_run_renderFirePattern: shiftPhaseY:%f\n", (float)cuvals->shiftPhaseY);
+ printf("p_run_renderFirePattern: createImage:%d\n", (int)cuvals->createImage);
+ printf("p_run_renderFirePattern: nframes:%d\n", (int)cuvals->nframes);
+ printf("p_run_renderFirePattern: createFireLayer:%d\n", (int)cuvals->createFireLayer);
+ printf("p_run_renderFirePattern: useTransparentBg:%d\n", (int)cuvals->useTransparentBg);
+ printf("p_run_renderFirePattern: fireOpacity:%f\n", (float)cuvals->fireOpacity);
+
+ printf("p_run_renderFirePattern: seed1:%d\n", (int)cuvals->seed1);
+ printf("p_run_renderFirePattern: cloudLayer1:%d (%s)\n", (int)cuvals->cloudLayer1, p_drawable_get_name(cuvals->cloudLayer1));
+ printf("p_run_renderFirePattern: fireShapeLayer:%d (%s)\n", (int)cuvals->fireShapeLayer, p_drawable_get_name(cuvals->fireShapeLayer));
+ printf("p_run_renderFirePattern: forceShapeLayer:%d\n", (int)cuvals->forceShapeLayer);
+ printf("p_run_renderFirePattern: reverseGradient:%d\n", (int)cuvals->reverseGradient);
+ printf("p_run_renderFirePattern: gradientName:%s\n", cuvals->gradientName);
+
+ printf("p_run_renderFirePattern: ref_image_id:%d\n", (int)ctxt->ref_image_id);
+ printf("p_run_renderFirePattern: image_id:%d\n", (int)ctxt->image_id);
+ printf("p_run_renderFirePattern: width:%d\n", (int)ctxt->width);
+ printf("p_run_renderFirePattern: height:%d\n", (int)ctxt->height);
+ printf("p_run_renderFirePattern: strechedHeight:%d\n", (int)ctxt->strechedHeight);
+ printf("p_run_renderFirePattern: blend_mode:%d\n", (int)ctxt->blend_mode);
+ printf("p_run_renderFirePattern: forceFireShapeLayerCreationPerFrame:%d\n", (int)ctxt->forceFireShapeLayerCreationPerFrame);
+ printf("p_run_renderFirePattern: success:%d\n", (int)success);
+
+ }
+
+ if(!success)
+ {
+ return (FALSE);
+ }
+
+ /* save gimp context (before changeing FG/BG color) */
+ gimp_context_push();
+ gimp_context_set_default_colors(); /* set fg and bg to black and white */
+
+ /* end while nframes loop */
+ for(count=0; count < nframesToProcess; count++)
+ {
+ if (cuvals->createImage)
+ {
+ /* in case we are creating a multilayer anim in a new image
+ * copy the drawable to a new layer in this new image
+ */
+ templayer_id = gimp_layer_new_from_drawable(drawable_id, ctxt->image_id);
+ gimp_image_add_layer(ctxt->image_id, templayer_id, -1 /* -1 place above active layer */);
+ }
+
+ /* copy cloud layers from ref image to current processed image_id
+ * at stackposition above the current processed layer (templayer_id)
+ */
+ newlayer1_id = gimp_layer_new_from_drawable(cuvals->cloudLayer1, ctxt->image_id);
+ gimp_image_set_active_layer(ctxt->image_id, templayer_id);
+
+ if(ctxt->forceFireShapeLayerCreationPerFrame)
+ {
+ newlayer2_id = gimp_layer_new(ctxt->image_id
+ , "FireShape"
+ , ctxt->width
+ , ctxt->height
+ , GIMP_RGBA_IMAGE
+ , 100.0 /* full opaque */
+ , GIMP_NORMAL_MODE /* 0 */
+ );
+ gimp_image_add_layer(ctxt->image_id, newlayer2_id, -1 /* -1 place above active layer */);
+ p_render_fireshape_layer(ctxt, cuvals, newlayer2_id);
+ }
+ else
+ {
+ newlayer2_id = gimp_layer_new_from_drawable(cuvals->fireShapeLayer, ctxt->image_id);
+ gimp_image_add_layer(ctxt->image_id, newlayer2_id, -1 /* -1 place above active layer */);
+ }
+
+ gimp_image_add_layer(ctxt->image_id, newlayer1_id, -1 /* -1 place above active layer */);
+
+ p_cloud_size_check(newlayer1_id, ctxt);
+ p_shape_size_check(newlayer2_id, ctxt);
+
+ gimp_drawable_set_visible(newlayer1_id, TRUE);
+ gimp_drawable_set_visible(newlayer2_id, TRUE);
+ gimp_drawable_set_visible(templayer_id, TRUE);
+
+ /* calculate offsets to shift the cloud layer according to pahse of the currently processed frame */
+ {
+ gdouble shifty;
+ gint32 intShifty;
+ gint32 offsetx;
+ gint32 offsety;
+
+ if(nframesToProcess > 1)
+ {
+ gdouble phY;
+ phY = cuvals->shiftPhaseY;
+
+ if (phY == 0.0)
+ {
+ phY = 1.0;
+ }
+ shifty = (gdouble)count * (((gdouble)ctxt->strechedHeight * phY) / (gdouble)nframesToProcess);
+ }
+ else
+ {
+ shifty = (gdouble)ctxt->strechedHeight * cuvals->shiftPhaseY;
+ }
+ intShifty = rint(shifty);
+
+ offsetx = 0;
+ offsety = 0 - (intShifty % ctxt->strechedHeight);
+ gimp_drawable_offset(newlayer1_id, 1 /* 1:Wrap around */, 0 /* fill type */, offsetx, offsety);
+
+ gimp_layer_set_mode(newlayer1_id, ctxt->blend_mode);
+ gimp_layer_set_mode(newlayer2_id, GIMP_NORMAL_MODE);
+
+ newlayer2_id = gimp_image_merge_down(ctxt->image_id, newlayer1_id, GIMP_EXPAND_AS_NECESSARY);
+
+ gimp_layer_resize_to_image_size(newlayer2_id);
+
+ }
+
+ /* colorize firepattern layer and calculate its alpha channel from luminosity */
+ p_remap_drawable_with_gradient_colors (newlayer2_id, cuvals, ctxt);
+
+ gimp_layer_set_opacity(newlayer2_id, cuvals->fireOpacity);
+ gimp_layer_set_mode(newlayer2_id, GIMP_NORMAL_MODE);
+
+
+ /* optional merge the firepattern onto the processed layer */
+ if(cuvals->createFireLayer != TRUE)
+ {
+ templayer_id = gimp_image_merge_down(ctxt->image_id, newlayer2_id, GIMP_EXPAND_AS_NECESSARY);
+ }
+
+ if (cuvals->createImage)
+ {
+ gchar *layerName;
+
+ layerName = g_strdup_printf("Frame_%03d", (int)count +1);
+ gimp_drawable_set_name(templayer_id, layerName);
+ g_free(layerName);
+ }
+
+ if(!success)
+ {
+ break;
+ }
+
+ } /* end while nframes loop */
+
+
+ /* restore visibility status of processed layer */
+ gimp_drawable_set_visible(templayer_id, isVisible);
+
+ if (cuvals->createImage)
+ {
+ gimp_image_undo_thaw(ctxt->image_id);
+ }
+
+ /* restore the gimp context */
+ gimp_context_pop();
+
+ /* in case a new image was created in an interactive mode,
+ * add a display for this newly created imge
+ */
+ if(ctxt->run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ if (cuvals->createImage)
+ {
+ gimp_display_new(ctxt->image_id);
+ }
+ if(ctxt->add_display_to_ref_image)
+ {
+ gimp_display_new(ctxt->ref_image_id);
+ }
+ }
+
+ return (success);
+
+} /* end p_run_renderFirePattern */
+
+
+/*
+ * DIALOG and callback stuff
+ */
+
+
+/* --------------------------------------
+ * on_blend_radio_callback
+ * --------------------------------------
+ */
+static void
+on_blend_radio_callback(GtkWidget *wgt, gpointer user_data)
+{
+ FirePatternDialog *wcd;
+
+ if(gap_debug)
+ {
+ printf("on_blend_radio_callback: START\n");
+ }
+ wcd = (FirePatternDialog*)user_data;
+ if(wcd != NULL)
+ {
+ if(wcd->vals != NULL)
+ {
+ if(wgt == wcd->radio_blend_burn) { wcd->vals->blendNum = BLEND_NUM_BURN; }
+ if(wgt == wcd->radio_blend_subtract) { wcd->vals->blendNum = BLEND_NUM_SUBTRACT; }
+ if(wgt == wcd->radio_blend_multiply) { wcd->vals->blendNum = BLEND_NUM_MULTIPLY; }
+
+ }
+ }
+}
+
+/* --------------------------------------
+ * p_update_widget_sensitivity
+ * --------------------------------------
+ */
+static void
+p_update_widget_sensitivity (FirePatternDialog *wcd)
+{
+ gboolean inverseCreateImage;
+ gboolean inversecreateNewPattern;
+ gboolean inverseForceShape;
+
+
+ /* createImage dependent widgets */
+ if(wcd->vals->createImage)
+ {
+ inverseCreateImage = FALSE;
+ }
+ else
+ {
+ inverseCreateImage = TRUE;
+ }
+ gtk_widget_set_sensitive(wcd->nframes_spinbutton , wcd->vals->createImage);
+ //gtk_widget_set_sensitive(wcd->shiftPhaseY_spinbutton , inverseCreateImage);
+
+ /* createNewPattern dependent widgets */
+ if(wcd->createNewPattern)
+ {
+ inversecreateNewPattern = FALSE;
+ }
+ else
+ {
+ inversecreateNewPattern = TRUE;
+ }
+ gtk_widget_set_sensitive(wcd->stretchHeight_spinbutton , wcd->createNewPattern);
+ gtk_widget_set_sensitive(wcd->scaleX_spinbutton , wcd->createNewPattern);
+ gtk_widget_set_sensitive(wcd->scaleY_spinbutton , wcd->createNewPattern);
+ gtk_widget_set_sensitive(wcd->seed1_spinbutton , wcd->createNewPattern);
+ gtk_widget_set_sensitive(wcd->detail_spinbutton , wcd->createNewPattern);
+ gtk_widget_set_sensitive(wcd->cloud1Combo , inversecreateNewPattern);
+ gtk_widget_set_sensitive(wcd->fireShapeCombo , inversecreateNewPattern);
+
+
+ /* createNewShape dependent widgets */
+ if(wcd->vals->forceShapeLayer)
+ {
+ inverseForceShape = FALSE;
+ }
+ else
+ {
+ inverseForceShape = TRUE;
+ }
+ gtk_widget_set_sensitive(wcd->flameHeight_spinbutton , wcd->vals->forceShapeLayer);
+ gtk_widget_set_sensitive(wcd->flameWidthBase_spinbutton , wcd->vals->forceShapeLayer);
+ gtk_widget_set_sensitive(wcd->flameWidthTop_spinbutton , wcd->vals->forceShapeLayer);
+ gtk_widget_set_sensitive(wcd->flameBorder_spinbutton , wcd->vals->forceShapeLayer);
+ gtk_widget_set_sensitive(wcd->flameOffestX_spinbutton , wcd->vals->forceShapeLayer);
+ gtk_widget_set_sensitive(wcd->useTrapezoidShapeCheckbutton , wcd->vals->forceShapeLayer);
+ gtk_widget_set_sensitive(wcd->fireShapeCombo , inverseForceShape);
+
+} /* end p_update_widget_sensitivity */
+
+
+/* --------------------------------------
+ * p_init_widget_values
+ * --------------------------------------
+ * update GUI widgets to reflect the current values.
+ */
+static void
+p_init_widget_values(FirePatternDialog *wcd)
+{
+ gint countClouds;
+ if(wcd == NULL)
+ {
+ return;
+ }
+ if(wcd->vals == NULL)
+ {
+ return;
+ }
+
+ /* init spnbuttons */
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->nframes_spinbutton_adj)
+ , (gfloat)wcd->vals->nframes);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->fireOpacity_spinbutton_adj)
+ , (gfloat)wcd->vals->fireOpacity);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->stretchHeight_spinbutton_adj)
+ , (gfloat)wcd->vals->stretchHeight);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->flameHeight_spinbutton_adj)
+ , (gfloat)wcd->vals->flameHeight);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->flameWidthBase_spinbutton_adj)
+ , (gfloat)wcd->vals->flameWidthBase);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->flameWidthTop_spinbutton_adj)
+ , (gfloat)wcd->vals->flameWidthTop);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->flameBorder_spinbutton_adj)
+ , (gfloat)wcd->vals->flameBorder);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->flameOffestX_spinbutton_adj)
+ , (gfloat)wcd->vals->flameOffestX);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->shiftPhaseY_spinbutton_adj)
+ , (gfloat)wcd->vals->shiftPhaseY);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->scaleX_spinbutton_adj)
+ , (gfloat)wcd->vals->scalex);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->scaleY_spinbutton_adj)
+ , (gfloat)wcd->vals->scaley);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->detail_spinbutton_adj)
+ , (gfloat)wcd->vals->detail);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->seed1_spinbutton_adj)
+ , (gfloat)wcd->vals->seed1);
+
+
+
+ /* init checkbuttons */
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->createImageCheckbutton)
+ , wcd->vals->createImage);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->patternCheckbutton)
+ , wcd->createNewPatternDefault);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->createFireLayerCheckbutton)
+ , wcd->vals->createFireLayer);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->useTransparentBgCheckbutton)
+ , wcd->vals->useTransparentBg);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->reverseGradientCheckbutton)
+ , wcd->vals->reverseGradient);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->useTrapezoidShapeCheckbutton)
+ , wcd->vals->useTrapezoidShape);
+
+
+ /* init radiobuttons */
+ if(wcd->vals->blendNum == BLEND_NUM_BURN)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (wcd->radio_blend_burn), TRUE);
+ }
+ else if (wcd->vals->blendNum == BLEND_NUM_SUBTRACT)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (wcd->radio_blend_subtract), TRUE);
+ }
+ else if (wcd->vals->blendNum == BLEND_NUM_MULTIPLY)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (wcd->radio_blend_multiply), TRUE);
+ }
+
+ countClouds = 0;
+ if(gimp_drawable_is_valid(wcd->existingCloud1Id))
+ {
+ countClouds++;
+ }
+ if(gimp_drawable_is_valid(wcd->existingFireShapeLayerId))
+ {
+ countClouds++;
+ }
+
+ if(countClouds == 2)
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (wcd->cloud1Combo), wcd->existingCloud1Id);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (wcd->fireShapeCombo), wcd->existingFireShapeLayerId);
+ }
+
+ gimp_gradient_select_button_set_gradient (GIMP_GRADIENT_SELECT_BUTTON (wcd->gradient_select),
+ &wcd->vals->gradientName[0]);
+
+} /* end p_init_widget_values */
+
+
+/* --------------------------------------
+ * on_gboolean_button_update
+ * --------------------------------------
+ */
+static void
+on_gboolean_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ FirePatternDialog *wcd;
+ gint *toggle_val = (gint *) data;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ *toggle_val = TRUE;
+ }
+ else
+ {
+ *toggle_val = FALSE;
+ }
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+
+ wcd = (FirePatternDialog *) g_object_get_data (G_OBJECT (widget), "wcd");
+ if(wcd != NULL)
+ {
+ p_update_widget_sensitivity (wcd);
+ }
+}
+
+
+/* ---------------------------------
+ * p_firepattern_response
+ * ---------------------------------
+ */
+static void
+p_firepattern_response (GtkWidget *widget,
+ gint response_id,
+ FirePatternDialog *wcd)
+{
+ GtkWidget *dialog;
+
+ switch (response_id)
+ {
+ case GAP_FIREPATTERN_RESPONSE_RESET:
+ if(wcd)
+ {
+ /* rset default values */
+ p_init_default_cuvals_without_cloud_layers(wcd->vals);
+ p_init_widget_values(wcd);
+ p_update_widget_sensitivity (wcd);
+ }
+ break;
+
+ case GTK_RESPONSE_OK:
+ if(wcd)
+ {
+ if (GTK_WIDGET_VISIBLE (wcd->shell))
+ {
+ gtk_widget_hide (wcd->shell);
+ }
+ wcd->run = TRUE;
+ }
+
+ default:
+ dialog = NULL;
+ if(wcd)
+ {
+ dialog = wcd->shell;
+ if(dialog)
+ {
+ wcd->shell = NULL;
+ gtk_widget_destroy (dialog);
+ }
+ }
+ gtk_main_quit ();
+ break;
+ }
+} /* end p_firepattern_response */
+
+
+/* --------------------------------------
+ * p_gradient_changed_callback
+ * --------------------------------------
+ *
+ */
+static void
+p_gradient_changed_callback (GimpGradientSelectButton *button,
+ const gchar *gradient_name,
+ gint width,
+ const gdouble *grad_data,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ FirePatternDialog *wcd;
+
+ wcd = (FirePatternDialog *) user_data;
+
+ if(wcd == NULL)
+ {
+ return;
+ }
+ if(wcd->vals == NULL)
+ {
+ return;
+ }
+
+ g_snprintf (wcd->vals->gradientName, MAX_GRADIENT_NAME -1, "%s", gradient_name);
+
+} /* end p_gradient_changed_callback */
+
+
+/* ------------------------------
+ * p_cloud_layer_menu_callback
+ * ------------------------------
+ *
+ */
+static void
+p_cloud_layer_menu_callback(GtkWidget *widget, gint32 *cloudLayerId)
+{
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+
+ if(gap_debug)
+ {
+ printf("p_cloud_layer_menu_callback: cloudLayerAddr:%d value:%d\n"
+ ,(int)cloudLayerId
+ ,(int)value
+ );
+ }
+
+ if(cloudLayerId != NULL)
+ {
+ *cloudLayerId = value;
+ }
+
+
+} /* end p_cloud_layer_menu_callback */
+
+
+/* ------------------------------
+ * p_pattern_layer_constrain
+ * ------------------------------
+ *
+ */
+static gint
+p_pattern_layer_constrain(gint32 image_id, gint32 drawable_id, FirePatternDialog *wcd)
+{
+ gint32 processedImageId;
+
+ if(gap_debug)
+ {
+ printf("p_pattern_layer_constrain PROCEDURE image_id:%d drawable_id:%d wcd:%d\n"
+ ,(int)image_id
+ ,(int)drawable_id
+ ,(int)wcd
+ );
+ }
+
+ 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);
+ }
+
+ if(gimp_drawable_is_valid(drawable_id) != TRUE)
+ {
+ return(FALSE);
+ }
+
+ processedImageId = gimp_drawable_get_image(wcd->drawable_id);
+
+ if(image_id == processedImageId)
+ {
+ return (FALSE);
+ }
+
+ if(!gimp_drawable_is_rgb(drawable_id))
+ {
+ if(!gimp_drawable_is_gray(drawable_id))
+ {
+ return (FALSE);
+ }
+ }
+
+ wcd->countPotentialCloudLayers++;
+
+ return(TRUE);
+
+} /* end p_pattern_layer_constrain */
+
+
+/* ------------------------------
+ * do_dialog
+ * ------------------------------
+ * create and show the dialog window
+ */
+static FirePatternDialog *
+do_dialog (FirePatternDialog *wcd, firepattern_val_t *cuvals)
+{
+ GtkWidget *vbox;
+
+ GtkWidget *dialog1;
+ GtkWidget *dialog_vbox1;
+ GtkWidget *frame1;
+ GtkWidget *vbox1;
+ GtkWidget *label;
+ GSList *vbox1_group = NULL;
+ GtkWidget *radiobutton;
+ GtkWidget *table1;
+ GtkObject *spinbutton_adj;
+ GtkWidget *spinbutton;
+ GtkWidget *dialog_action_area1;
+ GtkWidget *checkbutton;
+ GtkWidget *combo;
+ gint countClouds;
+ gint row;
+
+
+ /* Init UI */
+ gimp_ui_init ("firepattern", FALSE);
+
+
+ /* The dialog1 */
+ wcd->run = FALSE;
+ wcd->vals = cuvals;
+ wcd->createNewPatternDefault = TRUE;
+ wcd->createNewShapeDefault = wcd->vals->forceShapeLayer;
+ wcd->existingCloud1Id = -1;
+ wcd->existingFireShapeLayerId = -1;
+
+ wcd->countPotentialCloudLayers = 0;
+ if(gimp_drawable_is_valid(cuvals->cloudLayer1))
+ {
+ wcd->existingCloud1Id = cuvals->cloudLayer1;
+ wcd->createNewPatternDefault = FALSE;
+ }
+ if(gimp_drawable_is_valid(cuvals->fireShapeLayer))
+ {
+ wcd->existingFireShapeLayerId = cuvals->fireShapeLayer;
+ }
+ else
+ {
+ wcd->createNewShapeDefault = TRUE;
+ wcd->vals->forceShapeLayer = TRUE;
+ }
+
+
+ wcd->createNewPattern = wcd->createNewPatternDefault;
+
+ /* The dialog1 and main vbox */
+ dialog1 = gimp_dialog_new (_("Fire-Pattern"), "fire_pattern",
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_HELP_ID,
+
+ GIMP_STOCK_RESET, GAP_FIREPATTERN_RESPONSE_RESET,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ wcd->shell = dialog1;
+
+
+ /*
+ * g_object_set_data (G_OBJECT (dialog1), "dialog1", dialog1);
+ * gtk_window_set_title (GTK_WINDOW (dialog1), _("dialog1"));
+ */
+
+
+ g_signal_connect (G_OBJECT (dialog1), "response",
+ G_CALLBACK (p_firepattern_response),
+ wcd);
+
+ /* the vbox */
+ vbox = gtk_vbox_new (FALSE, 2);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog1)->vbox), vbox,
+ TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ dialog_vbox1 = GTK_DIALOG (dialog1)->vbox;
+ g_object_set_data (G_OBJECT (dialog1), "dialog_vbox1", dialog_vbox1);
+ gtk_widget_show (dialog_vbox1);
+
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Animation options"));
+
+ gtk_widget_show (frame1);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+ gtk_widget_show (vbox1);
+
+
+ /* table1 for spinbuttons */
+ table1 = gtk_table_new (4, 2, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+
+ row = 0;
+
+ /* createImage checkbutton */
+ label = gtk_label_new (_("Create Image:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->createImageCheckbutton = checkbutton;
+ gtk_widget_show (checkbutton);
+ gimp_help_set_help_data (checkbutton, _("ON: create a new image with n copies of the input drawable"
+ " and render complete animation effect on those copies. "
+ "OFF: render only one phase of the animation effect on "
+ "the input drawable"), NULL);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->createImage);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->createImage);
+
+ row++;
+
+ label = gtk_label_new (_("N-Frames:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ /* nframes spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->nframes, 1.0, 500, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0);
+ gimp_help_set_help_data (spinbutton, _("Number of frames to be rendered as layer in the newly created image."), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_int_adjustment_update), &cuvals->nframes);
+ wcd->nframes_spinbutton_adj = spinbutton_adj;
+ wcd->nframes_spinbutton = spinbutton;
+
+ row++;
+
+
+
+ /* shiftPhaseY spinbutton */
+ label = gtk_label_new (_("Phase shift"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ spinbutton_adj = gtk_adjustment_new (cuvals->shiftPhaseY, 0.0, 100, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Vertical shift phase where 1.0 refers to image height"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->shiftPhaseY);
+ wcd->shiftPhaseY_spinbutton_adj = spinbutton_adj;
+ wcd->shiftPhaseY_spinbutton = spinbutton;
+
+
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Pattern options"));
+
+ gtk_widget_show (frame1);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+ gtk_widget_show (vbox1);
+
+
+
+ /* table1 for spinbuttons */
+ table1 = gtk_table_new (4, 2, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+
+
+ row = 0;
+ /* use existing Patterns checkbutton */
+ label = gtk_label_new (_("Create Pattern:"));
+ gtk_widget_show (label);
+ wcd->patternLabel = label;
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->patternCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("ON: create firepattern cloud layer according to options. OFF: Use external pattern layer. "), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), wcd->createNewPattern);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &wcd->createNewPattern);
+
+ /* stretchHeight spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->stretchHeight, 0.0, 3.0, 0.1, 0.1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 3);
+ gimp_help_set_help_data (spinbutton, _("vertical stretch factor for the fire pattern"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->stretchHeight);
+ wcd->stretchHeight_spinbutton_adj = spinbutton_adj;
+ wcd->stretchHeight_spinbutton = spinbutton;
+
+ row++;
+
+
+ /* scalex spinbutton */
+ label = gtk_label_new (_("Scale Pattern X:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->scalex, 0.1, 16, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Horizontal scaling of the random patterns that are created for rendering (cloud layer)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->scalex);
+ wcd->scaleX_spinbutton_adj = spinbutton_adj;
+ wcd->scaleX_spinbutton = spinbutton;
+
+
+ label = gtk_label_new (_("Y:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->scaley, 0.1, 16, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Vertical scaling of the random patterns that are created for rendering (cloud layer)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->scaley);
+ wcd->scaleY_spinbutton_adj = spinbutton_adj;
+ wcd->scaleY_spinbutton = spinbutton;
+
+
+
+ row++;
+
+ label = gtk_label_new (_("Seed Pattern:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ /* seed1 spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->seed1, 0.0, 2147483647, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0);
+ gimp_help_set_help_data (spinbutton, _("Seed for creating random pattern (cloud1 layer) use 0 for random value."), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_int_adjustment_update), &cuvals->seed1);
+ wcd->seed1_spinbutton_adj = spinbutton_adj;
+ wcd->seed1_spinbutton = spinbutton;
+
+
+
+ label = gtk_label_new (_("Detail:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ /* detail spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->detail, 0.0, 15.0, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0);
+ gimp_help_set_help_data (spinbutton, _("Detail level for creating random pattern (cloud layer)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_int_adjustment_update), &cuvals->detail);
+ wcd->detail_spinbutton_adj = spinbutton_adj;
+ wcd->detail_spinbutton = spinbutton;
+
+ row++;
+
+ /* pattern */
+ label = gtk_label_new (_("Layer Pattern:"));
+ wcd->cloud1Label = label;
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ combo = gimp_layer_combo_box_new (p_pattern_layer_constrain, wcd);
+ wcd->cloud1Combo = combo;
+ gtk_widget_show(combo);
+ gtk_table_attach(GTK_TABLE(table1), combo, 1, 4, row, row+1,
+ GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+ gimp_help_set_help_data(combo,
+ _("Select an already existing pattern layer (from previous run)")
+ , NULL);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ wcd->existingCloud1Id, /* initial value */
+ G_CALLBACK (p_cloud_layer_menu_callback),
+ &wcd->existingCloud1Id);
+
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Fireshape options"));
+
+ gtk_widget_show (frame1);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+ gtk_widget_show (vbox1);
+
+
+
+ /* table1 for spinbuttons */
+ table1 = gtk_table_new (4, 2, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+ row = 0;
+ label = gtk_label_new (_("Create Fireshape:"));
+ gtk_widget_show (label);
+ wcd->shapeLabel = label;
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->shapeCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("ON: create fire shape layer according to options. OFF: Use external fire shape layer. "), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), wcd->vals->forceShapeLayer);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &wcd->vals->forceShapeLayer);
+
+
+ /* useTrapezoidShape checkbutton */
+ label = gtk_label_new (_("Trapezoid:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->useTrapezoidShapeCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("ON: Render trapezoid shaped fire, OFF: render fire at full image width"), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 3, 4, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->useTrapezoidShape);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->useTrapezoidShape);
+
+
+
+ row++;
+
+ /* flameHeight spinbutton */
+ label = gtk_label_new (_("Flame Height:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->flameHeight, 0.1, 4.0, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Height of the flame (1.0 refers to full image height)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->flameHeight);
+ wcd->flameHeight_spinbutton_adj = spinbutton_adj;
+ wcd->flameHeight_spinbutton = spinbutton;
+
+
+ /* flameBorder spinbutton */
+ label = gtk_label_new (_("Flame Border:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->flameBorder, 0.0, 0.5, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("border of the flame"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->flameBorder);
+ wcd->flameBorder_spinbutton_adj = spinbutton_adj;
+ wcd->flameBorder_spinbutton = spinbutton;
+
+
+ row++;
+
+ /* flameWidth checkbuttons */
+ label = gtk_label_new (_("FlameWidth:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->flameWidthBase, 0.0, 1.0, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("width of the flame at base line (1.0 for full image width)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->flameWidthBase);
+ wcd->flameWidthBase_spinbutton_adj = spinbutton_adj;
+ wcd->flameWidthBase_spinbutton = spinbutton;
+
+ label = gtk_label_new (_("Top:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->flameWidthTop, 0.0, 1.0, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("width of the flame at flame height (1.0 for full image width)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->flameWidthTop);
+ wcd->flameWidthTop_spinbutton_adj = spinbutton_adj;
+ wcd->flameWidthTop_spinbutton = spinbutton;
+
+ row++;
+
+ /* flameOffestX spinbutton */
+ label = gtk_label_new (_("Flame Center:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->flameOffestX, -1.0, 1.0, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("horizontal offset of the flame center (0 for center, -0.5 left border +0.5 at right border of the image)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->flameOffestX);
+ wcd->flameOffestX_spinbutton_adj = spinbutton_adj;
+ wcd->flameOffestX_spinbutton = spinbutton;
+
+ row++;
+
+
+ label = gtk_label_new (_("Fire Shape:"));
+ gtk_widget_show (label);
+ wcd->fireShapeLabel = label;
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ combo = gimp_layer_combo_box_new (p_pattern_layer_constrain, wcd);
+ wcd->fireShapeCombo = combo;
+ gtk_widget_show(combo);
+ gtk_table_attach(GTK_TABLE(table1), combo, 1, 4, row, row+1,
+ GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+ gimp_help_set_help_data(combo,
+ _("Select an already existing fire shape layer (from previous run)")
+ , NULL);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ wcd->existingFireShapeLayerId, /* initial value */
+ G_CALLBACK (p_cloud_layer_menu_callback),
+ &wcd->existingFireShapeLayerId);
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Render options"));
+
+ gtk_widget_show (frame1);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+ gtk_widget_show (vbox1);
+
+
+
+ /* table1 for spinbuttons */
+ table1 = gtk_table_new (4, 2, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+
+
+ row = 0;
+
+ /* createFireLayer checkbutton */
+ label = gtk_label_new (_("Create FireLayer:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->createFireLayerCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("ON: Render fire pattern effect as separate layer, OFF: merge rendered effect onto processed layer"), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->createFireLayer);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->createFireLayer);
+
+
+
+ row++;
+
+ /* Highlights blend mode */
+ label = gtk_label_new (_("Blend Mode:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+
+ gtk_widget_show (vbox1);
+ gtk_table_attach (GTK_TABLE (table1), vbox1, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+
+ /* Blend Mode the radio buttons */
+
+ radiobutton = gtk_radio_button_new_with_label (vbox1_group, _("Burn"));
+ wcd->radio_blend_burn = radiobutton;
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
+ gtk_widget_show (radiobutton);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (radiobutton), "clicked", G_CALLBACK (on_blend_radio_callback), wcd);
+ if(wcd->vals->blendNum == BLEND_NUM_BURN)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (radiobutton), TRUE);
+ }
+
+ radiobutton = gtk_radio_button_new_with_label (vbox1_group, _("Subtract"));
+ wcd->radio_blend_subtract = radiobutton;
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
+ gtk_widget_show (radiobutton);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (radiobutton), "clicked", G_CALLBACK (on_blend_radio_callback), wcd);
+ if(wcd->vals->blendNum == BLEND_NUM_SUBTRACT)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (radiobutton), TRUE);
+ }
+
+ radiobutton = gtk_radio_button_new_with_label (vbox1_group, _("Multiply"));
+ wcd->radio_blend_multiply = radiobutton;
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
+ gtk_widget_show (radiobutton);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (radiobutton), "clicked", G_CALLBACK (on_blend_radio_callback), wcd);
+ if(wcd->vals->blendNum == BLEND_NUM_MULTIPLY)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (radiobutton), TRUE);
+ }
+
+
+
+ row++;
+
+ /* useTransparentBg checkbutton */
+ label = gtk_label_new (_("Transparent BG:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->useTransparentBgCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("ON: Render fire layer with transparent background, OFF: render with black background"), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->useTransparentBg);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->useTransparentBg);
+
+ label = gtk_label_new (_("Opacity:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ /* fireOpacity spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->fireOpacity, 0.0, 100, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 3);
+ gimp_help_set_help_data (spinbutton, _("The opacity of the flames"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->fireOpacity);
+ wcd->fireOpacity_spinbutton_adj = spinbutton_adj;
+ wcd->fireOpacity_spinbutton = spinbutton;
+
+ row++;
+
+ /* reverseGradient checkbutton */
+ label = gtk_label_new (_("Reverse Gradient:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->reverseGradientCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("ON: use reverse gradient colors, OFF: use gradient colors"), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->reverseGradient);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->reverseGradient);
+
+ /* the gradient select button */
+ {
+ GtkWidget *gradientButton;
+
+ gradientButton = gimp_gradient_select_button_new ("Gradient", &cuvals->gradientName[0]);
+ wcd->gradient_select = gradientButton;
+ g_signal_connect (gradientButton, "gradient-set",
+ G_CALLBACK (p_gradient_changed_callback), wcd);
+ gtk_widget_show (gradientButton);
+ gtk_table_attach( GTK_TABLE(table1), gradientButton, 2, 4, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ }
+
+ /* -- */
+
+
+ dialog_action_area1 = GTK_DIALOG (dialog1)->action_area;
+ gtk_widget_show (dialog_action_area1);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area1), 10);
+
+ p_init_widget_values(wcd);
+ p_update_widget_sensitivity (wcd);
+
+ gtk_widget_show (dialog1);
+
+ gtk_main ();
+ gdk_flush ();
+
+ if((!wcd->createNewPattern) && (wcd->countPotentialCloudLayers > 0))
+ {
+ wcd->vals->cloudLayer1 = wcd->existingCloud1Id;
+ }
+ else
+ {
+ /* use -1 to force creation of new cloud layer */
+ wcd->vals->cloudLayer1 = -1;
+ }
+
+ if(!wcd->vals->forceShapeLayer)
+ {
+ wcd->vals->fireShapeLayer = wcd->existingFireShapeLayerId;
+ }
+ else
+ {
+ /* use -1 to force creation of new fireShapeLayer */
+ wcd->vals->fireShapeLayer = -1;
+ }
+
+ return wcd;
+
+} /* end do_dialog */
+
+
+
+
+
+MAIN ()
+
+/* --------------------------------------
+ * query
+ * --------------------------------------
+ *
+ */
+static void
+query (void)
+{
+ static GimpParamDef args[] = {
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (must be a layer without layermask)"},
+ { GIMP_PDB_FLOAT, "scalex", "Horizontal scale (0.1 upto 16.0)"},
+ { GIMP_PDB_FLOAT, "scaley", "Vertical scale (0.1 upto 16.0)"},
+ { GIMP_PDB_INT32, "detail", "detail level for creating cloud pattern (1 upto 15)"},
+ { GIMP_PDB_INT32, "useTrapezoidShape", "TRUE (1): trapezoid fire shape, FALSE (0) rectangular shape)"},
+ { GIMP_PDB_FLOAT, "flameHeight", "height of the flame (0.1 upto 4.0) where 1.0 is full image height"},
+ { GIMP_PDB_FLOAT, "flameWidthBase", "width of the flame at the baseline (0.1 upto 1.0) where 1.0 is full image width"},
+ { GIMP_PDB_FLOAT, "flameWidthTop", "width of the flame at fireHeight (0.1 upto 1.0) where 1.0 is full image width"},
+ { GIMP_PDB_FLOAT, "flameBorder", "fading of the fire shape on left/right border (0.1 upto 0.5) where 0.5 is half image width"},
+ { GIMP_PDB_FLOAT, "flameOffestX", "horizontal offset of the flame center (0 for center, -0.5 left border +0.5 at right border of the image)"},
+ { GIMP_PDB_INT32, "blendNum", "Blend mode { BURN (0), SUBTRACT (1), MULTIPLY (2) }"},
+ { GIMP_PDB_FLOAT, "stretchHeight", "vertical stretch factor for flame pattern (1.0 to 3.0)"},
+ { GIMP_PDB_FLOAT, "shiftPhaseY", "Vertical shift phase (0.0 to 1.0 full height, values >= 1.0 foldback to range 0.0 - 0.99999)"},
+ { GIMP_PDB_INT32, "createImage", "TRUE (1): create a new image with n copies of the input drawable and render complete animation effect on those copies. FALSE (0): render only one phase of the anim effect on the input drawable"},
+ { GIMP_PDB_INT32, "nframes", "number of layer to be created in case createImage option is TRUE (1)"},
+ { GIMP_PDB_INT32, "createFireLayer", "TRUE (1): create a new image with n copies of the input drawable and render complete animation effect on those copies. FALSE (0): render only one phase of the anim effect on the input drawable"},
+ { GIMP_PDB_INT32, "useTransparentBg", "TRUE (1): create a new image with n copies of the input drawable and render complete animation effect on those copies. FALSE (0): render only one phase of the anim effect on the input drawable"},
+ { GIMP_PDB_FLOAT, "fireOpacity", "fire layer opacity in percent (0.0 to 100.0)"},
+ { GIMP_PDB_INT32, "seed1", "Seed for creating random pattern (cloud1 layer) use 0 for random value"},
+ { GIMP_PDB_DRAWABLE, "cloudLayer1", "-1 automatically create cloud layer1 that is required to render or pass a layer id that was returned in a previous call to this plug-in (for rendering the next phase of the effect in one call per frame manner)"},
+ { GIMP_PDB_DRAWABLE, "fireShapeLayer", "-1 automatically create fire shape layer that is required to render or pass a layer id that was returned in a previous call to this plug-in (for rendering the next phase of the effect in one call per frame manner)"},
+ { GIMP_PDB_INT32, "reverseGradient", "TRUE (1) use reversed gradient, FALSE (0) use gradient as it is"},
+ { GIMP_PDB_STRING, "gradientName", "Name of the gradient to be used to colorize the firepattern"}
+ };
+ static int nargs = sizeof(args) / sizeof(args[0]);
+
+ static GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_DRAWABLE, "imageId", "the image that was newly created (when creatImage is TRUE) or otherwise the image id tha contains the processed drawable" },
+ { GIMP_PDB_DRAWABLE, "cloudLayer", "the pattern cloud layer" },
+ { GIMP_PDB_DRAWABLE, "fireShapeLayer", "the fire shape layer" }
+ };
+ static int nreturn_vals = sizeof(return_vals) / sizeof(return_vals[0]);
+
+
+ static firepattern_val_t firevals;
+
+ static GimpLastvalDef lastvals[] =
+ {
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_FALSE, firevals.scalex, "scalex"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_FALSE, firevals.scaley, "scaley"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, firevals.detail, "detail"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, firevals.useTrapezoidShape, "useTrapezoidShape"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.flameHeight, "flameHeight"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.flameWidthBase, "flameWidthBase"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.flameWidthTop, "flameWidthTop"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.flameBorder, "flameBorder"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.flameOffestX, "flameOffestX"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, firevals.blendNum, "blendNum"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.stretchHeight, "stretchHeight"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.shiftPhaseY, "shiftPhaseY"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, firevals.createImage, "createImage"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, firevals.nframes, "nframes"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, firevals.createFireLayer, "createFireLayer"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, firevals.useTransparentBg, "useTransparentBg"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, firevals.fireOpacity, "fireOpacity"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, firevals.seed1, "seed1"),
+ GIMP_LASTVALDEF_DRAWABLE (GIMP_ITER_TRUE, firevals.cloudLayer1, "cloudLayer1"),
+ GIMP_LASTVALDEF_DRAWABLE (GIMP_ITER_TRUE, firevals.fireShapeLayer, "fireShapeLayer"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, firevals.forceShapeLayer, "forceShapeLayer"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, firevals.reverseGradient, "reverseGradient"),
+ GIMP_LASTVALDEF_ARRAY (GIMP_ITER_FALSE, firevals.gradientName, "gradientNameArray"),
+ GIMP_LASTVALDEF_GCHAR (GIMP_ITER_FALSE, firevals.gradientName[0], "gradientNameChar"),
+ };
+
+ gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+ /* registration for last values buffer structure (for animated filter apply) */
+ gimp_lastval_desc_register(PLUG_IN_NAME,
+ &firevals,
+ sizeof(firevals),
+ G_N_ELEMENTS (lastvals),
+ lastvals);
+
+ /* the actual installation of the firepattern plugin */
+ gimp_install_procedure (PLUG_IN_NAME,
+ PLUG_IN_DESCRIPTION,
+ "This Plugin generates an animated fire effect."
+ " This Plugin can render the animation in one call, where the input drawable is copied n-times to a newly created image. "
+ " Optional this Plugin can be called to render just one frame/phase of the animated effect."
+ " This type of animated call on multiple already existent layers (or frames) with varying shiftPhase parameter"
+ " is supported by the GIMP-GAP filter all layers or by applying as filter via GIMP-GAP modify frames feature. "
+ " Note that the render one frame per call style offers more flexibility where you can "
+ " apply the flame with varying shape, color and opacity for each rendered frame atomatically. "
+ ,
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ GAP_VERSION_WITH_DATE,
+ N_("Fire Pattern..."),
+ PLUG_IN_IMAGE_TYPES,
+ GIMP_PLUGIN,
+ nargs,
+ nreturn_vals,
+ args,
+ return_vals);
+
+
+
+ {
+ /* Menu names */
+ const char *menupath_image_video_layer_colors = N_("<Image>/Video/Layer/Render/");
+
+ gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_video_layer_colors);
+ }
+
+} /* end query */
+
+
+/* --------------------------------------
+ * run
+ * --------------------------------------
+ */
+static void
+run(const gchar *name
+ , gint nparams
+ , const GimpParam *param
+ , gint *nreturn_vals
+ , GimpParam **return_vals)
+{
+ firepattern_val_t l_cuvals;
+ FirePatternDialog *wcd = NULL;
+ firepattern_context_t firepattern_context;
+ firepattern_context_t *ctxt;
+
+ gint32 l_image_id = -1;
+ gint32 l_drawable_id = -1;
+ gint32 l_handled_drawable_id = -1;
+
+
+
+ /* Get the runmode from the in-parameters */
+ GimpRunMode run_mode = param[0].data.d_int32;
+
+ ctxt = &firepattern_context;
+ ctxt->run_mode = run_mode;
+
+ /* status variable, use it to check for errors in invocation usualy only
+ during non-interactive calling */
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ /*always return at least the status to the caller. */
+ static GimpParam values[N_RET_VALUES];
+
+
+ INIT_I18N();
+
+ /* initialize the return of the status */
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_DRAWABLE;
+ values[1].data.d_int32 = -1;
+ values[2].type = GIMP_PDB_DRAWABLE;
+ values[2].data.d_int32 = -1;
+ values[3].type = GIMP_PDB_DRAWABLE;
+ values[3].data.d_int32 = -1;
+ *nreturn_vals = N_RET_VALUES;
+ *return_vals = values;
+
+
+ /* get image and drawable */
+ l_image_id = param[1].data.d_int32;
+ l_drawable_id = param[2].data.d_drawable;
+
+ /* Possibly retrieve data from a previous run, otherwise init with default values */
+ p_init_cuvals(&l_cuvals);
+
+ if(gap_debug)
+ {
+ printf("Run Firepattern on l_drawable_id:%d (%s)\n"
+ ,(int)l_drawable_id
+ ,p_drawable_get_name(l_drawable_id)
+ );
+ }
+
+ if(status == GIMP_PDB_SUCCESS)
+ {
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ wcd = g_malloc (sizeof (FirePatternDialog));
+ wcd->run = FALSE;
+ wcd->drawable_id = l_drawable_id;
+ /* Get information from the dialog */
+ do_dialog(wcd, &l_cuvals);
+ wcd->show_progress = TRUE;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* check to see if invoked with the correct number of parameters */
+ if (nparams == 25)
+ {
+
+ l_cuvals.scalex = param[3].data.d_float;
+ l_cuvals.scalex = param[4].data.d_float;
+ l_cuvals.detail = param[5].data.d_int32;
+ l_cuvals.useTrapezoidShape = param[6].data.d_int32;
+ l_cuvals.flameHeight = param[7].data.d_float;
+ l_cuvals.flameWidthBase = param[8].data.d_float;
+ l_cuvals.flameWidthTop = param[9].data.d_float;
+ l_cuvals.flameBorder = param[10].data.d_float;
+ l_cuvals.flameOffestX = param[11].data.d_float;
+ l_cuvals.blendNum = param[12].data.d_int32;
+ l_cuvals.stretchHeight = param[13].data.d_float;
+ l_cuvals.shiftPhaseY = param[14].data.d_float;
+ l_cuvals.createImage = param[15].data.d_int32;
+ l_cuvals.nframes = param[16].data.d_int32;
+ l_cuvals.createFireLayer = param[17].data.d_int32;
+ l_cuvals.useTransparentBg = param[18].data.d_int32;
+ l_cuvals.fireOpacity = param[19].data.d_float;
+ l_cuvals.seed1 = param[20].data.d_int32;
+ l_cuvals.cloudLayer1 = param[21].data.d_drawable;
+ l_cuvals.fireShapeLayer = param[22].data.d_drawable;
+ l_cuvals.reverseGradient = param[23].data.d_int32;
+
+ l_cuvals.gradientName[0] = '\0';
+ if(param[24].data.d_string != NULL)
+ {
+ g_snprintf(l_cuvals.gradientName, MAX_GRADIENT_NAME -1, "%s", param[23].data.d_string);
+ }
+ if(l_cuvals.gradientName[0] == '\0')
+ {
+ g_snprintf (l_cuvals.gradientName, MAX_GRADIENT_NAME -1, "%s", DEFAULT_GRADIENT_NAME);
+ }
+
+
+
+ p_check_for_valid_cloud_layers(&l_cuvals);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ wcd = g_malloc (sizeof (FirePatternDialog));
+ wcd->run = TRUE;
+ wcd->show_progress = TRUE;
+ wcd->createNewPattern = FALSE;
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_NAME, &l_cuvals);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (wcd == NULL)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Run the main function */
+ if(wcd->run)
+ {
+ gboolean success;
+
+ gimp_image_undo_group_start (l_image_id);
+ success = p_run_renderFirePattern(l_drawable_id, &l_cuvals, ctxt);
+ l_handled_drawable_id = l_drawable_id;
+ gimp_image_undo_group_end (l_image_id);
+
+ if(success)
+ {
+ /* Store variable states for next run */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ gimp_set_data(PLUG_IN_NAME, &l_cuvals, sizeof(l_cuvals));
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR; /* dialog ended with cancel button */
+ }
+
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR; /* dialog ended with cancel button */
+ }
+
+ /* If run mode is interactive, flush displays, else (script) don't
+ do it, as the screen updates would make the scripts slow */
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ gimp_displays_flush ();
+ }
+ }
+ values[0].data.d_status = status;
+ values[1].data.d_int32 = ctxt->image_id; /* return the image id of handled layer (or of the newly created anim image */
+ values[2].data.d_int32 = l_cuvals.cloudLayer1; /* return the id of cloud1 layer */
+ values[3].data.d_int32 = l_cuvals.fireShapeLayer; /* return the id of fire shape layer */
+
+} /* end run */
diff --git a/gap/gap_fmac_base.c b/gap/gap_fmac_base.c
index d839cc1..0e1c44f 100644
--- a/gap/gap_fmac_base.c
+++ b/gap/gap_fmac_base.c
@@ -596,6 +596,117 @@ p_fmac_execute_single_filter(GimpRunMode run_mode, gint32 image_id, gint32 drawa
} /* end p_fmac_execute_single_filter */
+/* ---------------------------------
+ * p_find_layer_for_next_processing
+ * ---------------------------------
+ * find a layer in the image that was newly created (e.g is not
+ * found in the old_layers_list snaphaot taken of the layerstack
+ * before the processing step)
+ * If there are more such new layers present, then pick the one
+ * that has same position as the ref_layer_id had in the old_layers_list
+ */
+static gint32
+p_find_layer_for_next_processing(gint32 image_id, gint32 ref_layer_id
+ , gint32 *old_layers_list, gint old_nlayers)
+{
+ gint l_nlayers;
+ gint32 *l_layers_list;
+ gint32 l_idx;
+ gint32 l_old_stackposition;
+ gint32 l_relevantLayer;
+
+ if(old_layers_list == NULL)
+ {
+ return (-1);
+ }
+
+ if(gap_debug)
+ {
+ printf("OLD_layerstack:");
+ for(l_idx = 0; l_idx < old_nlayers; l_idx++)
+ {
+ printf(" ID(%d):%d"
+ ,(int)l_idx
+ ,(int)old_layers_list[l_idx]
+ );
+ }
+ printf("\n");
+ }
+
+
+ for(l_idx = 0; l_idx < old_nlayers; l_idx++)
+ {
+ if(old_layers_list[l_idx] == ref_layer_id)
+ {
+ l_old_stackposition = l_idx;
+ break;
+ }
+ }
+
+
+ l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+ l_relevantLayer = -1;
+
+ if(l_layers_list != NULL)
+ {
+ if(gap_debug)
+ {
+ printf("NEW_layerstack:");
+ for(l_idx = 0; l_idx < l_nlayers; l_idx++)
+ {
+ printf(" ID(%d):%d"
+ ,(int)l_idx
+ ,(int)l_layers_list[l_idx]
+ );
+ }
+ printf("\n");
+ }
+
+
+ for(l_idx = 0; l_idx < l_nlayers; l_idx++)
+ {
+ gint32 l_ii;
+ gboolean l_found;
+
+ l_found = FALSE;
+ for(l_ii = 0; l_ii < l_nlayers; l_ii++)
+ {
+ if (l_layers_list[l_idx] == old_layers_list[l_ii])
+ {
+ l_found = TRUE;
+ break;
+ }
+ }
+
+ if(l_found == FALSE)
+ {
+ /* we found a new layer that was created during last filtercall */
+ l_relevantLayer = l_layers_list[l_idx];
+ if(l_idx == l_old_stackposition)
+ {
+ /* the new layer has same stackposition */
+ break;
+ }
+ }
+ }
+ g_free (l_layers_list);
+ }
+
+ if(gap_debug)
+ {
+ printf("FIND_LAYER(2): ref_layer_id:%d l_old_stackposition:%d l_relevantLayer:%d\n"
+ ,(int)ref_layer_id
+ ,(int)l_old_stackposition
+ ,(int)l_relevantLayer
+ );
+ }
+
+ return (l_relevantLayer);
+
+} /* end p_find_layer_for_next_processing */
+
+
+
/* ------------------------
* p_fmac_execute
* ------------------------
@@ -652,7 +763,9 @@ p_fmac_execute(GimpRunMode run_mode, gint32 image_id, gint32 drawable_id
FMacElem *fmac_elem;
GapFmacContext theFmacContext;
GapFmacContext *fmacContext;
+ gint32 l_drawable_id;
+ l_drawable_id = drawable_id;
fmacContext = &theFmacContext;
gap_fmct_setup_GapFmacContext(fmacContext
@@ -668,16 +781,44 @@ p_fmac_execute(GimpRunMode run_mode, gint32 image_id, gint32 drawable_id
}
for(fmac_elem = fmac_root; fmac_elem != NULL; fmac_elem = (FMacElem *)fmac_elem->next)
{
- p_fmac_execute_single_filter(run_mode, image_id, drawable_id
- , fmac_elem
- , current_step
- , total_steps
- );
+ gint l_nlayers;
+ gint32 *l_layers_list;
- /* intermediate cleanup of temporary image duplicates that may have been
- * created while iterating persistent drawable ids (by iterator sub-procedur p_delta_drawable)
- */
- gap_frame_fetch_delete_list_of_duplicated_images(fmacContext->ffetch_user_id);
+ l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+ if(l_layers_list != NULL)
+ {
+
+ if(gap_debug)
+ {
+ printf("p_fmac_execute: %s\n", fmac_elem->filtername);
+ }
+
+ p_fmac_execute_single_filter(run_mode, image_id, l_drawable_id
+ , fmac_elem
+ , current_step
+ , total_steps
+ );
+
+ /* intermediate cleanup of temporary image duplicates that may have been
+ * created while iterating persistent drawable ids (by iterator sub-procedur p_delta_drawable)
+ */
+ gap_frame_fetch_delete_list_of_duplicated_images(fmacContext->ffetch_user_id);
+
+ if(gimp_drawable_is_valid(l_drawable_id) != TRUE)
+ {
+ /* the filter ha made the processed drawable_id invalid
+ * (probably by merging with another layer)
+ * In this case we try to figure out the new layer id
+ * that can be used for the further filter calls to be done..
+ */
+ l_drawable_id = p_find_layer_for_next_processing(image_id
+ , l_drawable_id
+ , l_layers_list
+ , l_nlayers
+ );
+ }
+ g_free (l_layers_list);
+ }
}
/* disable the sessionwide filtermacro context */
diff --git a/gap/gap_layer_copy.c b/gap/gap_layer_copy.c
index f8a2fe4..c18de6a 100644
--- a/gap/gap_layer_copy.c
+++ b/gap/gap_layer_copy.c
@@ -78,7 +78,7 @@ gint32 gap_layer_copy_to_dest_image (gint32 dst_image_id,
l_name = gimp_drawable_get_name(src_layer_id);
/* copy the layer */
- l_new_layer_id = gap_pdb_gimp_layer_new_from_drawable(src_layer_id, dst_image_id);
+ l_new_layer_id = gimp_layer_new_from_drawable(src_layer_id, dst_image_id);
if(l_new_layer_id >= 0)
{
diff --git a/gap/gap_pdb_calls.c b/gap/gap_pdb_calls.c
index 41cd070..0bb3a40 100644
--- a/gap/gap_pdb_calls.c
+++ b/gap/gap_pdb_calls.c
@@ -254,42 +254,6 @@ gap_pdb_gimp_displays_reconnect(gint32 old_image_id, gint32 new_image_id)
-/* ============================================================================
- * gap_pdb_gimp_layer_new_from_drawable
- *
- * ============================================================================
- */
-
-gint32
-gap_pdb_gimp_layer_new_from_drawable(gint32 drawable_id, gint32 dst_image_id)
-{
- static char *l_called_proc = "gimp_layer_new_from_drawable";
- GimpParam *return_vals;
- int nreturn_vals;
- gint32 layer_id;
-
- return_vals = gimp_run_procedure (l_called_proc,
- &nreturn_vals,
- GIMP_PDB_DRAWABLE, drawable_id,
- GIMP_PDB_IMAGE, dst_image_id,
- GIMP_PDB_END);
-
- if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
- {
- layer_id = return_vals[1].data.d_int32;
- gimp_destroy_params(return_vals, nreturn_vals);
-
- return(layer_id); /* return the resulting layer_id */
- }
- gimp_destroy_params(return_vals, nreturn_vals);
- printf("GAP: Error: PDB call of %s failed, d_status:%d %s\n"
- , l_called_proc
- , (int)return_vals[0].data.d_status
- , gap_status_to_string(return_vals[0].data.d_status)
- );
-
- return(-1);
-} /* end gap_pdb_gimp_layer_new_from_drawable */
/* ============================================================================
* gap_pdb_gimp_file_save_thumbnail
@@ -416,3 +380,126 @@ workaround:
);
return(FALSE);
} /* end gap_pdb_gimp_image_thumbnail */
+
+
+
+
+/* --------------------------------------
+ * gap_pdb_call_displace
+ * --------------------------------------
+ *
+ */
+gboolean
+gap_pdb_call_displace(gint32 image_id, gint32 layer_id
+ ,gdouble amountX, gdouble amountY
+ ,gint32 doX, gint32 doY
+ ,gint32 mapXId, gint32 mapYId
+ ,gint32 displaceType)
+{
+ static char *l_called_proc = "plug-in-displace";
+ GimpParam *return_vals;
+ int nreturn_vals;
+
+ return_vals = gimp_run_procedure (l_called_proc,
+ &nreturn_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_id,
+ GIMP_PDB_DRAWABLE, layer_id, /* input drawable (to be processed) */
+ GIMP_PDB_FLOAT, amountX, /* Displace multiplier for X or radial direction */
+ GIMP_PDB_FLOAT, amountY, /* Displace multiplier for Y or tangent (degrees) direction */
+ GIMP_PDB_INT32, doX, /* Displace in X or radial direction? */
+ GIMP_PDB_INT32, doY, /* Displace in Y or tangential direction? */
+ GIMP_PDB_DRAWABLE, mapXId, /* Displacement map for X or radial direction */
+ GIMP_PDB_DRAWABLE, mapYId, /* Displacement map for Y or tangent direction */
+ GIMP_PDB_INT32, displaceType, /* Edge behavior: WRAP (0), SMEAR (1), BLACK (2) */
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ gimp_destroy_params(return_vals, nreturn_vals);
+ return (TRUE); /* OK */
+ }
+ gimp_destroy_params(return_vals, nreturn_vals);
+ g_message("Error: PDB call of %s failed, d_status:%d %s\n"
+ , l_called_proc
+ , (int)return_vals[0].data.d_status
+ , gap_status_to_string(return_vals[0].data.d_status)
+ );
+ return(FALSE);
+} /* end gap_pdb_call_displace */
+
+
+/* --------------------------------------
+ * gap_pdb_call_solid_noise
+ * --------------------------------------
+ *
+ */
+gboolean
+gap_pdb_call_solid_noise(gint32 image_id, gint32 layer_id
+ , gint32 tileable, gint32 turbulent, gint32 seed, gint32 detail, gdouble xsize, gdouble ysize)
+{
+ static char *l_called_proc = "plug-in-solid-noise";
+ GimpParam *return_vals;
+ int nreturn_vals;
+
+ return_vals = gimp_run_procedure (l_called_proc,
+ &nreturn_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_id,
+ GIMP_PDB_DRAWABLE, layer_id, /* input drawable (to be processed) */
+ GIMP_PDB_INT32, tileable, /* create tileable output (0:no, 1:yes) */
+ GIMP_PDB_INT32, turbulent, /* Make turbulent noise (0:no, 1:yes) */
+ GIMP_PDB_INT32, seed, /* Random seed */
+ GIMP_PDB_INT32, detail, /* detail level (0 - 15) */
+ GIMP_PDB_FLOAT, xsize, /* texture size */
+ GIMP_PDB_FLOAT, ysize, /* texture size */
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ gimp_destroy_params(return_vals, nreturn_vals);
+ return (TRUE); /* OK */
+ }
+ gimp_destroy_params(return_vals, nreturn_vals);
+ g_message("Error: PDB call of %s failed, d_status:%d %s\n"
+ , l_called_proc
+ , (int)return_vals[0].data.d_status
+ , gap_status_to_string(return_vals[0].data.d_status)
+ );
+ return(FALSE);
+} /* end gap_pdb_call_solid_noise */
+
+
+/* --------------------------------------
+ * gap_pdb_call_normalize
+ * --------------------------------------
+ *
+ */
+gboolean
+gap_pdb_call_normalize(gint32 image_id, gint32 layer_id)
+{
+ static char *l_called_proc = "plug-in-normalize";
+ GimpParam *return_vals;
+ int nreturn_vals;
+
+ return_vals = gimp_run_procedure (l_called_proc,
+ &nreturn_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_id,
+ GIMP_PDB_DRAWABLE, layer_id, /* input drawable (to be processed) */
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ gimp_destroy_params(return_vals, nreturn_vals);
+ return (TRUE); /* OK */
+ }
+ gimp_destroy_params(return_vals, nreturn_vals);
+ g_message("Error: PDB call of %s failed, d_status:%d %s\n"
+ , l_called_proc
+ , (int)return_vals[0].data.d_status
+ , gap_status_to_string(return_vals[0].data.d_status)
+ );
+ return(FALSE);
+} /* end gap_pdb_call_normalize */
+
diff --git a/gap/gap_pdb_calls.h b/gap/gap_pdb_calls.h
index 814b37a..dc7f31d 100644
--- a/gap/gap_pdb_calls.h
+++ b/gap/gap_pdb_calls.h
@@ -47,7 +47,6 @@ gint gap_pdb_procedure_available(char *proc_name);
gint32 gap_pdb_gimp_rotate_degree(gint32 drawable_id, gboolean interpolation, gdouble angle_deg);
gboolean gap_pdb_gimp_displays_reconnect(gint32 old_image_id, gint32 new_image_id);
-gint32 gap_pdb_gimp_layer_new_from_drawable(gint32 drawable_id, gint32 dst_image_id);
gboolean gap_pdb_gimp_file_save_thumbnail(gint32 image_id, char* filename);
@@ -56,4 +55,19 @@ gboolean gap_pdb_gimp_image_thumbnail(gint32 image_id, gint32 width, gint32 he
gint32 *th_data_count, unsigned char **th_data);
gboolean gap_pdb_procedure_name_available (const gchar *search_name);
+
+
+
+gboolean gap_pdb_call_displace(gint32 image_id, gint32 layer_id
+ ,gdouble amountX, gdouble amountY
+ ,gint32 doX, gint32 doY
+ ,gint32 mapXId, gint32 mapYId
+ ,gint32 displaceType);
+
+gboolean gap_pdb_call_solid_noise(gint32 image_id, gint32 layer_id
+ , gint32 tileable, gint32 turbulent, gint32 seed
+ , gint32 detail, gdouble xsize, gdouble ysize);
+
+gboolean gap_pdb_call_normalize(gint32 image_id, gint32 layer_id);
+
#endif
diff --git a/gap/gap_story_file.c b/gap/gap_story_file.c
index 67f09f2..f6f24ec 100644
--- a/gap/gap_story_file.c
+++ b/gap/gap_story_file.c
@@ -9064,8 +9064,11 @@ gap_story_file_calculate_render_attributes(GapStoryCalcAttr *result_attr
center_x_ofs = ((gdouble)vid_width/2.0) - (result_width/2.0);
center_y_ofs = ((gdouble)vid_height/2.0) - (result_height/2.0);
- result_ofsx = center_x_ofs + (((gdouble)result_attr->width / 2.0) * move_x) + (((gdouble)vid_width / 2.0) * move_x);
- result_ofsy = center_y_ofs + (((gdouble)result_attr->height / 2.0 ) * move_y) + (((gdouble)vid_height / 2.0 ) * move_y);
+// result_ofsx = center_x_ofs + (((gdouble)result_attr->width / 2.0) * move_x) + (((gdouble)vid_width / 2.0) * move_x);
+// result_ofsy = center_y_ofs + (((gdouble)result_attr->height / 2.0 ) * move_y) + (((gdouble)vid_height / 2.0 ) * move_y);
+ result_ofsx = center_x_ofs + ((result_width / 2.0) * move_x) + (((gdouble)vid_width / 2.0) * move_x);
+ result_ofsy = center_y_ofs + ((result_height / 2.0 ) * move_y) + (((gdouble)vid_height / 2.0 ) * move_y);
+
}
diff --git a/gap/gap_story_render_processor.c b/gap/gap_story_render_processor.c
index 1de8d0c..1f5d088 100644
--- a/gap/gap_story_render_processor.c
+++ b/gap/gap_story_render_processor.c
@@ -4506,6 +4506,10 @@ gap_story_render_open_vid_handle(GapLibTypeInputRange input_mode
* - execute the (optional) filtermacro_file if not NULL
* (filtermacro_file is a set of one or more gimp_filter procedures
* with predefined parameter values)
+ * returns the resulting layer_id (this may be the same as the specified layer_id at calling time
+ * but can change in case the called filter did add additional layers that were
+ * merged to one resulting layer (either in the called filter or after the filtercall
+ * by this procedure)
*/
static gint32
p_exec_filtermacro(gint32 image_id, gint32 layer_id, const char *filtermacro_file
@@ -4598,6 +4602,7 @@ p_exec_filtermacro(gint32 image_id, gint32 layer_id, const char *filtermacro_fil
l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
if(l_layers_list != NULL)
{
+ l_rc_layer_id = l_layers_list[0];
g_free (l_layers_list);
}
if(l_nlayers > 1 )
@@ -4608,6 +4613,18 @@ p_exec_filtermacro(gint32 image_id, gint32 layer_id, const char *filtermacro_fil
}
}
+
+ if(gap_debug)
+ {
+ if(l_rc_layer_id != layer_id)
+ {
+ printf("p_exec_filtermacro: layer_id:%d HAS CHANGED to %d\n"
+ ,(int)layer_id
+ ,(int)l_rc_layer_id
+ );
+
+ }
+ }
GAP_TIMM_STOP_FUNCTION(funcId);
return(l_rc_layer_id);
@@ -4717,6 +4734,7 @@ p_transform_and_add_layer( gint32 comp_image_id
gint32 vid_height;
gint32 l_new_layer_id;
gint32 l_fsel_layer_id;
+ gint32 l_fmac_layer_id;
GapStoryCalcAttr calculate_attributes;
GapStoryCalcAttr *calculated;
@@ -4751,7 +4769,7 @@ p_transform_and_add_layer( gint32 comp_image_id
* is used to define fmac_current_step
* (starts at 0)
*/
- p_exec_filtermacro(tmp_image_id
+ l_fmac_layer_id = p_exec_filtermacro(tmp_image_id
, layer_id
, filtermacro_file
, frn_elem->filtermacro_file_to
@@ -4762,12 +4780,14 @@ p_transform_and_add_layer( gint32 comp_image_id
if(gap_debug)
{
- printf("p_transform_and_add_layer: FILTERMACRO DONE at layer_id: %d, tmp_image_id:%d\n"
+ printf("p_transform_and_add_layer: FILTERMACRO DONE at layer_id: %d (l_fmac_layer_id:%d), tmp_image_id:%d\n"
, (int)layer_id
- ,(int)tmp_image_id );
+ , (int)l_fmac_layer_id
+ , (int)tmp_image_id
+ );
}
- layer_id = gap_layer_flip(layer_id, flip_request);
+ layer_id = gap_layer_flip(l_fmac_layer_id, flip_request);
/* expand layer to tmp image size (before applying any scaling) */
diff --git a/gap/gap_water_pattern.c b/gap/gap_water_pattern.c
new file mode 100644
index 0000000..a142708
--- /dev/null
+++ b/gap/gap_water_pattern.c
@@ -0,0 +1,1837 @@
+/* gap_water_pattern.c
+ * 2011.01.31 hof (Wolfgang Hofer)
+ *
+ * Water pattern - This is a filter for The GIMP to generate a highlight pattern such as those found on the bottom of pools.
+ * This implementation was ported from the script sf-will-wavetank.scm Copyright (C) 2010 William Morrison
+ * to C and was adapted to run on a single layer as animated filter, using GIMP-GAP typical
+ * iterator capabilities for animated apply. This provides the ability to apply the effect on already
+ * existing layers or frames.
+ *
+ */
+/* 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
+ * (2011/01/31) v1.0 hof: - created
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gap-intl.h"
+#include "gap_lastvaldesc.h"
+#include "gap_pdb_calls.h"
+
+/* Defines */
+#define PLUG_IN_NAME "plug-in-waterpattern"
+#define PLUG_IN_IMAGE_TYPES "RGB*, GRAY*"
+#define PLUG_IN_AUTHOR "Wolfgang Hofer (hof gimp org)"
+#define PLUG_IN_COPYRIGHT "Wolfgang Hofer"
+#define PLUG_IN_DESCRIPTION "Generates a water pattern effect as if the active layer were on the bottom of a ripple tank."
+
+#define PLUG_IN_DATA_ITER_FROM "plug-in-waterpattern-ITER-FROM"
+#define PLUG_IN_DATA_ITER_TO "plug-in-waterpattern-ITER-TO"
+#define PLUG_IN_HELP_ID "plug-in-waterpattern"
+
+#define BLEND_NUM_OVERLAY 0
+#define BLEND_NUM_ADDITION 1
+#define BLEND_NUM_SCREEN 2
+#define BLEND_NUM_DODGE 3
+
+#define N_RET_VALUES 4
+
+#define GAP_WATERPATTERN_RESPONSE_RESET 1
+
+typedef struct
+{
+ gdouble scalex;
+ gdouble scaley;
+ gint32 blendNum;
+ gdouble shiftPhaseX;
+ gdouble shiftPhaseY;
+ gboolean useHighlights;
+ gdouble highlightOpacity;
+ gboolean useDisplaceMap;
+ gdouble displaceStrength;
+
+ gboolean createImage;
+ gint32 nframes;
+ gint32 seed1;
+ gint32 seed2;
+ gint32 cloudLayer1;
+ gint32 cloudLayer2;
+
+} waterpattern_val_t;
+
+
+typedef struct _WaterPatternDialog WaterPatternDialog;
+
+struct _WaterPatternDialog
+{
+ gint run;
+ gint show_progress;
+ gint32 drawable_id;
+
+ gboolean createNewPatterns; /* FALSE: use cloud layers 1/2 entered via dialog */
+ gboolean createNewPatternsDefault; /* FALSE: use cloud layers 1/2 entered via dialog */
+ gint32 existingCloud1Id;
+ gint32 existingCloud2Id;
+ gint32 countPotentialCloudLayers;
+
+ GtkWidget *shell;
+ GtkWidget *radio_blend_overlay;
+ GtkWidget *radio_blend_addition;
+ GtkWidget *radio_blend_screen;
+ GtkWidget *radio_blend_dodge;
+ GtkObject *nframes_spinbutton_adj;
+ GtkWidget *nframes_spinbutton;
+ GtkObject *highlightOpacity_spinbutton_adj;
+ GtkWidget *highlightOpacity_spinbutton;
+ GtkObject *displaceStrength_spinbutton_adj;
+ GtkWidget *displaceStrength_spinbutton;
+
+ GtkObject *shiftPhaseX_spinbutton_adj;
+ GtkWidget *shiftPhaseX_spinbutton;
+ GtkObject *shiftPhaseY_spinbutton_adj;
+ GtkWidget *shiftPhaseY_spinbutton;
+ GtkObject *scaleX_spinbutton_adj;
+ GtkWidget *scaleX_spinbutton;
+ GtkObject *scaleY_spinbutton_adj;
+ GtkWidget *scaleY_spinbutton;
+ GtkObject *seed1_spinbutton_adj;
+ GtkWidget *seed1_spinbutton;
+ GtkObject *seed2_spinbutton_adj;
+ GtkWidget *seed2_spinbutton;
+
+ GtkWidget *cloud1Combo;
+ GtkWidget *cloud2Combo;
+ GtkWidget *cloud1Label;
+ GtkWidget *cloud2Label;
+ GtkWidget *patternCheckbutton;
+ GtkWidget *patternLabel;
+ GtkWidget *createImageCheckbutton;
+ GtkWidget *useHighlichtsCheckbutton;
+ GtkWidget *useDisplaceMapCheckbutton;
+
+ waterpattern_val_t *vals;
+};
+
+
+typedef struct
+{
+ GimpRunMode run_mode;
+ gint32 image_id;
+ gint32 ref_image_id;
+
+ gint32 width;
+ gint32 height;
+ GimpLayerModeEffects blend_mode;
+ gboolean add_display_to_ref_image;
+} waterpattern_context_t;
+
+
+static void p_int_default_cuvals(waterpattern_val_t *cuvals);
+static void p_check_for_valid_cloud_layers(waterpattern_val_t *cuvals);
+static void p_int_cuvals(waterpattern_val_t *cuvals);
+
+static WaterPatternDialog *do_dialog (WaterPatternDialog *wcd, waterpattern_val_t *);
+static void query (void);
+static void run(const gchar *name
+ , gint nparams
+ , const GimpParam *param
+ , gint *nreturn_vals
+ , GimpParam **return_vals);
+
+/* Global Variables */
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+
+gint gap_debug = 0; /* 0.. no debug, 1 .. print debug messages */
+
+/* --------------
+ * procedures
+ * --------------
+ */
+
+/* -----------------------------------------
+ * p_int_default_cuvals
+ * p_int_default_cuvals_without_cloud_layers
+ * -----------------------------------------
+ *
+ */
+static void
+p_int_default_cuvals_without_cloud_layers(waterpattern_val_t *cuvals)
+{
+ cuvals->scalex = 2.0;
+ cuvals->scaley = 5.0;
+ cuvals->blendNum = 0;
+ cuvals->shiftPhaseX = 0.0;
+ cuvals->shiftPhaseY = 0.0;
+ cuvals->useHighlights = 1;
+ cuvals->useDisplaceMap = 1;
+ cuvals->displaceStrength = 0.01;
+ cuvals->highlightOpacity = 50.0;
+ cuvals->createImage = FALSE;
+ cuvals->nframes = 50;
+ cuvals->seed1 = 0;
+ cuvals->seed2 = 0;
+
+} /* end p_int_default_cuvals_without_cloud_layers */
+
+static void
+p_int_default_cuvals(waterpattern_val_t *cuvals)
+{
+ p_int_default_cuvals_without_cloud_layers(cuvals);
+ cuvals->cloudLayer1 = -1;
+ cuvals->cloudLayer2 = -1;
+
+} /* end p_int_default_cuvals */
+
+/* --------------------------------------
+ * p_check_for_valid_cloud_layers
+ * --------------------------------------
+ *
+ */
+static void
+p_check_for_valid_cloud_layers(waterpattern_val_t *cuvals)
+{
+ if(gimp_drawable_is_valid(cuvals->cloudLayer1) != TRUE)
+ {
+ cuvals->cloudLayer1 = -1;
+ }
+ if(gimp_drawable_is_valid(cuvals->cloudLayer2) != TRUE)
+ {
+ cuvals->cloudLayer2 = -1;
+ }
+} /* end p_check_for_valid_cloud_layers */
+
+
+/* --------------------------------------
+ * p_int_cuvals
+ * --------------------------------------
+ *
+ */
+static void
+p_int_cuvals(waterpattern_val_t *cuvals)
+{
+ p_int_default_cuvals(cuvals);
+
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_NAME, cuvals);
+
+ p_check_for_valid_cloud_layers(cuvals);
+
+} /* end p_int_cuvals */
+
+
+
+/* --------------------------------------
+ * p_convertBlendNum_to_BlendMode
+ * --------------------------------------
+ */
+static GimpLayerModeEffects
+p_convertBlendNum_to_BlendMode(gint32 blendNum)
+{
+ GimpLayerModeEffects blendMode = GIMP_OVERLAY_MODE;
+ switch(blendNum)
+ {
+ case BLEND_NUM_OVERLAY:
+ blendMode = GIMP_OVERLAY_MODE; /* 5 */
+ break;
+ case BLEND_NUM_ADDITION:
+ blendMode = GIMP_ADDITION_MODE; /* 7 */
+ break;
+ case BLEND_NUM_SCREEN:
+ blendMode = GIMP_SCREEN_MODE; /* 4 */
+ break;
+ case BLEND_NUM_DODGE:
+ blendMode = GIMP_DODGE_MODE; /* 16 */
+ break;
+ default:
+ printf("unsupported blend mode, using default value Overlay\n");
+ break;
+ }
+
+ return (blendMode);
+}
+
+
+/* --------------------------------------
+ * p_init_context_and_cloud_layers
+ * --------------------------------------
+ *
+ */
+static gboolean
+p_init_context_and_cloud_layers(gint32 drawable_id, waterpattern_val_t *cuvals, waterpattern_context_t *ctxt)
+{
+ gboolean success;
+
+ ctxt->image_id = gimp_drawable_get_image(drawable_id);
+ ctxt->blend_mode = p_convertBlendNum_to_BlendMode(cuvals->blendNum);
+ ctxt->width = gimp_image_width(ctxt->image_id);
+ ctxt->height = gimp_image_height(ctxt->image_id);
+ ctxt->add_display_to_ref_image = FALSE;
+
+ success = TRUE;
+
+
+ /* check if both cloud layers are already available */
+ if((!gimp_drawable_is_valid(cuvals->cloudLayer1)) || (!gimp_drawable_is_valid(cuvals->cloudLayer2)))
+ {
+ /* create both cloud layers */
+ GRand *gr;
+ gint32 seed;
+ gr = g_rand_new ();
+
+ ctxt->ref_image_id = gimp_image_new(ctxt->width, ctxt->height, GIMP_RGB);
+ gimp_image_set_filename(ctxt->ref_image_id, "WaterPattern");
+
+ cuvals->cloudLayer1 = gimp_layer_new(ctxt->ref_image_id
+ , "Frame"
+ , ctxt->width
+ , ctxt->height
+ , GIMP_RGBA_IMAGE
+ , 100.0 /* full opaque */
+ , GIMP_NORMAL_MODE /* 0 */
+ );
+ cuvals->cloudLayer2 = gimp_layer_new(ctxt->ref_image_id
+ , "diff"
+ , ctxt->width
+ , ctxt->height
+ , GIMP_RGBA_IMAGE
+ , 100.0 /* full opaque */
+ , GIMP_DIFFERENCE_MODE /* 6 */
+ );
+ gimp_image_add_layer(ctxt->ref_image_id, cuvals->cloudLayer1, -1);
+ gimp_image_add_layer(ctxt->ref_image_id, cuvals->cloudLayer2, -1);
+
+
+ /* Adds the solid noise */
+ seed = cuvals->seed1;
+ if (seed == 0)
+ {
+ seed = g_rand_int_range(gr, 0, 2100000000);
+ }
+ success = gap_pdb_call_solid_noise(ctxt->ref_image_id
+ ,cuvals->cloudLayer1
+ ,1 /* 1 create tileable output */
+ ,0 /* turbulent noise 0 = no, 1 = yes */
+ ,seed /* rand(2100000000) */
+ ,1 /* detail level */
+ ,cuvals->scalex /* horizontal texture size */
+ ,cuvals->scaley /* vertical texture size */
+ );
+ if(success)
+ {
+ seed = cuvals->seed2;
+ if (seed == 0)
+ {
+ seed = g_rand_int_range(gr, 0, 2100000000);
+ }
+ success = gap_pdb_call_solid_noise(ctxt->ref_image_id
+ ,cuvals->cloudLayer2
+ ,1
+ ,0
+ ,seed
+ ,1
+ ,cuvals->scalex
+ ,cuvals->scaley
+ );
+ }
+
+ if(success)
+ {
+ success = gap_pdb_call_normalize(ctxt->ref_image_id, cuvals->cloudLayer1);
+ }
+ if(success)
+ {
+ success = gap_pdb_call_normalize(ctxt->ref_image_id, cuvals->cloudLayer2);
+ }
+
+ g_rand_free (gr);
+ ctxt->add_display_to_ref_image = TRUE;
+
+ }
+
+ return (success);
+
+} /* end p_init_context_and_cloud_layers */
+
+/* --------------------------------------
+ * p_cloud_size_check
+ * --------------------------------------
+ * check if layer is same size as processed image size (as specified in the context)
+ */
+static void
+p_cloud_size_check(gint32 layer_id, waterpattern_context_t *ctxt)
+{
+ if( (gimp_drawable_width(layer_id) != ctxt->width)
+ || (gimp_drawable_height(layer_id) != ctxt->height) )
+ {
+ gimp_layer_scale(layer_id, ctxt->width, ctxt->height, TRUE);
+ gimp_layer_set_offsets(layer_id, 0, 0);
+ }
+} /* end p_cloud_size_check */
+
+
+/* --------------------------------------
+ * p_run_renderWaterPattern
+ * --------------------------------------
+ * renders the water pattern effect onto the specified drawable.
+ * returns TRUE on success
+ */
+static gboolean
+p_run_renderWaterPattern(gint32 drawable_id, waterpattern_val_t *cuvals, waterpattern_context_t *ctxt)
+{
+ #define NUMBER_VAL_ARRAY_ELEMENTS 6
+ static guchar curveValuesArray[NUMBER_VAL_ARRAY_ELEMENTS] = { 0, 255, 64, 64, 255, 0 };
+ gint32 templayer_id;
+ gint32 newlayer1_id = -1;
+ gint32 newlayer2_id = -1;
+ gint32 displace_layer_id = -1;
+ gboolean isVisible;
+ gboolean success;
+ gint32 count;
+ gint32 nframesToProcess;
+
+
+
+ if (!gimp_drawable_is_layer(drawable_id))
+ {
+ g_message(_("drawable:%d is not a layer\n"), drawable_id);
+ return (FALSE);
+ }
+
+ /* save visibility status of processed layer .. */
+ isVisible = gimp_drawable_get_visible(drawable_id);
+ /* .. and force visibility (required for merge down effects) */
+ gimp_drawable_set_visible(drawable_id, TRUE);
+
+ templayer_id = drawable_id;
+ nframesToProcess = 1;
+
+ gimp_layer_resize_to_image_size(drawable_id);
+ success = p_init_context_and_cloud_layers(drawable_id, cuvals, ctxt);
+
+ if (cuvals->createImage)
+ {
+ ctxt->image_id = gimp_image_new(ctxt->width, ctxt->height, GIMP_RGB);
+ /* dont save history during the creation of the animation layers */
+ gimp_image_undo_freeze(ctxt->image_id);
+ templayer_id = -1;
+
+ if (cuvals->nframes > 1)
+ {
+ nframesToProcess = cuvals->nframes;
+ }
+ }
+
+
+
+ if(gap_debug)
+ {
+ printf("p_run_renderWaterPattern: drawable_id :%d (%s)\n", (int)drawable_id, gimp_drawable_get_name(drawable_id));
+ printf("p_run_renderWaterPattern: scalex:%f\n", (float)cuvals->scalex);
+ printf("p_run_renderWaterPattern: scaley:%f\n", (float)cuvals->scaley);
+ printf("p_run_renderWaterPattern: blendNum:%d\n", (int)cuvals->blendNum);
+ printf("p_run_renderWaterPattern: shiftPhaseX:%f\n", (float)cuvals->shiftPhaseX);
+ printf("p_run_renderWaterPattern: shiftPhaseY:%f\n", (float)cuvals->shiftPhaseY);
+ printf("p_run_renderWaterPattern: useHighlights:%d\n", (int)cuvals->useHighlights);
+ printf("p_run_renderWaterPattern: highlightOpacity:%f\n", (float)cuvals->highlightOpacity);
+ printf("p_run_renderWaterPattern: useDisplaceMap:%d\n", (int)cuvals->useDisplaceMap);
+ printf("p_run_renderWaterPattern: displaceStrength:%f\n", (float)cuvals->displaceStrength);
+ printf("p_run_renderWaterPattern: createImage:%d\n", (int)cuvals->createImage);
+ printf("p_run_renderWaterPattern: nframes:%d\n", (int)cuvals->nframes);
+ printf("p_run_renderWaterPattern: seed1:%d\n", (int)cuvals->seed1);
+ printf("p_run_renderWaterPattern: seed2:%d\n", (int)cuvals->seed2);
+ printf("p_run_renderWaterPattern: cloudLayer1:%d (%s)\n", (int)cuvals->cloudLayer1, gimp_drawable_get_name(cuvals->cloudLayer1));
+ printf("p_run_renderWaterPattern: cloudLayer2:%d (%s)\n", (int)cuvals->cloudLayer2, gimp_drawable_get_name(cuvals->cloudLayer2));
+
+ printf("p_run_renderWaterPattern: ref_image_id:%d\n", (int)ctxt->ref_image_id);
+ printf("p_run_renderWaterPattern: image_id:%d\n", (int)ctxt->image_id);
+ printf("p_run_renderWaterPattern: width:%d\n", (int)ctxt->width);
+ printf("p_run_renderWaterPattern: height:%d\n", (int)ctxt->height);
+ printf("p_run_renderWaterPattern: blend_mode:%d\n", (int)ctxt->blend_mode);
+ printf("p_run_renderWaterPattern: success:%d\n", (int)success);
+
+ }
+
+ if(!success)
+ {
+ return (FALSE);
+ }
+
+
+ /* end while nframes loop */
+ for(count=0; count < nframesToProcess; count++)
+ {
+ if (cuvals->createImage)
+ {
+ /* in case we are creating a multilayer anim in a new image
+ * copy the drawable to a new layer in this new image
+ */
+ templayer_id = gimp_layer_new_from_drawable(drawable_id, ctxt->image_id);
+ gimp_image_add_layer(ctxt->image_id, templayer_id, -1 /* -1 place above active layer */);
+ }
+
+ /* copy cloud layers from ref image to current processed image_id
+ * at stackposition above the current processed layer (templayer_id)
+ */
+ newlayer1_id = gimp_layer_new_from_drawable(cuvals->cloudLayer1, ctxt->image_id);
+ newlayer2_id = gimp_layer_new_from_drawable(cuvals->cloudLayer2, ctxt->image_id);
+
+ gimp_image_set_active_layer(ctxt->image_id, templayer_id);
+ gimp_image_add_layer(ctxt->image_id, newlayer1_id, -1 /* -1 place above active layer */);
+ gimp_image_add_layer(ctxt->image_id, newlayer2_id, -1 /* -1 place above active layer */);
+
+ p_cloud_size_check(newlayer1_id, ctxt);
+ p_cloud_size_check(newlayer2_id, ctxt);
+
+ /* offsets */
+ {
+ gdouble shiftx;
+ gdouble shifty;
+ gint32 intShiftx;
+ gint32 intShifty;
+ gint32 offsetx;
+ gint32 offsety;
+
+ if(nframesToProcess > 1)
+ {
+ gdouble phX;
+ gdouble phY;
+
+ phY = cuvals->shiftPhaseY;
+ phX = cuvals->shiftPhaseX;
+
+ if ((phX == 0.0) && (phY == 0.0))
+ {
+ phX = 1.0;
+ phY = 1.0;
+ }
+
+ shiftx = (gdouble)count * (((gdouble)ctxt->width * phX) / (gdouble)nframesToProcess);
+ shifty = (gdouble)count * (((gdouble)ctxt->height * phY) / (gdouble)nframesToProcess);
+ }
+ else
+ {
+ shiftx = (gdouble)ctxt->width * cuvals->shiftPhaseX;
+ shifty = (gdouble)ctxt->height * cuvals->shiftPhaseY;
+ }
+ intShiftx = rint(shiftx);
+ intShifty = rint(shifty);
+
+ offsetx = intShiftx % ctxt->width;
+ offsety = intShifty % ctxt->height;
+ gimp_drawable_offset(newlayer2_id, 1 /* 1:Wrap around */, 0 /* fill type */, offsetx, offsety);
+
+ offsetx = 0 - offsetx;
+ offsety = 0 - offsety;
+ gimp_drawable_offset(newlayer1_id, 1 /* 1:Wrap around */, 0 /* fill type */, offsetx, offsety);
+
+ newlayer1_id = gimp_image_merge_down(ctxt->image_id, newlayer2_id, GIMP_EXPAND_AS_NECESSARY);
+
+ if(cuvals->useDisplaceMap)
+ {
+ displace_layer_id = gimp_layer_new_from_drawable(newlayer1_id, ctxt->image_id);
+ }
+
+ /* call color curves tool with GimpHistogramChannel GIMP_HISTOGRAM_VALUE 0 */
+ gimp_curves_spline(newlayer1_id, GIMP_HISTOGRAM_VALUE, NUMBER_VAL_ARRAY_ELEMENTS, curveValuesArray);
+
+ }
+
+ /* optional merge the highlights onto the processed layer */
+ if(cuvals->useHighlights)
+ {
+ gimp_layer_set_mode(newlayer1_id, ctxt->blend_mode);
+ gimp_layer_set_opacity(newlayer1_id, cuvals->highlightOpacity);
+ templayer_id = gimp_image_merge_down(ctxt->image_id, newlayer1_id, GIMP_EXPAND_AS_NECESSARY);
+ }
+ else
+ {
+ gimp_image_remove_layer(ctxt->image_id, newlayer1_id);
+ }
+
+ if (cuvals->createImage)
+ {
+ gchar *layerName;
+
+ layerName = g_strdup_printf("Frame_%03d", (int)count +1);
+ gimp_drawable_set_name(templayer_id, layerName);
+ g_free(layerName);
+ }
+
+ /* optional displaces the final result according to the displacement map */
+ if(cuvals->useDisplaceMap)
+ {
+ gdouble displaceAmountX;
+ gdouble displaceAmountY;
+
+
+ displaceAmountX = cuvals->displaceStrength * (gdouble)ctxt->width;
+ displaceAmountY = cuvals->displaceStrength * (gdouble)ctxt->height;
+
+ success = gap_pdb_call_displace(ctxt->image_id, templayer_id
+ , displaceAmountX, displaceAmountY
+ , 1, 1
+ , displace_layer_id, displace_layer_id
+ , 1 /* 0:WRAP, 1: SMEAR, 2: BLACK */
+ );
+ /* delete the displace_layer_id (that is not added to any image yet) */
+ gimp_drawable_delete(displace_layer_id);
+ }
+
+ if(!success)
+ {
+ break;
+ }
+
+ } /* end while nframes loop */
+
+
+ /* restore visibility status of processed layer */
+ gimp_drawable_set_visible(templayer_id, isVisible);
+
+ if (cuvals->createImage)
+ {
+ gimp_image_undo_thaw(ctxt->image_id);
+ }
+
+ /* in case a new image was created in an interactive mode,
+ * add a display for this newly created imge
+ */
+ if(ctxt->run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ if (cuvals->createImage)
+ {
+ gimp_display_new(ctxt->image_id);
+ }
+ if(ctxt->add_display_to_ref_image)
+ {
+ gimp_display_new(ctxt->ref_image_id);
+ }
+ }
+
+ return (success);
+
+} /* end p_run_renderWaterPattern */
+
+
+/*
+ * DIALOG and callback stuff
+ */
+
+
+/* --------------------------------------
+ * on_blend_radio_callback
+ * --------------------------------------
+ */
+static void
+on_blend_radio_callback(GtkWidget *wgt, gpointer user_data)
+{
+ WaterPatternDialog *wcd;
+
+ if(gap_debug)
+ {
+ printf("on_blend_radio_callback: START\n");
+ }
+ wcd = (WaterPatternDialog*)user_data;
+ if(wcd != NULL)
+ {
+ if(wcd->vals != NULL)
+ {
+ if(wgt == wcd->radio_blend_overlay) { wcd->vals->blendNum = BLEND_NUM_OVERLAY; }
+ if(wgt == wcd->radio_blend_addition) { wcd->vals->blendNum = BLEND_NUM_ADDITION; }
+ if(wgt == wcd->radio_blend_screen) { wcd->vals->blendNum = BLEND_NUM_SCREEN; }
+ if(wgt == wcd->radio_blend_dodge) { wcd->vals->blendNum = BLEND_NUM_DODGE; }
+
+ }
+ }
+}
+
+/* --------------------------------------
+ * p_update_widget_sensitivity
+ * --------------------------------------
+ */
+static void
+p_update_widget_sensitivity (WaterPatternDialog *wcd)
+{
+ gboolean inverseCreateNewPatterns;
+
+
+ /* createImage dependent widgets */
+ gtk_widget_set_sensitive(wcd->nframes_spinbutton , wcd->vals->createImage);
+
+ /* createNewPatterns dependent widgets */
+ if(wcd->createNewPatterns)
+ {
+ inverseCreateNewPatterns = FALSE;
+ }
+ else
+ {
+ inverseCreateNewPatterns = TRUE;
+ }
+ gtk_widget_set_sensitive(wcd->scaleX_spinbutton , wcd->createNewPatterns);
+ gtk_widget_set_sensitive(wcd->scaleY_spinbutton , wcd->createNewPatterns);
+ gtk_widget_set_sensitive(wcd->seed1_spinbutton , wcd->createNewPatterns);
+ gtk_widget_set_sensitive(wcd->seed2_spinbutton , wcd->createNewPatterns);
+ gtk_widget_set_sensitive(wcd->cloud1Combo , inverseCreateNewPatterns);
+ gtk_widget_set_sensitive(wcd->cloud2Combo , inverseCreateNewPatterns);
+
+
+ /* highlight dependent widgets */
+ gtk_widget_set_sensitive(wcd->radio_blend_overlay , wcd->vals->useHighlights);
+ gtk_widget_set_sensitive(wcd->radio_blend_addition , wcd->vals->useHighlights);
+ gtk_widget_set_sensitive(wcd->radio_blend_screen , wcd->vals->useHighlights);
+ gtk_widget_set_sensitive(wcd->radio_blend_dodge , wcd->vals->useHighlights);
+ gtk_widget_set_sensitive(wcd->highlightOpacity_spinbutton , wcd->vals->useHighlights);
+
+ /* displace dependent widgets */
+ gtk_widget_set_sensitive(wcd->displaceStrength_spinbutton , wcd->vals->useDisplaceMap);
+
+
+} /* end p_update_widget_sensitivity */
+
+
+/* --------------------------------------
+ * p_init_widget_values
+ * --------------------------------------
+ * update GUI widgets to reflect the current values.
+ */
+static void
+p_init_widget_values(WaterPatternDialog *wcd)
+{
+ gint countClouds;
+ if(wcd == NULL)
+ {
+ return;
+ }
+ if(wcd->vals == NULL)
+ {
+ return;
+ }
+
+ /* init spnbuttons */
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->nframes_spinbutton_adj)
+ , (gfloat)wcd->vals->nframes);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->highlightOpacity_spinbutton_adj)
+ , (gfloat)wcd->vals->highlightOpacity);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->displaceStrength_spinbutton_adj)
+ , (gfloat)wcd->vals->displaceStrength);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->shiftPhaseX_spinbutton_adj)
+ , (gfloat)wcd->vals->shiftPhaseX);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->shiftPhaseY_spinbutton_adj)
+ , (gfloat)wcd->vals->shiftPhaseY);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->scaleX_spinbutton_adj)
+ , (gfloat)wcd->vals->scalex);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->scaleY_spinbutton_adj)
+ , (gfloat)wcd->vals->scaley);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->seed1_spinbutton_adj)
+ , (gfloat)wcd->vals->seed1);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(wcd->seed2_spinbutton_adj)
+ , (gfloat)wcd->vals->seed2);
+
+
+
+ /* init checkbuttons */
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->createImageCheckbutton)
+ , wcd->vals->createImage);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->patternCheckbutton)
+ , wcd->createNewPatternsDefault);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->useHighlichtsCheckbutton)
+ , wcd->vals->useHighlights);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wcd->useDisplaceMapCheckbutton)
+ , wcd->vals->useDisplaceMap);
+
+ /* init radiobuttons */
+ if(wcd->vals->blendNum == BLEND_NUM_OVERLAY)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (wcd->radio_blend_overlay), TRUE);
+ }
+ else if (wcd->vals->blendNum == BLEND_NUM_ADDITION)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (wcd->radio_blend_addition), TRUE);
+ }
+ else if (wcd->vals->blendNum == BLEND_NUM_SCREEN)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (wcd->radio_blend_screen), TRUE);
+ }
+ else if (wcd->vals->blendNum == BLEND_NUM_DODGE)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (wcd->radio_blend_dodge), TRUE);
+ }
+
+ countClouds = 0;
+ if(gimp_drawable_is_valid(wcd->existingCloud1Id))
+ {
+ countClouds++;
+ }
+ if(gimp_drawable_is_valid(wcd->existingCloud2Id))
+ {
+ countClouds++;
+ }
+
+ if(countClouds == 2)
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (wcd->cloud1Combo), wcd->existingCloud1Id);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (wcd->cloud2Combo), wcd->existingCloud2Id);
+ }
+
+} /* end p_init_widget_values */
+
+
+/* --------------------------------------
+ * on_gboolean_button_update
+ * --------------------------------------
+ */
+static void
+on_gboolean_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ WaterPatternDialog *wcd;
+ gint *toggle_val = (gint *) data;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ *toggle_val = TRUE;
+ }
+ else
+ {
+ *toggle_val = FALSE;
+ }
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+
+ wcd = (WaterPatternDialog *) g_object_get_data (G_OBJECT (widget), "wcd");
+ if(wcd != NULL)
+ {
+ p_update_widget_sensitivity (wcd);
+ }
+}
+
+
+/* ---------------------------------
+ * p_waterpattern_response
+ * ---------------------------------
+ */
+static void
+p_waterpattern_response (GtkWidget *widget,
+ gint response_id,
+ WaterPatternDialog *wcd)
+{
+ GtkWidget *dialog;
+
+ switch (response_id)
+ {
+ case GAP_WATERPATTERN_RESPONSE_RESET:
+ if(wcd)
+ {
+ /* rset default values */
+ p_int_default_cuvals_without_cloud_layers(wcd->vals);
+ p_init_widget_values(wcd);
+ p_update_widget_sensitivity (wcd);
+ }
+ break;
+
+ case GTK_RESPONSE_OK:
+ if(wcd)
+ {
+ if (GTK_WIDGET_VISIBLE (wcd->shell))
+ {
+ gtk_widget_hide (wcd->shell);
+ }
+ wcd->run = TRUE;
+ }
+
+ default:
+ dialog = NULL;
+ if(wcd)
+ {
+ dialog = wcd->shell;
+ if(dialog)
+ {
+ wcd->shell = NULL;
+ gtk_widget_destroy (dialog);
+ }
+ }
+ gtk_main_quit ();
+ break;
+ }
+} /* end p_waterpattern_response */
+
+
+
+/* ------------------------------
+ * p_cloud_layer_menu_callback
+ * ------------------------------
+ *
+ */
+static void
+p_cloud_layer_menu_callback(GtkWidget *widget, gint32 *cloudLayerId)
+{
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+
+ if(gap_debug)
+ {
+ printf("p_cloud_layer_menu_callback: cloudLayerAddr:%d value:%d\n"
+ ,(int)cloudLayerId
+ ,(int)value
+ );
+ }
+
+ if(cloudLayerId != NULL)
+ {
+ *cloudLayerId = value;
+ }
+
+
+} /* end p_cloud_layer_menu_callback */
+
+
+/* ------------------------------
+ * p_pattern_layer_constrain
+ * ------------------------------
+ *
+ */
+static gint
+p_pattern_layer_constrain(gint32 image_id, gint32 drawable_id, WaterPatternDialog *wcd)
+{
+ gint32 processedImageId;
+
+ if(gap_debug)
+ {
+ printf("p_pattern_layer_constrain PROCEDURE image_id:%d drawable_id:%d wcd:%d\n"
+ ,(int)image_id
+ ,(int)drawable_id
+ ,(int)wcd
+ );
+ }
+
+ 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);
+ }
+
+ if(gimp_drawable_is_valid(drawable_id) != TRUE)
+ {
+ return(FALSE);
+ }
+
+ processedImageId = gimp_drawable_get_image(wcd->drawable_id);
+
+ if(image_id == processedImageId)
+ {
+ return (FALSE);
+ }
+
+ if(!gimp_drawable_is_rgb(drawable_id))
+ {
+ if(!gimp_drawable_is_gray(drawable_id))
+ {
+ return (FALSE);
+ }
+ }
+
+ wcd->countPotentialCloudLayers++;
+
+ return(TRUE);
+
+} /* end p_pattern_layer_constrain */
+
+
+/* ------------------------------
+ * do_dialog
+ * ------------------------------
+ * create and show the dialog window
+ */
+static WaterPatternDialog *
+do_dialog (WaterPatternDialog *wcd, waterpattern_val_t *cuvals)
+{
+ GtkWidget *vbox;
+
+ GtkWidget *dialog1;
+ GtkWidget *dialog_vbox1;
+ GtkWidget *frame1;
+ GtkWidget *vbox1;
+ GtkWidget *label;
+ GSList *vbox1_group = NULL;
+ GtkWidget *radiobutton;
+ GtkWidget *table1;
+ GtkObject *spinbutton_adj;
+ GtkWidget *spinbutton;
+ GtkWidget *dialog_action_area1;
+ GtkWidget *checkbutton;
+ GtkWidget *combo;
+ gint countClouds;
+ gint row;
+
+
+ /* Init UI */
+ gimp_ui_init ("waterpattern", FALSE);
+
+
+ /* The dialog1 */
+ wcd->run = FALSE;
+ wcd->vals = cuvals;
+ wcd->createNewPatternsDefault = TRUE;
+ wcd->existingCloud1Id = -1;
+ wcd->existingCloud2Id = -1;
+ countClouds = 0;
+ wcd->countPotentialCloudLayers = 0;
+ if(gimp_drawable_is_valid(cuvals->cloudLayer1))
+ {
+ countClouds++;
+ wcd->existingCloud1Id = cuvals->cloudLayer1;
+ }
+ if(gimp_drawable_is_valid(cuvals->cloudLayer2))
+ {
+ countClouds++;
+ wcd->existingCloud2Id = cuvals->cloudLayer2;
+ }
+
+ if(countClouds == 2)
+ {
+ /* both cloud layers from aprevious run are still valid
+ * in this case createNewPatterns checkbutton shall be FALSE as default
+ */
+ wcd->createNewPatternsDefault = FALSE;
+ }
+ wcd->createNewPatterns = wcd->createNewPatternsDefault;
+
+ /* The dialog1 and main vbox */
+ dialog1 = gimp_dialog_new (_("Water-Pattern"), "water_pattern",
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_HELP_ID,
+
+ GIMP_STOCK_RESET, GAP_WATERPATTERN_RESPONSE_RESET,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ wcd->shell = dialog1;
+
+
+ /*
+ * g_object_set_data (G_OBJECT (dialog1), "dialog1", dialog1);
+ * gtk_window_set_title (GTK_WINDOW (dialog1), _("dialog1"));
+ */
+
+
+ g_signal_connect (G_OBJECT (dialog1), "response",
+ G_CALLBACK (p_waterpattern_response),
+ wcd);
+
+ /* the vbox */
+ vbox = gtk_vbox_new (FALSE, 2);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog1)->vbox), vbox,
+ TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ dialog_vbox1 = GTK_DIALOG (dialog1)->vbox;
+ g_object_set_data (G_OBJECT (dialog1), "dialog_vbox1", dialog_vbox1);
+ gtk_widget_show (dialog_vbox1);
+
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Animation options"));
+
+ gtk_widget_show (frame1);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+ gtk_widget_show (vbox1);
+
+
+ /* table1 for spinbuttons */
+ table1 = gtk_table_new (4, 2, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+
+ row = 0;
+
+ /* createImage checkbutton */
+ label = gtk_label_new (_("Create Image:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->createImageCheckbutton = checkbutton;
+ gtk_widget_show (checkbutton);
+ gimp_help_set_help_data (checkbutton, _("ON: create a new image with n copies of the input drawable and render complete animation effect on those copies. OFF: render only one phase of the animation effect on the input drawable"), NULL);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->createImage);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->createImage);
+
+ row++;
+
+ label = gtk_label_new (_("N-Frames:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ /* nframes spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->nframes, 1.0, 500, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0);
+ gimp_help_set_help_data (spinbutton, _("Number of frames to be rendered as layer in the newly created image."), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_int_adjustment_update), &cuvals->nframes);
+ wcd->nframes_spinbutton_adj = spinbutton_adj;
+ wcd->nframes_spinbutton = spinbutton;
+
+ row++;
+
+
+
+ /* shiftPhaseX spinbutton */
+ label = gtk_label_new (_("Phase shift X:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ spinbutton_adj = gtk_adjustment_new (cuvals->shiftPhaseX, 0.0, 100, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Horizontal shift phase where 1.0 refers to image width"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->shiftPhaseX);
+ wcd->shiftPhaseX_spinbutton_adj = spinbutton_adj;
+ wcd->shiftPhaseX_spinbutton = spinbutton;
+
+
+ /* shiftPhaseY spinbutton */
+ label = gtk_label_new (_("Y:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ spinbutton_adj = gtk_adjustment_new (cuvals->shiftPhaseY, 0.0, 100, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Vertical shift phase where 1.0 refers to image height"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->shiftPhaseY);
+ wcd->shiftPhaseY_spinbutton_adj = spinbutton_adj;
+ wcd->shiftPhaseY_spinbutton = spinbutton;
+
+
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Pattern options"));
+
+ gtk_widget_show (frame1);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+ gtk_widget_show (vbox1);
+
+
+
+ /* table1 for spinbuttons */
+ table1 = gtk_table_new (4, 2, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+
+
+ row = 0;
+ /* use existing Patterns checkbutton */
+ label = gtk_label_new (_("Create Pattern:"));
+ gtk_widget_show (label);
+ wcd->patternLabel = label;
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->patternCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("ON: create waterpattern cloud layers according options. OFF: Use external pattern layers. "), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), wcd->createNewPatterns);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &wcd->createNewPatterns);
+ row++;
+
+ /* pattern */
+ label = gtk_label_new (_("Layer Pattern 1:"));
+ wcd->cloud1Label = label;
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ combo = gimp_layer_combo_box_new (p_pattern_layer_constrain, wcd);
+ wcd->cloud1Combo = combo;
+ gtk_widget_show(combo);
+ gtk_table_attach(GTK_TABLE(table1), combo, 1, 4, row, row+1,
+ GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+ gimp_help_set_help_data(combo,
+ _("Select an already existing pattern layer (from previous run)")
+ , NULL);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ wcd->existingCloud1Id, /* initial value */
+ G_CALLBACK (p_cloud_layer_menu_callback),
+ &wcd->existingCloud1Id);
+
+ row++;
+
+ label = gtk_label_new (_("Layer Pattern 2:"));
+ gtk_widget_show (label);
+ wcd->cloud2Label = label;
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ combo = gimp_layer_combo_box_new (p_pattern_layer_constrain, wcd);
+ wcd->cloud2Combo = combo;
+ gtk_widget_show(combo);
+ gtk_table_attach(GTK_TABLE(table1), combo, 1, 4, row, row+1,
+ GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+ gimp_help_set_help_data(combo,
+ _("Select an already existing pattern layer (from previous run)")
+ , NULL);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ wcd->existingCloud2Id, /* initial value */
+ G_CALLBACK (p_cloud_layer_menu_callback),
+ &wcd->existingCloud2Id);
+
+ row++;
+
+
+ /* scalex spinbutton */
+ label = gtk_label_new (_("Scale Pattern X:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->scalex, 0.1, 16, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Horizontal scaling of the random patterns that are created for rendering (cloud1 and cloud2 layers)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->scalex);
+ wcd->scaleX_spinbutton_adj = spinbutton_adj;
+ wcd->scaleX_spinbutton = spinbutton;
+
+
+ label = gtk_label_new (_("Y:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ spinbutton_adj = gtk_adjustment_new (cuvals->scaley, 0.1, 16, 0.1, 1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 2);
+ gimp_help_set_help_data (spinbutton, _("Vertical scaling of the random patterns that are created for rendering (cloud1 and cloud2 layers)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->scaley);
+ wcd->scaleY_spinbutton_adj = spinbutton_adj;
+ wcd->scaleY_spinbutton = spinbutton;
+
+
+ row++;
+
+ label = gtk_label_new (_("Seed Pattern 1:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ /* seed1 spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->seed1, 0.0, 2147483647, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0);
+ gimp_help_set_help_data (spinbutton, _("Seed for creating random pattern (cloud1 layer) use 0 for random value."), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_int_adjustment_update), &cuvals->seed1);
+ wcd->seed1_spinbutton_adj = spinbutton_adj;
+ wcd->seed1_spinbutton = spinbutton;
+
+
+ label = gtk_label_new ("2:");
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ /* seed2 spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->seed2, 0.0, 2147483647, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0);
+ gimp_help_set_help_data (spinbutton, _("Seed for creating random pattern (cloud2 layer) use 0 for random value."), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_int_adjustment_update), &cuvals->seed2);
+ wcd->seed2_spinbutton_adj = spinbutton_adj;
+ wcd->seed2_spinbutton = spinbutton;
+
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Render options"));
+
+ gtk_widget_show (frame1);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+ gtk_widget_show (vbox1);
+
+
+
+ /* table1 for spinbuttons */
+ table1 = gtk_table_new (4, 2, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+
+
+ row = 0;
+
+ row++;
+
+ /* useHighlights checkbutton */
+ label = gtk_label_new (_("Use Highlights:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->useHighlichtsCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("Render water pattern highlight effect"), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->useHighlights);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->useHighlights);
+
+
+ label = gtk_label_new (_("Opacity:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ /* highlightOpacity spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->highlightOpacity, 0.0, 100, 1.0, 10, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 3);
+ gimp_help_set_help_data (spinbutton, _("The highlight strength (e.g. opacity)"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->highlightOpacity);
+ wcd->highlightOpacity_spinbutton_adj = spinbutton_adj;
+ wcd->highlightOpacity_spinbutton = spinbutton;
+
+ row++;
+
+ /* Highlights blend mode */
+ label = gtk_label_new (_("Blend Mode:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+
+ gtk_widget_show (vbox1);
+ gtk_table_attach (GTK_TABLE (table1), vbox1, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+
+ /* Blend Mode the radio buttons */
+
+ radiobutton = gtk_radio_button_new_with_label (vbox1_group, _("Overlay"));
+ wcd->radio_blend_overlay = radiobutton;
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
+ gtk_widget_show (radiobutton);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (radiobutton), "clicked", G_CALLBACK (on_blend_radio_callback), wcd);
+ if(wcd->vals->blendNum == BLEND_NUM_OVERLAY)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (radiobutton), TRUE);
+ }
+
+ radiobutton = gtk_radio_button_new_with_label (vbox1_group, _("Addition"));
+ wcd->radio_blend_addition = radiobutton;
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
+ gtk_widget_show (radiobutton);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (radiobutton), "clicked", G_CALLBACK (on_blend_radio_callback), wcd);
+ if(wcd->vals->blendNum == BLEND_NUM_ADDITION)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (radiobutton), TRUE);
+ }
+
+ radiobutton = gtk_radio_button_new_with_label (vbox1_group, _("Screen"));
+ wcd->radio_blend_screen = radiobutton;
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
+ gtk_widget_show (radiobutton);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (radiobutton), "clicked", G_CALLBACK (on_blend_radio_callback), wcd);
+ if(wcd->vals->blendNum == BLEND_NUM_SCREEN)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (radiobutton), TRUE);
+ }
+
+ radiobutton = gtk_radio_button_new_with_label (vbox1_group, _("Dodge"));
+ wcd->radio_blend_dodge = radiobutton;
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
+ gtk_widget_show (radiobutton);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (radiobutton), "clicked", G_CALLBACK (on_blend_radio_callback), wcd);
+ if(wcd->vals->blendNum == BLEND_NUM_DODGE)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (radiobutton), TRUE);
+ }
+
+
+
+
+
+ row++;
+
+ /* useDisplaceMap checkbutton */
+ label = gtk_label_new (_("Use Displace Map:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+ checkbutton = gtk_check_button_new_with_label (" ");
+ g_object_set_data (G_OBJECT (checkbutton), "wcd", wcd);
+ wcd->useDisplaceMapCheckbutton = checkbutton;
+ gimp_help_set_help_data (checkbutton, _("Render water pattern distortion effect (by applying a generated displace map)"), NULL);
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->useDisplaceMap);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->useDisplaceMap);
+
+ label = gtk_label_new (_("Strength:"));
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table1), label, 2, 3, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+ /* displaceStrength spinbutton */
+ spinbutton_adj = gtk_adjustment_new (cuvals->displaceStrength, 0.0, 1.0, 0.01, 0.1, 0);
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 3);
+ gimp_help_set_help_data (spinbutton, _("The distortion displace strength"), NULL);
+ gtk_widget_show (spinbutton);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ g_signal_connect (G_OBJECT (spinbutton_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->displaceStrength);
+ wcd->displaceStrength_spinbutton_adj = spinbutton_adj;
+ wcd->displaceStrength_spinbutton = spinbutton;
+
+ /* -- */
+
+
+ dialog_action_area1 = GTK_DIALOG (dialog1)->action_area;
+ gtk_widget_show (dialog_action_area1);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area1), 10);
+
+ p_init_widget_values(wcd);
+ p_update_widget_sensitivity (wcd);
+
+
+ gtk_widget_show (dialog1);
+
+ gtk_main ();
+ gdk_flush ();
+
+ if((!wcd->createNewPatterns) && (wcd->countPotentialCloudLayers > 0))
+ {
+ wcd->vals->cloudLayer1 = wcd->existingCloud1Id;
+ wcd->vals->cloudLayer2 = wcd->existingCloud2Id;
+ }
+ else
+ {
+ /* use -1 to force creation of new clod layers */
+ wcd->vals->cloudLayer1 = -1;
+ wcd->vals->cloudLayer2 = -1;
+ }
+
+ return wcd;
+
+} /* end do_dialog */
+
+
+
+
+
+MAIN ()
+
+/* --------------------------------------
+ * query
+ * --------------------------------------
+ *
+ */
+static void
+query (void)
+{
+ static GimpParamDef args[] = {
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (must be a layer without layermask)"},
+ { GIMP_PDB_FLOAT, "scalex", "Horizontal scale (0.1 upto 16.0)"},
+ { GIMP_PDB_FLOAT, "scaley", "Vertical scale (0.1 upto 16.0)"},
+ { GIMP_PDB_INT32, "blendNum", "Blend mode { OVERLAY (0), ADDITION (1), SCREEN (2), DODGE (3) }"},
+ { GIMP_PDB_FLOAT, "shiftPhaseX", "Horizontal shift phase (0.0 to 1.0 full width, values >= 1.0 foldback to range 0.0 - 0.99999)"},
+ { GIMP_PDB_FLOAT, "shiftPhaseY", "Vertical shift phase (0.0 to 1.0 full height, values >= 1.0 foldback to range 0.0 - 0.99999)"},
+ { GIMP_PDB_INT32, "useHighlights", "TRUE (1): Apply watereffect highlights)"},
+ { GIMP_PDB_FLOAT, "highlightOpacity", "highlight strength (0.0 to 100.0 opacity of the highlight effect, only relevant in case useHighlights is TRUE)"},
+ { GIMP_PDB_INT32, "useDisplaceMap", "TRUE (1): Apply watereffect displace distortion"},
+ { GIMP_PDB_FLOAT, "displaceStrength", "displace strength (0.0 to 1.0, only relevant in case useDisplaceMap is TRUE)"},
+ { GIMP_PDB_INT32, "createImage", "TRUE (1): create a new image with n copies of the input drawable and render complete animation effect on those copies. FALSE (0): render only one phase of the anim effect on the input drawable"},
+ { GIMP_PDB_INT32, "nframes", "number of layer to be created in case createImage option is TRUE (1)"},
+ { GIMP_PDB_INT32, "seed1", "Seed for creating random pattern (cloud1 layer) use 0 for random value"},
+ { GIMP_PDB_INT32, "seed2", "Seed for creating random pattern (cloud2 layer) use 0 for random value"},
+ { GIMP_PDB_DRAWABLE, "cloudLayer1", "-1 automatically create cloud layer1 that is required to render or pass a layer id that was returned in a previous call to this plug-in (for rendering the next phase of the effect in one call per frame manner)"},
+ { GIMP_PDB_DRAWABLE, "cloudLayer2", "-1 automatically create cloud layer2 that is required to render or pass a layer id that was returned in a previous call to this plug-in (for rendering the next phase of the effect in one call per frame manner)"}
+ };
+ static int nargs = sizeof(args) / sizeof(args[0]);
+
+ static GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_DRAWABLE, "imageId", "the image that was newly created (when creatImage is TRUE) or otherwise the image id tha contains the processed drawable" },
+ { GIMP_PDB_DRAWABLE, "cloudLayer1", "the pattern cloud layer1" },
+ { GIMP_PDB_DRAWABLE, "cloudLayer2", "the pattern cloud layer2" }
+ };
+ static int nreturn_vals = sizeof(return_vals) / sizeof(return_vals[0]);
+
+
+ static waterpattern_val_t watervals;
+
+ static GimpLastvalDef lastvals[] =
+ {
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_FALSE, watervals.scalex, "scalex"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_FALSE, watervals.scaley, "scaley"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, watervals.blendNum, "blendNum"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, watervals.shiftPhaseX, "shiftPhaseX"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, watervals.shiftPhaseY, "shiftPhaseY"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, watervals.useHighlights, "useHighlights"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, watervals.highlightOpacity, "highlightOpacity"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, watervals.useDisplaceMap, "useDisplaceMap"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, watervals.displaceStrength, "displaceStrength"),
+ GIMP_LASTVALDEF_GBOOLEAN (GIMP_ITER_FALSE, watervals.createImage, "createImage"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, watervals.nframes, "nframes"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, watervals.seed1, "seed1"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, watervals.seed2, "seed2"),
+ GIMP_LASTVALDEF_DRAWABLE (GIMP_ITER_TRUE, watervals.cloudLayer1, "cloudLayer1"),
+ GIMP_LASTVALDEF_DRAWABLE (GIMP_ITER_TRUE, watervals.cloudLayer2, "cloudLayer2")
+ };
+
+ gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+ /* registration for last values buffer structure (for animated filter apply) */
+ gimp_lastval_desc_register(PLUG_IN_NAME,
+ &watervals,
+ sizeof(watervals),
+ G_N_ELEMENTS (lastvals),
+ lastvals);
+
+ /* the actual installation of the waterpattern plugin */
+ gimp_install_procedure (PLUG_IN_NAME,
+ PLUG_IN_DESCRIPTION,
+ "This Plugin generates an animated water effect that looks like the bottom of a ripple tank."
+ " This Plugin can render the animation in one call, where n copies of the input drawable is copied n-times to a newly created image. "
+ " To render just one frame/phase of the animated effect this plug-in can be called on multiple layers with varying shiftPhase parameters."
+ " (note that GIMP-GAP provides such animated filter calls) "
+ ,
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ GAP_VERSION_WITH_DATE,
+ N_("Water Pattern..."),
+ PLUG_IN_IMAGE_TYPES,
+ GIMP_PLUGIN,
+ nargs,
+ nreturn_vals,
+ args,
+ return_vals);
+
+
+
+ {
+ /* Menu names */
+ const char *menupath_image_video_layer_colors = N_("<Image>/Video/Layer/Render/");
+
+ gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_video_layer_colors);
+ }
+
+} /* end query */
+
+
+/* --------------------------------------
+ * run
+ * --------------------------------------
+ */
+static void
+run(const gchar *name
+ , gint nparams
+ , const GimpParam *param
+ , gint *nreturn_vals
+ , GimpParam **return_vals)
+{
+ waterpattern_val_t l_cuvals;
+ WaterPatternDialog *wcd = NULL;
+ waterpattern_context_t waterpattern_context;
+ waterpattern_context_t *ctxt;
+
+ gint32 l_image_id = -1;
+ gint32 l_drawable_id = -1;
+ gint32 l_handled_drawable_id = -1;
+
+
+
+ /* Get the runmode from the in-parameters */
+ GimpRunMode run_mode = param[0].data.d_int32;
+
+ ctxt = &waterpattern_context;
+ ctxt->run_mode = run_mode;
+
+ /* status variable, use it to check for errors in invocation usualy only
+ during non-interactive calling */
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ /*always return at least the status to the caller. */
+ static GimpParam values[N_RET_VALUES];
+
+
+ INIT_I18N();
+
+ /* initialize the return of the status */
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_DRAWABLE;
+ values[1].data.d_int32 = -1;
+ values[2].type = GIMP_PDB_DRAWABLE;
+ values[2].data.d_int32 = -1;
+ values[3].type = GIMP_PDB_DRAWABLE;
+ values[3].data.d_int32 = -1;
+ *nreturn_vals = N_RET_VALUES;
+ *return_vals = values;
+
+
+ /* get image and drawable */
+ l_image_id = param[1].data.d_int32;
+ l_drawable_id = param[2].data.d_drawable;
+
+ /* Possibly retrieve data from a previous run, otherwise init with default values */
+ p_int_cuvals(&l_cuvals);
+
+
+ if(status == GIMP_PDB_SUCCESS)
+ {
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ wcd = g_malloc (sizeof (WaterPatternDialog));
+ wcd->run = FALSE;
+ wcd->drawable_id = l_drawable_id;
+ /* Get information from the dialog */
+ do_dialog(wcd, &l_cuvals);
+ wcd->show_progress = TRUE;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* check to see if invoked with the correct number of parameters */
+ if (nparams == 18)
+ {
+
+ l_cuvals.scalex = param[3].data.d_float;
+ l_cuvals.scalex = param[4].data.d_float;
+ l_cuvals.blendNum = param[5].data.d_int32;
+ l_cuvals.shiftPhaseX = param[6].data.d_float;
+ l_cuvals.shiftPhaseY = param[7].data.d_float;
+ l_cuvals.useHighlights = param[8].data.d_int32;
+ l_cuvals.highlightOpacity = param[9].data.d_float;
+ l_cuvals.useDisplaceMap = param[10].data.d_int32;
+ l_cuvals.displaceStrength = param[11].data.d_float;
+ l_cuvals.createImage = param[12].data.d_int32;
+ l_cuvals.nframes = param[13].data.d_int32;
+ l_cuvals.seed1 = param[14].data.d_int32;
+ l_cuvals.seed2 = param[15].data.d_int32;
+ l_cuvals.cloudLayer1 = param[16].data.d_drawable;
+ l_cuvals.cloudLayer2 = param[17].data.d_drawable;
+
+ p_check_for_valid_cloud_layers(&l_cuvals);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ wcd = g_malloc (sizeof (WaterPatternDialog));
+ wcd->run = TRUE;
+ wcd->show_progress = TRUE;
+ wcd->createNewPatterns = FALSE;
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_NAME, &l_cuvals);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (wcd == NULL)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Run the main function */
+ if(wcd->run)
+ {
+ gboolean success;
+
+ gimp_image_undo_group_start (l_image_id);
+ success = p_run_renderWaterPattern(l_drawable_id, &l_cuvals, ctxt);
+ l_handled_drawable_id = l_drawable_id;
+ gimp_image_undo_group_end (l_image_id);
+
+ if(success)
+ {
+ /* Store variable states for next run */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ gimp_set_data(PLUG_IN_NAME, &l_cuvals, sizeof(l_cuvals));
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR; /* dialog ended with cancel button */
+ }
+
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR; /* dialog ended with cancel button */
+ }
+
+ /* If run mode is interactive, flush displays, else (script) don't
+ do it, as the screen updates would make the scripts slow */
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ gimp_displays_flush ();
+ }
+ }
+ values[0].data.d_status = status;
+ values[1].data.d_int32 = ctxt->image_id; /* return the image id of handled layer (or of the newly created anim image */
+ values[2].data.d_int32 = l_cuvals.cloudLayer1; /* return the id of cloud1 layer */
+ values[3].data.d_int32 = l_cuvals.cloudLayer2; /* return the id of cloud2 layer */
+
+} /* end run */
diff --git a/gap/gimplastvaldesc.h b/gap/gimplastvaldesc.h
index 3796b30..00b691e 100644
--- a/gap/gimplastvaldesc.h
+++ b/gap/gimplastvaldesc.h
@@ -87,7 +87,7 @@ typedef enum
#define GIMP_LASTVALDEF_DOUBLE(flag,elem,name) { GIMP_LASTVAL_DOUBLE, &elem, sizeof(elem), flag, name }
#define GIMP_LASTVALDEF_DRAWABLE(flag,elem,name) { GIMP_LASTVAL_DRAWABLE, &elem, sizeof(elem), flag, name }
#define GIMP_LASTVALDEF_GINTDRAWABLE(flag,elem,name) { GIMP_LASTVAL_GINTDRAWABLE, &elem, sizeof(elem), flag, name }
-#define GIMP_LASTVALDEF_GBOOLEAN(flag,elem,name) { GIMP_LASTVAL_BOOLEAN, &elem, sizeof(elem), flag, name }
+#define GIMP_LASTVALDEF_GBOOLEAN(flag,elem,name) { GIMP_LASTVAL_GBOOLEAN, &elem, sizeof(elem), flag, name }
#define GIMP_LASTVALDEF_ENUM(flag,elem,name) { GIMP_LASTVAL_ENUM, &elem, sizeof(elem), flag, name }
#define GIMP_LASTVALDEF_GUINT(flag,elem,name) { GIMP_LASTVAL_GUINT, &elem, sizeof(elem), flag, name }
#define GIMP_LASTVALDEF_GUINT32(flag,elem,name) { GIMP_LASTVAL_GUINT32, &elem, sizeof(elem), flag, name }
@@ -156,6 +156,8 @@ typedef enum
#define GIMP_ITER_TRUE 1
#define GIMP_ITER_FALSE 0
+#define GAP_LASTVAL_KEY_ANIMATED_CALL_INFO "GAP_LASTVAL_KEY_ANIMATED_CALL_INFO"
+
typedef struct _GimpLastvalDef GimpLastvalDef;
struct _GimpLastvalDef
@@ -177,6 +179,14 @@ typedef struct GimpLastvalDescType
gchar elem_name[100]; /* parameter name */
} GimpLastvalDescType;
+
+typedef struct
+{
+ gboolean animatedCallInProgress;
+ gint32 total_steps;
+ gdouble current_step; /* current step respecting acceleration characteristics */
+} GapLastvalAnimatedCallInfo;
+
/* ----------------------------------
* gimp_lastval_desc_register
* ----------------------------------
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6a0a80c..c193302 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,6 +16,7 @@ gap/gap_decode_mplayer.c
gap/gap_decode_xanim.c
gap/gap_filter_foreach.c
gap/gap_filter_main.c
+gap/gap_fire_pattern.c
gap/gap_fmac_base.c
gap/gap_fmac_main.c
gap/gap_frontends_main.c
@@ -58,6 +59,7 @@ gap/gap_vex_dialog.c
gap/gap_vex_exec.c
gap/gap_video_index_creator.c
gap/gap_vex_main.c
+gap/gap_water_pattern.c
gap/gap_wr_color_balance.c
gap/gap_wr_color_curve.c
gap/gap_wr_color_huesat.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]