[gimp-gap] added new fetures (morph, storyboard wrapper color-balance) some internal bugfixes, applied patch #6
- From: Wolfgang Hofer <wolfgangh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp-gap] added new fetures (morph, storyboard wrapper color-balance) some internal bugfixes, applied patch #6
- Date: Thu, 9 Sep 2010 08:35:46 +0000 (UTC)
commit 63e70e2d653be55db477d8070b52c3dc11d3e1a8
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date: Thu Sep 9 10:37:35 2010 +0200
added new fetures (morph, storyboard wrapper color-balance) some internal bugfixes, applied patch #625667 (various spelling fixes)
ChangeLog | 298 ++
NEWS | 38 +-
configure.in | 14 +-
docs/reference/txt/INTRODUCTION.txt | 9 +-
docs/reference/txt/STORYBOARD_FILE_DOC.txt | 103 +-
docs/reference/txt/gap_gimprc_params.txt | 44 +-
docs/reference/txt/plug-in-colormask.txt | 31 +
extern_libs/README_extern_libs | 17 +-
gap/Makefile.am | 27 +
gap/TESTPROT_iter_ALT | 2 +-
gap/gap_colordiff.c | 363 +++
gap/gap_colordiff.h | 142 +
gap/gap_colormask_dialog.c | 1848 +++++++++++
gap/gap_colormask_dialog.h | 45 +
gap/gap_colormask_exec.c | 4650 ++++++++++++++++++++++++++++
gap/gap_colormask_exec.h | 62 +
gap/gap_colormask_file.c | 166 +
gap/gap_colormask_file.h | 91 +
gap/gap_colormask_main.c | 467 +++
gap/gap_decode_mplayer.c | 2 +-
gap/gap_edge_detection.c | 421 +++
gap/gap_edge_detection.h | 63 +
gap/gap_fmac_base.c | 4 +-
gap/gap_frame_fetcher.c | 150 +-
gap/gap_frame_fetcher.h | 11 +
gap/gap_layer_copy.c | 38 +
gap/gap_layer_copy.h | 2 +
gap/gap_lib.c | 41 +-
gap/gap_lib.h | 1 +
gap/gap_libgimpgap.h | 5 +
gap/gap_locate.c | 843 +++++
gap/gap_locate.h | 83 +
gap/gap_main.c | 10 +-
gap/gap_match.c | 2 +-
gap/gap_mod_layer.c | 76 +-
gap/gap_mod_layer.h | 12 +
gap/gap_mod_layer_dialog.c | 63 +-
gap/gap_mod_layer_dialog.h | 4 +
gap/gap_morph_dialog.c | 512 ++--
gap/gap_morph_dialog.h | 7 +
gap/gap_morph_exec.c | 682 ++++-
gap/gap_morph_exec.h | 2 +-
gap/gap_morph_main.c | 203 +-
gap/gap_morph_main.h | 26 +
gap/gap_morph_shape.c | 1312 ++++++++
gap/gap_morph_shape.h | 102 +
gap/gap_morph_tween_dialog.c | 848 +++++-
gap/gap_morph_tween_dialog.h | 11 +-
gap/gap_mov_dialog.h | 1 +
gap/gap_mov_exec.c | 8 +
gap/gap_player_dialog.c | 20 +-
gap/gap_stock.c | 69 +
gap/gap_stock.h | 6 +-
gap/gap_story_att_trans_dlg.c | 90 +-
gap/gap_story_dialog.c | 104 +-
gap/gap_story_file.c | 560 +++--
gap/gap_story_file.h | 62 +-
gap/gap_story_main.h | 64 +-
gap/gap_story_properties.c | 288 ++-
gap/gap_story_render_audio.c | 2 +-
gap/gap_story_render_lossless.c | 3 +
gap/gap_story_render_processor.c | 598 +++-
gap/gap_story_render_types.h | 32 +-
gap/gap_story_section_properties.c | 2 +-
gap/gap_story_syntax.c | 19 +
gap/gap_story_syntax.h | 2 +
gap/gap_story_vthumb.c | 16 +-
gap/gap_thumbnail.c | 4 +-
gap/gap_vex_dialog.c | 2 +-
gap/gap_video_index_creator.c | 6 +-
gap/gap_wr_color_balance.c | 749 +++++
libgapbase/gap_val_file.c | 23 +-
libgapbase/gap_val_file.h | 1 +
libgapvidapi/gap_vid_api_ffmpeg.c | 543 ++--
libgapvidapi/gap_vid_api_mpeg3.c | 20 +-
libgapvidapi/gap_vid_api_mpeg3toc.c | 6 +-
libgapvidutil/gap_gve_raw.c | 4 +-
po/POTFILES.in | 5 +
vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c | 6 +-
vid_enc_ffmpeg/gap_enc_ffmpeg_main.c | 266 ++-
vid_enc_ffmpeg/gap_enc_ffmpeg_main.h | 40 +
vid_enc_ffmpeg/gap_enc_ffmpeg_par.c | 19 +
82 files changed, 16450 insertions(+), 1143 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 046acf4..e9a41d2 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,301 @@
+2010-09-09 Wolfgang Hofer <hof gimp org>
+
+- applied patch #625667 that fixed various spelling mistakes.
+ (most of them occured in comments, some in message texts)
+
+
+ * gap/gap_decode_mplayer.c
+ * gap/gap_fmac_base.c
+ * gap/gap_lib.c
+ * gap/gap_main.c
+ * gap/gap_match.c
+ * gap/gap_mod_layer.c
+ * gap/gap_morph_dialog.c
+ * gap/gap_morph_main.c
+ * gap/gap_story_att_trans_dlg.c
+ * gap/gap_story_dialog.c
+ * gap/gap_story_file.c
+ * gap/gap_story_properties.c
+ * gap/gap_story_render_audio.c
+ * gap/gap_story_section_properties.c
+ * gap/gap_story_vthumb.c
+ * gap/gap_thumbnail.c
+ * gap/gap_vex_dialog.c
+ * gap/gap_video_index_creator.c
+ * gap/TESTPROT_iter_ALT
+ * libgapbase/gap_val_file.c
+ * libgapvidapi/gap_vid_api_mpeg3.c
+ * libgapvidapi/gap_vid_api_mpeg3toc.c
+
+
+--- 2010-08-23
+
+- added wrapper plug-in to run gimp_color_balance tool as animated filter.
+
+
+ * gap/Makefile.am
+ * gap/gap_wr_color_balance.c # NEW file
+ * po/POTFILES.in
+
+--- 2010-08-15 (Morph features)
+
+- Added features to the Morph plug-in to support
+ more convenient creation of workpoints and lead to better
+ results (especially for morping tweens of a video)
+
+- Created new plug-in "Morph Workpoint Generator"
+ (in the menupath Video/Morph/)
+ Generates workpointfiles for each frame in the specified frame range.
+
+- added new feature to morph plug-in "Create Tween Frames"
+ that creates a directory with (renumbered) copies of the processed frames
+ and additionally generated tween frames that are rendered via fade or morphing.
+ 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").
+
+
+ example1:
+ generate 2 tween frames between each handled frame.
+ master_tween_steps = 2, tween_subdir = "with_tweens"
+
+ frame_000001.xcf == copied to ==> with_tweens/frame_000001.xcf
+ render via morph ==> with_tweens/frame_000002.xcf
+ render via morph ==> with_tweens/frame_000003.xcf
+
+ frame_000002.xcf == copied to ==> with_tweens/frame_000004.xcf
+ render via morph ==> with_tweens/frame_000005.xcf
+ render via morph ==> with_tweens/frame_000006.xcf
+
+
+ frame_000003.xcf == copied to ==> with_tweens/frame_000007.xcf
+
+
+ example2:
+ generate 1 tween frame between each handled frame
+ master_tween_steps = 1, tween_subdir = "with_tweens"
+ in this example frame 2 is missing, therefore 3 tweens
+ are generates between frame 1 and 3.
+
+ frame_000001.xcf == copied to ==> with_tweens/frame_000001.xcf
+ render via morph ==> with_tweens/frame_000002.xcf
+ render via morph ==> with_tweens/frame_000003.xcf
+ render via morph ==> with_tweens/frame_000004.xcf
+
+ frame_000003.xcf == copied to ==> with_tweens/frame_000005.xcf
+ render via morph ==> with_tweens/frame_000006.xcf
+
+ frame_000004.xcf == copied to ==> with_tweens/frame_000007.xcf
+
+- Morph dialog provides new features
+ for edge detection based automatic workpoint generation.
+ This feature is available when CTRL modifier key is hold down while
+ the Shape button is pressed.
+
+
+
+- morph dialog now supports a special way to set morph workpoints
+ that automatically tries to find the corresponding point in the other image
+ within a specified locate radius.
+ This feature is useful when both images contain similar data.
+
+ This new feature is available via CTRL modifier key when setting morph points
+ (in the modes set and move)
+
+
+ * gap/Makefile.am
+ * gap/gap_colordiff.c [.h] # NEW files
+ * gap/gap_locate.c [.h] # NEW files
+ * gap/gap_edge_detection.c [.h] # NEW files
+ * gap/gap_morph_shape.c [.h] # NEW files
+
+ * gap/gap_morph_dialog.c [.h]
+ * gap/gap_morph_main.c [.h]
+ * gap/gap_libgimpgap.h
+ * gap/gap_morph_tween_dialog.c [.h]
+
+ * docs/reference/txt/INTRODUCTION.txt
+
+
+-- 2010-07-30 (started update to ffmpeg release ffmpeg 0.6)
+
+ status: partly works, but problems to read some videoformats
+ - mjpeg (my Olympus SP-560UZ digicam uses this format)
+ - RealMedia
+
+ switch to ffmpeg-0.6 shall not be done until fixing this,
+ because update to new release shall not break
+ compatibility with videoformats that already worked fine with the
+ ffmpeg-0.5 based gimp-gap video api.
+
+- fixed gap_gve_raw_RGB_drawable_encode
+ that method did convert to BGR colormodel (and not to RGB as it should)
+ but this was compensated by another bug when using ffmpeg conversion via deprecated
+ img_convert.
+
+- ffmpeg API no longer supports the procedure img_convert.
+ gimp-gap now uses sws_scale (that was already part of ffmpeg-0.5) and provides
+ the colormodel conversions required to feed the codecs of libavcodec.
+
+- replaced calls to deprecated video/audio decoder procedures
+ avcodec_decode_video ==> avcodec_decode_video2
+ avcodec_decode_audio2 ==> avcodec_decode_audio3
+
+- ffmpeg-0.6 configure now requires --enable-nonfree when built with
+ non free libs (gimp-gap configure now adds this option when
+ building with the non free libfaac)
+
+- support to compile gimp-gap with ffmpeg-0.6,
+ but keep older ffmpeg-0.5 release as default
+
+- build gimp-gap with latest versions of xvid and x264 libraries is currently not possible.
+ note that the gap configure script checks the same way as the included ffmpeg 0.5 version
+ which does not accept new versions of those libraries.
+ therefore a change in the gap configure.in is not sufficient !
+
+ ==> for support of newer xvid and x264 libs porting gimp-gap to newer ffmpeg
+ is also required
+
+
+
+ * configure.in
+ * libgapvidapi/gap_vid_api_ffmpeg.c
+ * extern_libs/README_extern_libs
+ * extern_libs/ffmpeg.tar.gz
+ * vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+ * vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
+ * vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+ * libgapvidutil/gap_gve_raw.c
+
+
+--- 2010-05-xx
+
+- workaround for gtk_button_new_from_stock (); problem.
+
+ Short description of the problem:
+ buttons created with gtk_button_new_from_stock sometimes do not show the icon
+ (at least in case the theme is configured as "Show symbols in menus" = false")
+
+ Some of the gimp-gap buttons do not have any text
+ (For example the play, pause and reverse buttons in the playback dialog)
+ and appear as very small completely empty buttons
+ that makes it very hard to use with such a theme configuration.
+
+
+ Here are some examples of stock_ids where the button does NOT show the image:
+ GAP_STOCK_PLAY
+ GAP_STOCK_PAUSE
+ GAP_STOCK_PLAY_REVERSE
+
+ GIMP_STOCK_RESET
+
+ GTK_STOCK_SAVE
+ GTK_STOCK_CLOSE
+
+ Note that other plug-ins (map object, lighting, gfig) that are using stock images
+ in similar way as gimp-gap have the same problem.
+
+ The workaround uses the new procedure gap_stock_button_new
+ instead of gtk_button_new_from_stock to create buttons with stock images
+ and is used only where it makes no sense to disable the images via themes configuration.
+
+- support for configuration of the gap frame fetcher cache by new gimprc parameters:
+
+ (gap_ffetch_max_img_cache_elements "18")
+ (gap_ffetch_max_gvc_cache_elements "6")
+ (gap_ffetch_gva_frames_to_keep_cached "36")
+
+ the gap frame fetcher cache settings are relevant for storyboard
+ and filtermacro processing.
+
+
+
+- fixed sporadic crash in the storyboard render processor.
+ work copy duplicates of images that are cached as gimp image in the gap frame fetcher cache
+ now do detach the image parasite "GAP-IMAGE-CACHE-PARASITE" that marks the image as member of the cache.
+ In case rendering of one frame requires more than one image fetch operations this parasite
+ did sometimes cause an unexpected delete of the duplicate during fetch
+ when the maximum number of cached images is already reached.
+
+ * gap/gap_player_dialog.c
+ * gap/gap_stock.c [.h]
+ * gap/gap_frame_fetcher.c [.h]
+ * gap/gap_story_render_processor.c
+ * docs/refernce/txt/gap_gimprc_params.txt
+
+
+----- 2010-04-xx ----
+
+- modify frames plug-in now keeps dialog window open to show progress while processing frames
+ and provides button to cancel processing.
+ The old behavior based on gimp_progress was not sufficient, especially in case
+ when a filter is applied and the called filter also uses gimp_progresss
+ for local progress. This resulted in confusing concurrent progress updates...
+
+- 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 applying the constant background
+ as colormask on all frames.
+
+ NOTICE:
+ The current implementation is NOT YET READY for use.
+ There are more implementation variants, most of them are experimental
+ some of them will be removed after testing.
+ The dialog window currently hides the options for those experimental variants
+ (until implentation is finished, those options are only visible for USERNAME=hof).
+
+
+
+- New storyboard processing feature mask anchor mode CLIP_COLOR
+ applies the selected mask as colormask
+
+- Storyboard support rotate transitions
+
+- New storyboard processing feature to add external transparency
+ for clip type movie. This is done via format string that refers to
+ the external frames (typical gray image frames)
+ the format string has placeholder %s (replaced by basename of the video)
+ and %06d (replaced by the currently processed frame number)
+
+ The format string can be specified in the storyboard master properties dialog
+ (similar to the area replacement for logo processing)
+
+ * docs/reference/txt/plug-in-gap-move-path.txt
+ * libgapbase/gap_val_file.c [.h]
+ * gap/gap_mod_*
+
+ * NEWS
+ * docs/reference/txt/INTRODUCTION.txt
+ * docs/reference/txt/STORYBOARD_FILE_DOC.txt
+ * docs/reference/txt/plug-in-colormask.txt # NEW FILE
+
+ * gap/gap_colormask_main.c # NEW FILE
+ * gap/gap_colormask_exec.c [.h] # NEW FILES
+ * gap/gap_colormask_file.c [.h] # NEW FILES
+ * gap/gap_colormask_dialog.c [.h] # NEW FILES
+ * gap/Makefile.am
+ * gap/gap_libgimpgap.h
+ * gap/gap_layer_copy.c [.h]
+
+ * gap/gap_morph_dialog.c
+
+
+ * gap/gap_lib.c
+ * gap/gap_lib.h
+ * gap/gap_story_att_trans_dlg.c
+ * gap/gap_story_dialog.c
+ * gap/gap_story_file.c
+ * gap/gap_story_file.h
+ * gap/gap_story_main.h
+ * gap/gap_story_properties.c
+ * gap/gap_story_render_lossless.c
+ * gap/gap_story_render_processor.c
+ * gap/gap_story_render_types.h
+ * gap/gap_story_syntax.c
+ * gap/gap_story_syntax.h
+
+
2010-07-04 Wolfgang Hofer <hof gimp org>
- added gitignore files (applied patch provided by Carlo Baldassi #623050)
diff --git a/NEWS b/NEWS
index d395b06..ca549e5 100644
--- a/NEWS
+++ b/NEWS
@@ -19,7 +19,43 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
Th GAP dbbrowser now supports acceleration characteristic graph and spinbutton
(that replaces the "Apply Varying" button of older GIMP_GAP releases)
-- some other bugfixes (See ChangeLog for details)
+- 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.
+
+- A new storyboard processing feature allows adding external transparency
+ for clip type movie. This is done via format string that refers to
+ the external frames (typical gray image frames)
+ the format string has placeholder %s (replaced by basename of the video)
+ and %06d (replaced by the currently processed frame number)
+
+ The format string can be specified in the storyboard master properties dialog
+
+- Added features to the Morph plug-in to support
+ more convenient creation of workpoints and lead to better
+ results (especially for morping tweens of a video)
+
+ new plug-in "Video/Morph/Morph Workpoint Generator"
+ Generates workpointfiles for each frame in the specified frame range.
+
+ new feature for the morph plug-in "Video/Morph/Create Tween Frames"
+ that creates a directory with (renumbered) copies of the processed frames
+ and additionally generated tween frames that are rendered via fade or morphing.
+ 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").
+
+- support to run gimp_color_balance tool as animated filter
+ (added wrapper plug-in).
+
+- some bugfixes (See ChangeLog for details)
Here is a short overview whats new in GIMP-GAP-2.6.0:
diff --git a/configure.in b/configure.in
old mode 100644
new mode 100755
index f9c7a98..a5b8998
--- a/configure.in
+++ b/configure.in
@@ -405,6 +405,7 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
FFMPEG_LIBAVCODEC_A="$FFMPEG_DIR/libavcodec/${LIBPREF}avcodec${LIBSUF}"
FFMPEG_LIBAVUTIL_A="$FFMPEG_DIR/libavutil/${LIBPREF}avutil${LIBSUF}"
FFMPEG_LIBAVDEVICE_A="$FFMPEG_DIR/libavdevice/${LIBPREF}avdevice${LIBSUF}"
+ FFMPEG_LIBSWSCALE_A="$FFMPEG_DIR/libswscale/${LIBPREF}swscale${LIBSUF}"
dnl
dnl ffmpeg can be configured to use external codec libs x264, xvid ....
dnl options for ffmpeg ext libs configuration will be passed to ffmpeg/configure
@@ -415,8 +416,8 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
FFMPEG_EXTLIBS="$FFMPEG_EXTLIBS -lws2_32"
fi
- GAP_VLIBS_FFMPEG=" $FFMPEG_LIBAVDEVICE_A $FFMPEG_LIBAVFORMAT_A $FFMPEG_LIBAVCODEC_A $FFMPEG_LIBAVUTIL_A $FFMPEG_EXTLIBS "
- GAP_VINCS_FFMPEG=" -I$FFMPEG_DIR -I$FFMPEG_DIR/libavcodec -I$FFMPEG_DIR/libavformat -I$FFMPEG_DIR/libavutil -I$FFMPEG_DIR/libavdevice "
+ GAP_VLIBS_FFMPEG=" $FFMPEG_LIBAVDEVICE_A $FFMPEG_LIBAVFORMAT_A $FFMPEG_LIBAVCODEC_A $FFMPEG_LIBAVUTIL_A $FFMPEG_LIBSWSCALE_A $FFMPEG_EXTLIBS "
+ GAP_VINCS_FFMPEG=" -I$FFMPEG_DIR -I$FFMPEG_DIR/libavcodec -I$FFMPEG_DIR/libavformat -I$FFMPEG_DIR/libavutil -I$FFMPEG_DIR/libavdevice -I$FFMPEG_DIR/libswscale"
vid_ffmpeg_warning="
$x264_warn
@@ -450,9 +451,9 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
ff_pass_through_flags=" $ff_pass_through_flags --cc=$CC "
fi
- FFMPEG_CONFIGURE_OPTIONS=" $ff_pass_through_flags $FFMPEG_CONFIGURE_OPTIONS"
-
+ FFMPEG_CONFIGURE_OPTIONS=" $ff_pass_through_flags $FFMPEG_CONFIGURE_OPTIONS --enable-swscale"
+ FFMPEG_ENABLE_NONFREE=""
dnl configure ffmpeg bzip2 usage
if test "x$FF_BZIP2" != "x"; then
@@ -467,6 +468,7 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
fi
if test "x$FF_LIBFAAC" != "x"; then
FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --enable-libfaac"
+ FFMPEG_ENABLE_NONFREE="--enable-nonfree"
fi
if test "x$FF_LIBFAAD" != "x"; then
FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --enable-libfaad"
@@ -481,6 +483,9 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
if test "x$FF_LIBXVID" != "x"; then
FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --enable-libxvid"
fi
+
+ dnl in case nonfree libs are used ffmeg 0.6 configure requires --enable-nonfree
+ FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS $FFMPEG_ENABLE_NONFREE"
if test "x$os_win32" = "xyes"; then
dnl here we could check for required win32 api version on MonGW Win32 Systems.
@@ -934,6 +939,7 @@ AC_SUBST(FFMPEG_DIR)
AC_SUBST(FFMPEG_LIBAVFORMAT_A)
AC_SUBST(FFMPEG_LIBAVCODEC_A)
AC_SUBST(FFMPEG_LIBAVUTIL_A)
+AC_SUBST(FFMPEG_LIBSWSCALE_A)
AC_SUBST(LMPEG3_DIR)
AC_SUBST(LMPEG3_A)
diff --git a/docs/reference/txt/INTRODUCTION.txt b/docs/reference/txt/INTRODUCTION.txt
index fefc8fe..8195f41 100644
--- a/docs/reference/txt/INTRODUCTION.txt
+++ b/docs/reference/txt/INTRODUCTION.txt
@@ -99,7 +99,7 @@ Output to videofile:
GIMP-GAP provides video encoder plug-ins
to save a series of input frame images or videoclips
as videofile.
- This process is called encoding an usually
+ This process is called encoding and usually
does compresses the processed frames and audiodata.
GIMP-GAP encoder plug-ins can operate on
@@ -304,6 +304,9 @@ Internal PDB names and menu names of GIMP-GAP plug-ins
plug_in_gap_exchg "<Image>/Video/Exchange Frame"
plug_in_gap_modify "<Image>/Video/Frames Modify"
plug-in-gap-morph-layers "<Image>/Video/Morph"
+ plug_in_gap_morph_tween "<Image>/Video/Morph Tweenframes"
+ plug_in_gap_morph_one_tween "<Image>/Video/Morph One Tween"
+ plug_in_gap_morph_workpoints "<Image>/Video/Morph Workpoint Generator"
plug_in_gap_move "<Image>/Video/Move Path"
plug_in_gap_navigator "<Image>/Video/VCR Navigator"
plug_in_gap_range_convert "<Image>/Video/Frames Convert"
@@ -317,10 +320,12 @@ Internal PDB names and menu names of GIMP-GAP plug-ins
plug-in-gap-storyboard-edit "<Image>/Video/Storyboard"
plug_in_gap_videoframes_player "<Image>/Video/Playback"
+ plug-in-layer-set-alpha-by-colormask "<Image>/Video/Layer/Attributes/"
+
+
plug-in-gap-vid-encode-master "<Image>/Video/Encode/Master Videoencoder"
plug_in_gap_mpeg2encode "<Image>/Video/Encode/MPEG2 (mpeg2encode)"
plug_in_gap_mpeg_encode "<Image>/Video/Encode/MPEG1 (mpeg_encode)"
-
plug_in_gap_goto "<Image>/Video/Go To/Any Frame"
plug_in_gap_first "<Image>/Video/Go To/First Frame"
plug_in_gap_last "<Image>/Video/Go To/Last Frame"
diff --git a/docs/reference/txt/STORYBOARD_FILE_DOC.txt b/docs/reference/txt/STORYBOARD_FILE_DOC.txt
index 654d8cb..6d0660a 100644
--- a/docs/reference/txt/STORYBOARD_FILE_DOC.txt
+++ b/docs/reference/txt/STORYBOARD_FILE_DOC.txt
@@ -1,4 +1,4 @@
-STORYBOARD_FILES 2010.02.15:
+STORYBOARD_FILES 2010.03.20:
General
@@ -74,6 +74,7 @@ VID_MASTER_FRAME_ASPECT width:16 height:9
VID_MASTER_SIZE width:320 height:200
VID_PREFERRED_DECODER libavformat
VID_MASTER_LAYERSTACK track1:Background
+VID_MASTER_INSERT_ALPHA format:"/path/file_%s_%06d.xcf"
AUD_MASTER_VOLUME volume:1.0
AUD_MASTER_SAMPLERATE samples_per_sec:44100
@@ -143,6 +144,7 @@ VID_PLAY_COLOR track:1 red:0.0 green:1.0 blue:0.0 alpha:1.0 nloops:1 \
VID_SILENCE track:1 nloops:1 wait_until_sec:0.0
# video attributes
+VID_ROTATE track:1 rotate_from:0.0 rotate_to:360.0 nframes:10
VID_OPACITY track:1 opacity_from:0.0 opacity_to:1.0 nframes:10
VID_ZOOM_X track:1 zoom_x_from:0.1 zoom_x_to:10.0 nframes:10
VID_ZOOM_Y track:1 zoom_y_from:0.1 zoom_y_to:10.0 nframes:10
@@ -243,6 +245,65 @@ VID_MASTER_LAYERSTACK track1:[Top | Background]
Background .... (track 1 is the background)
DEFAULT: Top
+
+VID_MASTER_INSERT_ALPHA
+ Specifies a format string that referes to an image or to a series of frame images
+ to provide transparency for the corresponding video frame.
+ This format string shall contain placeholders
+ %s for the basename of videoclips and
+ %06d for the 6 digit framenumber.
+
+ This format string will be checked each time when storyboard processing
+ fetches a video frame (from clip type VID_PLAY_MOVIE)
+ and will be evaluated to a corresponding frame/image filename.
+ If a frame image with this evaluated filename exists,
+ that image will be automatically loaded,
+ scaled to frame size and attached as alpha channel
+ to the fetched frame.
+ This feature offers individual transparency per video clip and frame.
+
+ Example1:
+ Have a Storyboard with:
+ VID_MASTER_INSERT_ALPHA format="/home/storyboards/video_alpha_frame/alpha_%s_%06d.png"
+ VID_PLAY_MOVIE track=2 file=my_clip.AVI from=12 to=20
+
+ When processing fetches frame number 14 of my_clip.AVI
+ it evaluates the alpha imagename by substitution
+ where
+ %s is replaced by "my_clip"
+ %06d is replaced by "000014"
+ and checks if the resulting alpha imagename exists:
+
+ /home/storyboards/video_alpha_frame/alpha_my_clip_000014.png
+
+ If there is such an image it will be added as alpha channel
+ to add transparency for the fetched video frame.
+
+ Example2:
+ Have a Storyboard with:
+ VID_MASTER_INSERT_ALPHA format="/home/storyboards/video_alpha_frame/alpha_%s.png"
+ VID_PLAY_MOVIE track=2 file=my_clip.AVI from=12 to=20
+
+ In this case there is just the placeholder for the videofile but no
+ placeholder for the framenumber.
+ In this case the alpha imagename evaluates to
+
+ /home/storyboards/video_alpha_frame/alpha_my_clip.png
+
+ for all frames of this video clip. If such a file exists it
+ is applied as alpha channel on all frames of my_clip.AVI
+
+
+
+ (1) Record Key ... VID_MASTER_INSERT_ALPHA
+ (2) format ... the format string
+ shall include
+ %s as placeholder for
+ videofile basename and
+ %06d as placeholder for the framenumber.
+
+
+
VID_PLAY_MOVIE
fetch frames from a videofile. (such as MPEG, AVI, ...
see GAP_VID_API description for Informations about
@@ -318,6 +379,13 @@ VID_PLAY_MOVIE
In this mode the mask is fixed to the master frame.
and does not follow transformations and movements
of the input frame.
+ "xcolor" scale mask to resulting layer size.
+ and apply as color mask.
+ In this case the image that is referred via mask_name
+ should be a color image.
+ the mask scales and moves according
+ to transformations of the input frame
+ where it is attached to.
[17] mask_stepsize: ... stepsize factor for the mask-frames.
a value of 2.0 speeds up the mask effect (by skipping
every 2nd frame in the assigned mask clip)
@@ -335,6 +403,13 @@ VID_PLAY_MOVIE
other positive values result in increasing speed, negative values
result in decreasing speed.
+ [21] colormask_file: ... a filename referring to a file with parameters for
+ applying a mask with the colormask filter.
+ the colormask filter makes pixels of the processed frame
+ transparent when they are equal or similar to the corresponding
+ pixel in the colormask.
+ typically the colormask represents the non-moving backround
+ that can be set transparent while moving objects are opaque.
@@ -367,6 +442,7 @@ VID_PLAY_ANIMIMAGE
[14] mask_disable: ... Same as described for VID_PLAY_MOVIE (see above)
[15] macsteps: ... Same as described for VID_PLAY_MOVIE (see above)
[16] macaccel: ... Same as described for VID_PLAY_MOVIE (see above)
+ [17] colormask_file: ... Same as described for VID_PLAY_MOVIE (see above)
@@ -403,6 +479,7 @@ VID_PLAY_FRAMES
[15] mask_disable: ... Same as described for VID_PLAY_MOVIE (see above)
[16] macsteps: ... Same as described for VID_PLAY_MOVIE (see above)
[17] macaccel: ... Same as described for VID_PLAY_MOVIE (see above)
+ [18] colormask_file: ... Same as described for VID_PLAY_MOVIE (see above)
VID_PLAY_IMAGE
fetch frame from a single imagefile. (Multilayer Images are handled as
@@ -422,6 +499,7 @@ VID_PLAY_IMAGE
[10] mask_disable: ... Same as described for VID_PLAY_MOVIE (see above)
[11] macsteps: ... Same as described for VID_PLAY_MOVIE (see above)
[12] macaccel: ... Same as described for VID_PLAY_MOVIE (see above)
+ [13] colormask_file: ... Same as described for VID_PLAY_MOVIE (see above)
@@ -445,6 +523,7 @@ VID_PLAY_COLOR
[11] mask_disable: ... Same as described for VID_PLAY_MOVIE (see above)
[12] macsteps: ... Same as described for VID_PLAY_MOVIE (see above)
[13] macaccel: ... Same as described for VID_PLAY_MOVIE (see above)
+ [14] colormask_file: ... Same as described for VID_PLAY_MOVIE (see above)
VID_SILENCE
This record is used to define Pauses in a videotrack.
@@ -459,6 +538,28 @@ VID_SILENCE
timestamp can be a framenumber (integer) or seconds (float)
DEFAULT: 0
+VID_ROTATE
+ This record is used to define rotate treansitions,
+ by changing the rotation angle of the processed frames in the specified Videotrack slightly from one value
+ to another.
+
+ (1) Record Key ... VID_ROTATE
+ (2) track ... integer tracknumer
+ (3) rotate_from ... Start rotation with this value
+ where 0.0 is and 360.0 is one full rotation
+ [4] rotate_to ... Change rotation to this value
+ DEFAULT: use same value as rotate_from
+ [5] nframes ... optional duration of the effect
+ in number of frames (integer) or seconds (float)
+ The duration Value 0 applies the TO value immediate.
+ DEFAULT: 0
+ [6] accel ... an integer value specifiying acceleration characteristic
+ for speed behavior of rotation changes.
+ where 1 (and 0) rotates the object with constant speed.
+ other positive values result in increasing rotation speed, negative values
+ result in decreasing speed.
+ DEFAULT: 0
+
VID_OPACITY
This record is used to define Fade Effects,
by changing the Opacity of the Videotrack slightly from one value
diff --git a/docs/reference/txt/gap_gimprc_params.txt b/docs/reference/txt/gap_gimprc_params.txt
index 89cc40b..a5071e0 100644
--- a/docs/reference/txt/gap_gimprc_params.txt
+++ b/docs/reference/txt/gap_gimprc_params.txt
@@ -4,8 +4,8 @@ Video Configuration (gimprc)
--------------------------------------------------------------------
There are optional configuration values for the GIMP-GAP video plug-ins.
-Those values are stored in your personal gimprc file (located in $HOME/.gimp-2.2/gimprc)
-or in the system-wide gimprc file (usually located at /usr/local/etc/gimp/2.2/gimprc)
+Those values are stored in your personal gimprc file (located in $HOME/.gimp-2.6/gimprc)
+or in the system-wide gimprc file (usually located at /usr/local/etc/gimp/2.6/gimprc)
If you edit gimprc files by hand, you must do this before you startup GIMP.
@@ -253,3 +253,43 @@ If you edit gimprc files by hand, you must do this before you startup GIMP.
# anaylse results files (even if they are still valid)
#
(video-gva-libavformat-video-analyse-persistent "yes")
+
+
+# gimp_gap frame fetcher configuration
+# ------------------------------------
+#
+# the gap frame fetcher cache is currently relevant for storyboard processing
+# and filtermacro processing. it is used to keep the configured
+# number of images, videohandles and videoframes in cache for potential
+# reuse in further processing steps to avoid multiple load or open/close
+# operations.
+#
+# images are cached as gimp images that are marked with a temporary image parasite
+# "GAP-IMAGE-CACHE-PARASITE"
+# those parasite identifies an image as member of the gimp_gap frame fetcher cache.
+# (gap does not open a display on such images)
+# setting the gap_ffetch_max_img_cache_elements too high can result in
+# slow processing because the gimp core starts to swap when too much
+# or too large images are loaded.
+#
+# using values up to 100 or more may speed up storyboard processing
+# on machines with large memory (4G or more).
+# (note: expect speed up only in case the storyboard has multiple references
+# to images, such as applying the same mask image to more frames
+# or using the same image to render multiple frames)
+#
+# default value is 18.
+#
+(gap_ffetch_max_img_cache_elements "18")
+
+# number of video handles to keep cached.
+# default value is 6
+(gap_ffetch_max_gvc_cache_elements "6")
+
+# number of video frames per video handle to keep cached.
+# note that video frames are not cached as gimp image.
+# video frames are cached by the gimp gap video api that allocates
+# the memory (and will fail in case not enough memory is available to hold the configured
+# number of video frames)
+# default value is 36
+(gap_ffetch_gva_frames_to_keep_cached "36")
diff --git a/docs/reference/txt/plug-in-colormask.txt b/docs/reference/txt/plug-in-colormask.txt
new file mode 100644
index 0000000..408ba22
--- /dev/null
+++ b/docs/reference/txt/plug-in-colormask.txt
@@ -0,0 +1,31 @@
+"plug-in-colormask"
+
+Colormask Filter
+
+ Start from Menu:
+ <Image>/Video/Layer/Attributes/Apply Colormask
+
+ The colormask filter can apply transparency for pixels matching the colormask image.
+ It is 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 provides more flexibility than the bluebox filter
+ because it can operate on frames where the background is not just made of shades
+ of the same color.
+ But for good results the moving object shall not contain the same colors as the background.
+
+ In case the colormask layer has an alpha channel it is used to
+ define protected areas, e.g the alpha channel of the colormask controls
+ where the filter shall be applied (opaque) or not (transparent pixels are protected).
+ Pixels where the corresponding alpha channel in the colormask is below the specifed
+ triggerAlpha value are not affected by the colormask filter.
+ This is useful when the colormask filter runs non-interactive
+ under control of storyboard render processing
+ on frame images that are created internally where the image has no selction.
+
+
+
+ The colormask filter is not yet fully implemented.
+
+ TODO: add more detailed feature description here when implementation is finished.
diff --git a/extern_libs/README_extern_libs b/extern_libs/README_extern_libs
old mode 100644
new mode 100755
index f1b9a0e..5d69127
--- a/extern_libs/README_extern_libs
+++ b/extern_libs/README_extern_libs
@@ -3,7 +3,7 @@ as sourcecode for convenient static linking.
CURRENT FFMPEG version is:
-- ffmpeg-0.5.tar.bz2
+- ffmpeg 0.5
CURRENT LIBMPEG3 version is:
@@ -20,9 +20,9 @@ of those libs.
ffmpeg
--------------
-GIMP-GAP uses the libraries libavformat and libavcodec
+GIMP-GAP uses the libraries libavformat, libavcodec, libavutil, libavdevice and libswscale
that are part of ffmpeg.
-libavformat and libavcodec build up the basic videofile support for
+Those libs build up the basic videofile support for
many MPEG based fileformats, both for read and write access
(frame and audio extraction, and for the powerful video encoder plug-in
"FFMPEG" in the Master Videoencoder dialog).
@@ -45,10 +45,13 @@ this GIMP-GAP distribution.
of both GIMP-GAP and ffmpeg internals to get new ffmpeg versions
working.
- Therefore i decided to include the snapshot of the ffmpeg sourcetree.
- that was tested with GIMP-GAP during Mar. 2009
+ GIMP-GAP currently supports
+ o) ffmpeg-0.5 successfully tested with many videoformats
+ and
+ o) ffmpeg-0.6 basically works, but fails on some videoformats like MJPEG encdoded AVI files
+ (my Olympus SP560UZ Camera records videos in this format).
- newer ffmpeg SVN snapshots
+ newer ffmpeg GIT (or SVN) snapshots
may or may not compile, link and run with this GIMP-GAP release.
@@ -82,7 +85,7 @@ NOTE: Older ffmpeg versions that once worked with older GIMP-GAP
ffmpeg-CVS-2005.04.08 (Support dropped)
ffmpeg-CVS-2005.03.02 (Support dropped)
- ffmpeg-0.4.9pre1 (latest, but outdated official release is NOT SUPPORTED)
+ ffmpeg-0.4.9pre1 (outdated official release is NOT SUPPORTED)
ffmpeg 0.4.8 (outdated stable release is NOT SUPPORTED)
diff --git a/gap/Makefile.am b/gap/Makefile.am
index 0a8bb9a..b4b4387 100644
--- a/gap/Makefile.am
+++ b/gap/Makefile.am
@@ -44,6 +44,14 @@ BASE_SOURCES = \
gap_audio_util.h \
gap_audio_wav.c \
gap_audio_wav.h \
+ gap_colordiff.c \
+ gap_colordiff.h \
+ gap_colormask_exec.c \
+ gap_colormask_exec.h \
+ gap_colormask_file.c \
+ gap_colormask_file.h \
+ gap_edge_detection.c \
+ gap_edge_detection.h \
gap_image.c \
gap_image.h \
gap_layer_copy.c \
@@ -51,6 +59,8 @@ BASE_SOURCES = \
gap_lib.c \
gap_lib.h \
gap_lib_common_defs.h \
+ gap_locate.c \
+ gap_locate.h \
gap_lock.c \
gap_lock.h \
gap_navi_activtable.c \
@@ -96,6 +106,7 @@ libgapstory_a_SOURCES = $(BASE_SOURCES) \
libexec_PROGRAMS = \
gap_bluebox \
+ gap_colormask \
gap_plugins \
gap_filter \
gap_fmac \
@@ -113,6 +124,7 @@ libexec_PROGRAMS = \
gap_wr_color_curve \
gap_wr_color_levels \
gap_wr_color_huesat \
+ gap_wr_color_balance \
gap_wr_trans \
gap_wr_resynth \
gap_wr_opacity
@@ -125,6 +137,13 @@ gap_bluebox_SOURCES = \
gap_bluebox.h \
gap_libgimpgap.h
+gap_colormask_SOURCES = \
+ gap_lastvaldesc.c \
+ gap_lastvaldesc.h \
+ gap_colormask_dialog.c \
+ gap_colormask_dialog.h \
+ gap_colormask_main.c \
+ gap_libgimpgap.h
gap_plugins_SOURCES = \
gap_base_ops.c \
@@ -246,6 +265,8 @@ gap_morph_SOURCES = \
gap_morph_main.h \
gap_morph_exec.c \
gap_morph_exec.h \
+ gap_morph_shape.c \
+ gap_morph_shape.h \
gap_morph_dialog.c \
gap_morph_dialog.h \
gap_morph_tween_dialog.c \
@@ -369,6 +390,10 @@ gap_wr_color_huesat_SOURCES = \
gap_wr_color_huesat.c \
gap_libgimpgap.h
+gap_wr_color_balance_SOURCES = \
+ gap_wr_color_balance.c \
+ gap_libgimpgap.h
+
gap_wr_resynth_SOURCES = \
gap_wr_resynth.c \
gap_lastvaldesc.c \
@@ -400,6 +425,7 @@ LDADD = $(GIMP_LIBS)
gap_plugins_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_bluebox_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
+gap_colormask_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_filter_LDADD = $(GAPVIDEOAPI) $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_fmac_LDADD = $(GAPVIDEOAPI) $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_fmac_varying_LDADD = $(GAPVIDEOAPI) $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
@@ -418,6 +444,7 @@ gap_wr_trans_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_wr_color_curve_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_wr_color_levels_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_wr_color_huesat_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
+gap_wr_color_balance_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
gap_wr_resynth_LDADD = $(LIBGIMPGAP) $(LIBGAPBASE) $(GIMP_LIBS)
EXTRA_DIST = \
diff --git a/gap/TESTPROT_iter_ALT b/gap/TESTPROT_iter_ALT
index 56d1627..9daef2e 100644
--- a/gap/TESTPROT_iter_ALT
+++ b/gap/TESTPROT_iter_ALT
@@ -204,7 +204,7 @@ Additional notes:
plug_in_map_object old Testreport: (did not verfy that at the latest test)
- - The MapObject has an implicite feature:
+ - The MapObject has an implicit feature:
If the handled layer has NO ALPHA Channel (as backgrounds often do)
and the "Transparent background" is ON
it forces the creation of a new image.
diff --git a/gap/gap_colordiff.c b/gap/gap_colordiff.c
new file mode 100644
index 0000000..ddd2139
--- /dev/null
+++ b/gap/gap_colordiff.c
@@ -0,0 +1,363 @@
+/* gap_colordiff.c
+ * by hof (Wolfgang Hofer)
+ * color difference procedures
+ * 2010/08/06
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_pdb_calls.h"
+#include "gap_colordiff.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+
+#define TO_LONG_FACTOR 100000
+#define TO_GDOUDBLE_FACTOR 100000.0
+
+extern int gap_debug; /* ==0 ... dont print debug infos */
+
+
+
+static long p_gdouble_to_long(gdouble value)
+{
+ long lval;
+
+ lval = value * TO_LONG_FACTOR;
+ return (lval);
+}
+static gdouble p_long_to_gdouble(long value)
+{
+ gdouble dval;
+
+ dval = (gdouble)value / TO_GDOUDBLE_FACTOR;
+ return (dval);
+}
+
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_GimpHSV(GimpHSV *aHsvPtr
+ , GimpHSV *bHsvPtr
+ , gdouble colorSensitivity
+ , gboolean debugPrint)
+{
+ gdouble colorDiff;
+ gdouble hDif;
+ gdouble sDif;
+ gdouble vDif;
+ gdouble vMax;
+ gdouble sMax;
+ gdouble weight;
+
+
+ hDif = fabs(aHsvPtr->h - bHsvPtr->h);
+ /* normalize hue difference.
+ * hue values represents an angle
+ * where value 0.5 equals 180 degree
+ * and value 1.0 stands for 360 degree that is
+ * equal to 0.0
+ * Hue is maximal different at 180 degree.
+ *
+ * after normalizing, the difference
+ * hDiff value 1.0 represents angle difference of 180 degree
+ */
+ if(hDif > 0.5)
+ {
+ hDif = (1.0 - hDif) * 2.0;
+ }
+ else
+ {
+ hDif = hDif * 2.0;
+
+ }
+ sDif = fabs(aHsvPtr->s - bHsvPtr->s);
+ vDif = fabs(aHsvPtr->v - bHsvPtr->v);
+
+
+ vMax = MAX(aHsvPtr->v, bHsvPtr->v);
+
+ if(vMax <= 0.25)
+ {
+ /* reduce weight of hue and saturation
+ * when comparing 2 dark colors
+ */
+ weight = vMax / 0.25;
+ weight *= (weight * weight);
+ colorDiff = 1.0 - ((1.0 - pow((hDif * weight), colorSensitivity)) * (1.0 - pow((sDif * weight), colorSensitivity)) * (1.0 - pow(vDif, colorSensitivity)));
+ }
+ else
+ {
+ sMax = MAX(aHsvPtr->s, bHsvPtr->s);
+ if (sMax <= 0.25)
+ {
+ /* reduce weight of hue and saturation
+ * when comparing 2 gray colors
+ */
+ weight = sMax / 0.25;
+ weight *= (weight * weight);
+ colorDiff = 1.0 - ((1.0 - pow((hDif * weight), colorSensitivity)) * (1.0 - pow((sDif * weight), colorSensitivity)) * (1.0 - pow(vDif, colorSensitivity)));
+ }
+ else
+ {
+ weight = 1.0;
+ colorDiff = 1.0 - ((1.0 - pow(hDif, colorSensitivity)) * (1.0 - pow(sDif, colorSensitivity)) * (1.0 - pow(vDif, colorSensitivity)));
+ }
+ }
+
+
+
+ if(debugPrint)
+ {
+ printf("HSV: hsv 1/2 (%.3f %.3f %.3f) / (%.3f %.3f %.3f) vMax:%f\n"
+ , aHsvPtr->h
+ , aHsvPtr->s
+ , aHsvPtr->v
+ , bHsvPtr->h
+ , bHsvPtr->s
+ , bHsvPtr->v
+ , vMax
+ );
+ printf("diffHSV: (%.3f %.3f %.3f) weight:%.3f colorSensitivity:%.3f colorDiff:%.5f\n"
+ , hDif
+ , sDif
+ , vDif
+ , weight
+ , colorSensitivity
+ , colorDiff
+ );
+ }
+
+ return (colorDiff);
+} /* end gap_colordiff_GimpHSV */
+
+
+
+/* ---------------------------------
+ * gap_colordiff_guchar_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_guchar_GimpHSV(GimpHSV *aHsvPtr
+ , guchar *pixel
+ , gdouble colorSensitivity
+ , gboolean debugPrint)
+{
+ GimpRGB bRgb;
+ GimpHSV bHsv;
+ gdouble colordiff;
+
+ gimp_rgba_set_uchar (&bRgb, pixel[0], pixel[1], pixel[2], 255);
+ gimp_rgb_to_hsv(&bRgb, &bHsv);
+
+ colordiff = gap_colordiff_GimpHSV(aHsvPtr, &bHsv, colorSensitivity, debugPrint);
+ return (colordiff);
+
+} /* end gap_colordiff_guchar_GimpHSV */
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note:
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble
+gap_colordiff_GimpRGB(GimpRGB *aRgbPtr
+ , GimpRGB *bRgbPtr
+ , gdouble colorSensitivity
+ , gboolean debugPrint)
+{
+ GimpHSV aHsv;
+ GimpHSV bHsv;
+ gdouble colordiff;
+
+ gimp_rgb_to_hsv(aRgbPtr, &aHsv);
+ gimp_rgb_to_hsv(bRgbPtr, &bHsv);
+
+
+ colordiff = gap_colordiff_GimpHSV(&aHsv, &bHsv, colorSensitivity, debugPrint);
+ if(debugPrint)
+ {
+ printf("RGB 1/2 (%.3g %.3g %.3g) / (%.3g %.3g %.3g) colordiff:%f\n"
+ , aRgbPtr->r
+ , aRgbPtr->g
+ , aRgbPtr->b
+ , bRgbPtr->r
+ , bRgbPtr->g
+ , bRgbPtr->b
+ , colordiff
+ );
+ }
+ return (colordiff);
+
+} /* end gap_colordiff_GimpRGB */
+
+
+
+/* ---------------------------------
+ * gap_colordiff_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note:
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble
+gap_colordiff_guchar(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gdouble colorSensitivity
+ , gboolean debugPrint
+ )
+{
+ GimpRGB aRgb;
+ GimpRGB bRgb;
+
+ gimp_rgba_set_uchar (&aRgb, aPixelPtr[0], aPixelPtr[1], aPixelPtr[2], 255);
+ gimp_rgba_set_uchar (&bRgb, bPixelPtr[0], bPixelPtr[1], bPixelPtr[2], 255);
+ return (gap_colordiff_GimpRGB(&aRgb
+ , &bRgb
+ , colorSensitivity
+ , debugPrint
+ ));
+
+} /* end gap_colordiff_guchar */
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note:
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ */
+gdouble
+gap_colordiff_simple_GimpRGB(GimpRGB *aRgbPtr
+ , GimpRGB *bRgbPtr
+ , gboolean debugPrint
+ )
+{
+ gdouble rDif;
+ gdouble gDif;
+ gdouble bDif;
+ gdouble colorDiff;
+
+ rDif = fabs(aRgbPtr->r - bRgbPtr->r);
+ gDif = fabs(aRgbPtr->g - bRgbPtr->g);
+ bDif = fabs(aRgbPtr->b - bRgbPtr->b);
+
+ colorDiff = (rDif + gDif + bDif) / 3.0;
+
+ if(debugPrint)
+ {
+ printf("RGB 1/2 (%.3g %.3g %.3g) / (%.3g %.3g %.3g) rDif:%.3g gDif:%.3g bDif:%.3g colorDiff:%f\n"
+ , aRgbPtr->r
+ , aRgbPtr->g
+ , aRgbPtr->b
+ , bRgbPtr->r
+ , bRgbPtr->g
+ , bRgbPtr->b
+ , rDif
+ , gDif
+ , bDif
+ , colorDiff
+ );
+ }
+ return (colorDiff);
+
+
+} /* end gap_colordiff_simple_GimpRGB */
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note:
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ * in 1 byte per channel representation.
+ */
+gdouble
+gap_colordiff_simple_guchar(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gboolean debugPrint
+ )
+{
+#define MAX_CHANNEL_SUM 765.0
+ int rDif;
+ int gDif;
+ int bDif;
+ gdouble colorDiff;
+
+ rDif = abs(aPixelPtr[0] - bPixelPtr[0]);
+ gDif = abs(aPixelPtr[1] - bPixelPtr[1]);
+ bDif = abs(aPixelPtr[2] - bPixelPtr[2]);
+
+ colorDiff = (gdouble)(rDif + gDif + bDif) / MAX_CHANNEL_SUM;
+ if(debugPrint)
+ {
+ printf("rgb 1/2 (%.3g %.3g %.3g) / (%.3g %.3g %.3g) rDif:%.3g gDif:%.3g bDif:%.3g colorDiff:%f\n"
+ , (float)(aPixelPtr[0]) / 255.0
+ , (float)(aPixelPtr[1]) / 255.0
+ , (float)(aPixelPtr[2]) / 255.0
+ , (float)(bPixelPtr[0]) / 255.0
+ , (float)(bPixelPtr[1]) / 255.0
+ , (float)(bPixelPtr[2]) / 255.0
+ , (float)(rDif) / 255.0
+ , (float)(gDif) / 255.0
+ , (float)(bDif) / 255.0
+ , colorDiff
+ );
+ }
+ return (colorDiff);
+
+} /* end gap_colordiff_simple_guchar */
diff --git a/gap/gap_colordiff.h b/gap/gap_colordiff.h
new file mode 100644
index 0000000..e0c6de5
--- /dev/null
+++ b/gap/gap_colordiff.h
@@ -0,0 +1,142 @@
+/* gap_colordiff.h
+ * by hof (Wolfgang Hofer)
+ * color difference procedures
+ * 2010/08/06
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#ifndef _GAP_COLORDIFF_H
+#define _GAP_COLORDIFF_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+
+#define GAP_COLORDIFF_DEFAULT_SENSITIVITY 1.35
+
+/* ---------------------------------
+ * gap_colordiff_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * use this procedure in case both colors are already availabe in GimpHSV color.
+ */
+gdouble
+gap_colordiff_GimpHSV(GimpHSV *aHsvPtr
+ , GimpHSV *bHsvPtr
+ , gdouble colorSensitivity
+ , gboolean debugPrint);
+
+
+/* ---------------------------------
+ * gap_colordiff_guchar_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_guchar_GimpHSV(GimpHSV *aHsvPtr
+ , guchar *pixel
+ , gdouble colorSensitivity
+ , gboolean debugPrint);
+
+
+/* ---------------------------------
+ * gap_colordiff_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * use this procedure in case both colors are availabe in GimpRGB color.
+ * Note:
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble gap_colordiff_GimpRGB(GimpRGB *color1
+ , GimpRGB *color2
+ , gdouble sensitivity
+ , gboolean debugPrint
+ );
+
+/* ---------------------------------
+ * gap_colordiff_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * use this procedure in case where both colors are available
+ * as rgb byte value components in the range 0 to 255.
+ * Note:
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble
+gap_colordiff_guchar(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gdouble colorSensitivity
+ , gboolean debugPrint
+ );
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note:
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ */
+gdouble
+gap_colordiff_simple_GimpRGB(GimpRGB *aRgbPtr
+ , GimpRGB *bRgbPtr
+ , gboolean debugPrint
+ );
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note:
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ * PixelPtr must point to guchar data of at least 3 bytes size
+ * sequence RGB
+ */
+gdouble
+gap_colordiff_simple_guchar(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gboolean debugPrint
+ );
+
+
+#endif
diff --git a/gap/gap_colormask_dialog.c b/gap/gap_colormask_dialog.c
new file mode 100644
index 0000000..10bc58d
--- /dev/null
+++ b/gap/gap_colormask_dialog.c
@@ -0,0 +1,1848 @@
+/* gap_colormask_dialog.c
+ * by hof (Wolfgang Hofer)
+ * colormask filter dialog handling procedures
+ * 2010/02/25
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#include "config.h"
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+
+/* GIMP includes */
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+
+/* GAP includes */
+#include "gap-intl.h"
+#include "gap_colordiff.h"
+#include "gap_lib_common_defs.h"
+#include "gap_pdb_calls.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_exec.h"
+#include "gap_libgapbase.h"
+#include "gap_colormask_dialog.h"
+
+#define PLUG_IN_BINARY "gapcolormask"
+#define PLUG_IN_NAME_HELP "plug-in-layer-set-alpha-by-colormask"
+
+#define SCALE_WIDTH 140
+
+
+typedef struct {
+ char *paramFilename;
+ GimpDrawable *layer_drawable; /* the layer to be processed */
+ GimpRGB layercolor;
+ GimpRGB maskcolor;
+ GimpRGB nbcolor;
+ gboolean showExpertOptions;
+ GtkWidget *baseFrame;
+ GtkWidget *filterFrame;
+ GtkWidget *opacityFrame;
+ GtkWidget *expertFrame;
+ GtkWidget *debugFrame;
+ GtkWidget *keycolorButton;
+ GtkWidget *layercolorButton;
+ GtkWidget *maskcolorButton;
+ GtkWidget *nbcolorButton;
+ GtkWidget *colordiffLabel;
+ GtkWidget *nbColordiffLabel;
+ GimpPixelFetcher *pftMask;
+ GimpPixelFetcher *pftDest;
+
+
+ gint mouse_x;
+ gint mouse_y;
+ //gint drag_mode;
+
+ GtkObject *offset_adj_x;
+ GtkObject *offset_adj_y;
+
+ guchar **src_rows;
+ guchar **bm_rows;
+
+ GimpDrawable *colormask_drawable; /* the colormask to be applied */
+ gint bm_width;
+ gint bm_height;
+ gint bm_bpp;
+ gboolean bm_has_alpha;
+
+ GimpPixelRgn src_rgn;
+ GimpPixelRgn colormask_rgn;
+
+ GimpPreview *preview;
+ gboolean applyImmediate;
+ GapColormaskValues *cmaskvalsPtr;
+
+// GapColormaskValues params;
+/// gint run;
+} CmaskInterface;
+
+
+extern int gap_debug; /* ==0 ... dont print debug infos */
+
+static CmaskInterface cmaskint =
+{
+ NULL, /* paramFilename */
+ NULL, /* GimpDrawable *layer_drawable */
+ { 0,0,0,0 }, /* GimpRGB layercolor */
+ { 0,0,0,0 }, /* GimpRGB maskcolor */
+ { 0,0,0,0 }, /* GimpRGB nbcolor */
+ FALSE, /* showExpertOptions */
+ NULL, /* GtkWidget *baseFrame; */
+ NULL, /* GtkWidget *filterFrame; */
+ NULL, /* GtkWidget *opacityFrame; */
+ NULL, /* GtkWidget *expertFrame; */
+ NULL, /* GtkWidget *debugFrame; */
+ NULL, /* GtkWidget *keycolorButton */
+ NULL, /* GtkWidget *layercolorButton */
+ NULL, /* GtkWidget *maskcolorButton */
+ NULL, /* GtkWidget *nbcolorButton */
+ NULL, /* GtkWidget *colordiffLabel */
+ NULL, /* GtkWidget *nbColordiffLabel */
+ NULL, /* GimpPixelFetcher *pftMask */
+ NULL, /* GimpPixelFetcher *pftDest */
+
+
+ 0, /* mouse_x */
+ 0, /* mouse_y */
+ // DRAG_NONE, /* drag_mode */
+ NULL, /* offset_adj_x */
+ NULL, /* offset_adj_y */
+ NULL, /* src_rows */
+ NULL, /* bm_rows */
+ NULL, /* colormask_drawable */
+ 0, /* bm_width */
+ 0, /* bm_height */
+ 0, /* bm_bpp */
+ FALSE, /* bm_has_alpha */
+ { 0, }, /* src_rgn */
+ { 0, }, /* colormask_rgn */
+ //{ 0, } /* params */
+ NULL, /* preview */
+ FALSE,
+ NULL /* cmaskvalsPtr */
+};
+
+static void p_keycolor_update_callback(GtkWidget *widget, gpointer val);
+static void p_color_update_callback(GtkWidget *widget, gpointer val);
+static void p_fetch_layercolor_and_maskcolor(gint x, gint y);
+
+static void p_dialog_new_colormask (gboolean init_offsets);
+static void dialog_update_preview (GimpPreview *preview);
+static gint dialog_preview_events (GtkWidget *area,
+ GdkEvent *event,
+ GimpPreview *preview);
+static void dialog_get_rows (GimpPixelRgn *pr,
+ guchar **rows,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+static void dialog_fill_src_rows (gint start,
+ gint how_many,
+ gint yofs);
+static void dialog_fill_bumpmap_rows (gint start,
+ gint how_many,
+ gint yofs);
+static gint dialog_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+static void dialog_colormask_callback (GtkWidget *widget,
+ GimpPreview *preview);
+
+static void p_update_colorfiff_labels(GtkWidget *widget, gpointer val);
+static void p_update_option_frames_visiblity_callback(GtkWidget *widget, gpointer data);
+
+/* -----------------------------------------------
+ * p_set_widget_sensitivity_dependent_to_algorithm
+ * -----------------------------------------------
+ */
+static void
+p_set_widget_sensitivity_dependent_to_algorithm(GtkWidget *widget, gpointer val)
+{
+ printf("TODO p_set_widget_sensitivity_dependent_to_algorithm\n"); // TODO
+
+} /* end p_set_widget_sensitivity_dependent_to_algorithm */
+
+
+
+
+/* -------------------------------
+ * p_create_base_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_base_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkWidget *button;
+ GtkObject *adj;
+ gint row = 0;
+
+ /* the frame */
+ frame = gimp_frame_new (_("Base Options"));
+
+ gtk_widget_show (frame);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+
+
+ /* table */
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+ /* menu to select the colormask drawable */
+ combo = gimp_drawable_combo_box_new (dialog_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), cmaskvals->colormask_id,
+ G_CALLBACK (dialog_colormask_callback),
+ preview);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ _("Color mask:"), 0.0, 0.5, combo, 2, FALSE);
+
+
+
+ /* loColorThreshold for colordiffence range */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Colordiff threshold:"), SCALE_WIDTH, 6,
+ cmaskvals->loColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ _("Colordiff lower threshold. "
+ "pixels that differ in color less than this value "
+ "(compared with the corresponding pixel in the colormask) "
+ "are set to lower opacity value (usually transparent)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->loColorThreshold);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* hiColorThreshold for colordiffence range */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_HiColordiff threshold:"), SCALE_WIDTH, 6,
+ cmaskvals->hiColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ _("Colordiff upper threshold. "
+ "pixels that differ in color more than this value "
+ "(compared with the corresponding pixel in the colormask) "
+ "are set to upper opacity value (usually opaque)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->hiColorThreshold);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+
+
+ /* keep layermask checkbutton */
+ button = gtk_check_button_new_with_mnemonic (_("Keep layermask"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+ gtk_widget_show (button);
+ row++;
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->keepLayerMask);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &cmaskvals->keepLayerMask);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+
+ /* apply immediate checkbutton */
+ button = gtk_check_button_new_with_mnemonic (_("Apply Immediate"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+ gtk_widget_show (button);
+ row++;
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskint.applyImmediate);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &cmaskint.applyImmediate);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+ /* show expert options checkbutton */
+ button = gtk_check_button_new_with_mnemonic (_("Show All Options"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+ gtk_widget_show (button);
+ row++;
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskint.showExpertOptions);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &cmaskint.showExpertOptions);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (p_update_option_frames_visiblity_callback),
+ NULL);
+
+ return (frame);
+
+} /* end p_create_base_options */
+
+
+/* -------------------------------
+ * p_create_filter_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_filter_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkObject *adj;
+ gint row = 0;
+
+ /* the frame */
+ frame = gimp_frame_new (_("Filter Options"));
+
+ gtk_widget_show (frame);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+
+
+ /* table */
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+ /* isleRadius in pixels (for removing isolated pixels) */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Isle Radius:"), SCALE_WIDTH, 6,
+ cmaskvals->isleRadius, 0.0, 15.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ _("Isle removal radius in pixels (use value 0 to disable removal of isolated pixels)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->isleRadius);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* isleAreaPixelLimit in pixels (for removing isolated pixels) */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Isle Area:"), SCALE_WIDTH, 6,
+ cmaskvals->isleAreaPixelLimit, 0.0, 30.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ _("Isle Area size in pixels. "
+ "small isolated opaque or transparent pixel areas below that size "
+ "are removed (e.g toggled from opaque to transparent and vice versa)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->isleAreaPixelLimit);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+
+ /* featherRadius in pixels */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Feather Radius:"), SCALE_WIDTH, 6,
+ cmaskvals->featherRadius, 0.0, 10.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ _("feather radius in pixels (use value 0 to disable feathering)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->featherRadius);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+
+ return (frame);
+
+} /* end p_create_filter_options */
+
+
+
+
+/* -------------------------------
+ * p_create_opacity_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_opacity_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkObject *adj;
+ gint row = 0;
+
+ /* the frame */
+ frame = gimp_frame_new (_("Opacity Options"));
+
+ gtk_widget_show (frame);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+
+
+ /* table */
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+ /* lowerOpacity for colordiffence range */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Lower opacity:"), SCALE_WIDTH, 6,
+ cmaskvals->lowerOpacity, 0.0, 1.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ _("Lower opacity value is set for pixels with color difference "
+ "less than Colordiff threshold (use value 0 for transparency)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->lowerOpacity);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ row++;
+
+ /* upperOpacity for colordiffence range */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Upper opacity:"), SCALE_WIDTH, 6,
+ cmaskvals->upperOpacity, 0.0, 1.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ _("Upper opacity is set for pixels with color difference "
+ "greater than High Colordiff threshold (use value 1 for opacity)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->upperOpacity);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+ row++;
+
+ /* trigger alpha (only relevant if colormask has alpha channel) */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Trigger alpha:"), SCALE_WIDTH, 6,
+ cmaskvals->triggerAlpha, 0.0, 1.0, 0.1, 0.1, 4,
+ TRUE, 0, 0,
+ _("Trigger alpha is only relevant in case the color mask has an alpha channel. "
+ "All pixels where the alpha channel of the corresponding pixel "
+ "in the color mask is below this trigger value "
+ "are not changed (e.g. keep their original opacity)"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->triggerAlpha);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+ return (frame);
+
+} /* end p_create_opacity_options */
+
+
+/* -------------------------------
+ * p_create_expert_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_expert_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkObject *adj;
+ gint row = 0;
+
+ /* the frame */
+ frame = gimp_frame_new (_("Expert Options"));
+
+ gtk_widget_show (frame);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+
+
+ /* table */
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+ /* algorithm */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Algorithm:"), SCALE_WIDTH, 6,
+ cmaskvals->algorithm, 0.0, GAP_COLORMASK_ALGO_MAX, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &cmaskvals->algorithm);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (p_set_widget_sensitivity_dependent_to_algorithm),
+ preview);
+
+
+
+ /* --------------- start of keycolor widgets ------------- */
+
+ /* enableKeyColorThreshold checkbutton */
+ button = gtk_check_button_new_with_mnemonic (_("Enable individual color threshold for the key color"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+ gtk_widget_show (button);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->enableKeyColorThreshold);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &cmaskvals->enableKeyColorThreshold);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ cmaskint.preview);
+ // TODO add another callback with g_signal_connect_swapped
+ // to update widget sensitivity (en/disable keycolorButton, keyColorThreshold and keyColorSensitivity
+
+ row++;
+
+
+ /* the keycolor label */
+ label = gtk_label_new (_("Key color:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+ gtk_widget_show (label);
+
+ /* the keycolor button */
+ button = gimp_color_button_new (_("Key color"),
+ 40, 20, /* WIDTH, HEIGHT, */
+ &cmaskvals->keycolor,
+ GIMP_COLOR_AREA_FLAT);
+ gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+ GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+ gtk_widget_show (button);
+ cmaskint.keycolorButton = button;
+
+ g_signal_connect (button, "color_changed",
+ G_CALLBACK (p_keycolor_update_callback),
+ &cmaskvals->keycolor);
+
+ g_signal_connect_swapped (button, "color_changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ cmaskint.preview);
+
+ row++;
+
+ /* keyColorThreshold (individual lo threshold for the key color) */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Key Colordiff threshold:"), SCALE_WIDTH, 6,
+ cmaskvals->loKeyColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->loKeyColorThreshold);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ cmaskint.preview);
+
+ row++;
+
+ /* keyColorThreshold (individual lo color threshold for the key color) */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Key Color Sensitivity:"), SCALE_WIDTH, 6,
+ cmaskvals->keyColorSensitivity, 0.0, 10.0, 0.1, 1.0, 4,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->keyColorSensitivity);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ cmaskint.preview);
+
+
+ /* --------------- end of keycolor widgets ------------- */
+
+ row++;
+
+
+
+ /* significant colordiff threshold */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Sig Colordiff Threshold:"), SCALE_WIDTH, 6,
+ cmaskvals->significantColordiff, 0.0, 1.0, 0.1, 1.0, 4,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->significantColordiff);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ cmaskint.preview);
+ row++;
+
+ /* significant brightness difference threshold */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Sig Brightness Threshold:"), SCALE_WIDTH, 6,
+ cmaskvals->significantBrightnessDiff, 0.0, 1.0, 0.1, 1.0, 4,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->significantBrightnessDiff);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ cmaskint.preview);
+
+ row++;
+
+ /* significant brightness difference threshold */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Sig Radius:"), SCALE_WIDTH, 6,
+ cmaskvals->significantRadius, 0.0, 10.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->significantRadius);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ cmaskint.preview);
+
+
+ row++;
+
+
+ /* edgeColorThreshold for colordiffence range */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("_Edge Colordiff threshold:"), SCALE_WIDTH, 6,
+ cmaskvals->edgeColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->edgeColorThreshold);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ row++;
+
+ /* thresholdColorArea for colordiffence range */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("_Area Colordiff threshold:"), SCALE_WIDTH, 6,
+ cmaskvals->thresholdColorArea, 0.0, 1.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->thresholdColorArea);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ row++;
+
+ /* pixelDiagonal in pixels (for small area detection) */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Small Area Diagonal:"), SCALE_WIDTH, 6,
+ cmaskvals->pixelDiagonal, 0.0, 50.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->pixelDiagonal);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ row++;
+
+ /* pixelAreaLimit in pixels (for small area detection) */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Small Area Pixelsize:"), SCALE_WIDTH, 6,
+ cmaskvals->pixelAreaLimit, 0.0, 1000.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->pixelAreaLimit);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+ return (frame);
+
+} /* end p_create_expert_options */
+
+
+/* -------------------------------
+ * p_create_debug_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_debug_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkObject *adj;
+ gint row = 0;
+
+ /* the frame */
+ frame = gimp_frame_new (_("DEBUG Options"));
+
+ gtk_widget_show (frame);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+
+
+ /* table */
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+
+ /* colorSensitivity */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("DiffSensitvity:"), SCALE_WIDTH, 6,
+ cmaskvals->colorSensitivity, 1.0, 2.0, 0.01, 0.1, 4,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cmaskvals->colorSensitivity);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (p_update_colorfiff_labels),
+ NULL);
+
+
+
+
+ /* connectByCorner checkbutton */
+ button = gtk_check_button_new_with_mnemonic (_("DEBUG Connect by corner (+ use RGB colordiff)"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+ gtk_widget_show (button);
+ row++;
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->connectByCorner);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &cmaskvals->connectByCorner);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+
+ /* keep worklayer checkbutton */
+ button = gtk_check_button_new_with_mnemonic (_("DEBUG Keep worklayer"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+ gtk_widget_show (button);
+ row++;
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->keepWorklayer);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &cmaskvals->keepWorklayer);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+
+
+
+
+
+
+ row++;
+
+ /* the layercolor label */
+ label = gtk_label_new (_("Layer color:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+ gtk_widget_show (label);
+
+ /* the layercolor button */
+ button = gimp_color_button_new (_("Layer color (Colormask)"),
+ 40, 20, /* WIDTH, HEIGHT, */
+ &cmaskint.layercolor,
+ GIMP_COLOR_AREA_FLAT);
+ gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+ GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+ gtk_widget_show (button);
+ cmaskint.layercolorButton = button;
+
+ g_signal_connect (button, "color_changed",
+ G_CALLBACK (p_color_update_callback),
+ &cmaskint.layercolor);
+
+ row++;
+
+ /* the maskcolor label */
+ label = gtk_label_new (_("Mask color:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+ gtk_widget_show (label);
+
+ /* the maskcolor button */
+ button = gimp_color_button_new (_("Mask color (Colormask)"),
+ 40, 20, /* WIDTH, HEIGHT, */
+ &cmaskint.maskcolor,
+ GIMP_COLOR_AREA_FLAT);
+ gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+ GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+ gtk_widget_show (button);
+ cmaskint.maskcolorButton = button;
+
+ g_signal_connect (button, "color_changed",
+ G_CALLBACK (p_color_update_callback),
+ &cmaskint.maskcolor);
+
+ row++;
+
+
+ /* the maskcolor label */
+ label = gtk_label_new (_("Nb color:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+ gtk_widget_show (label);
+
+ /* the maskcolor button */
+ button = gimp_color_button_new (_("Left Neighbor color"),
+ 40, 20, /* WIDTH, HEIGHT, */
+ &cmaskint.nbcolor,
+ GIMP_COLOR_AREA_FLAT);
+ gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+ GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+ gtk_widget_show (button);
+ cmaskint.nbcolorButton = button;
+
+ g_signal_connect (button, "color_changed",
+ G_CALLBACK (p_color_update_callback),
+ &cmaskint.nbcolor);
+
+ row++;
+
+ /* the colordiff label */
+ label = gtk_label_new (_("Colordiff:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("#.#####"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 1, 3, row, row+1);
+ gtk_widget_show (label);
+ cmaskint.colordiffLabel = label;
+
+ row++;
+
+ /* the neighbor colordiff label */
+ label = gtk_label_new (_("NbDiff:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("#.#####"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 1, 3, row, row+1);
+ gtk_widget_show (label);
+ cmaskint.nbColordiffLabel = label;
+
+
+ return (frame);
+
+} /* end p_create_debug_options */
+
+
+/* ---------------------------------
+ * p_update_colorfiff_labels
+ * ---------------------------------
+ */
+static void
+p_update_colorfiff_labels(GtkWidget *widget, gpointer val)
+{
+ if((cmaskint.colordiffLabel != NULL) && (cmaskint.nbColordiffLabel != NULL))
+ {
+ gdouble colorDiff;
+ gdouble colorDiffSimple;
+ char *valueAsText;
+ gdouble sensitivity;
+
+ sensitivity = cmaskint.cmaskvalsPtr->colorSensitivity;
+
+ colorDiff = gap_colordiff_GimpRGB(&cmaskint.maskcolor
+ , &cmaskint.layercolor
+ , sensitivity, TRUE);
+
+ colorDiffSimple = gap_colordiff_simple_GimpRGB(&cmaskint.maskcolor
+ , &cmaskint.layercolor
+ , TRUE);
+
+printf("colorDiff:%f %f\n", colorDiff, (float)colorDiff);
+printf("colorDiffSimple:%f\n", colorDiffSimple);
+
+ valueAsText = g_strdup_printf("%.5f", (float)colorDiff);
+ gtk_label_set_text ( GTK_LABEL(cmaskint.colordiffLabel), valueAsText);
+ g_free(valueAsText);
+
+ colorDiff = gap_colordiff_GimpRGB(&cmaskint.nbcolor
+ , &cmaskint.layercolor
+ , sensitivity, TRUE);
+
+ valueAsText = g_strdup_printf("%.5f", (float)colorDiff);
+ gtk_label_set_text ( GTK_LABEL(cmaskint.nbColordiffLabel), valueAsText);
+ g_free(valueAsText);
+
+ }
+
+} /* end p_update_colorfiff_labels */
+
+
+
+
+/* -----------------------------------------
+ * p_update_option_frames_visiblity_callback
+ * -----------------------------------------
+ *
+ */
+static void
+p_update_option_frames_visiblity_callback(GtkWidget *widget,
+ gpointer data)
+{
+ if(cmaskint.showExpertOptions)
+ {
+ const gchar *l_env;
+ gtk_widget_show (cmaskint.filterFrame);
+ gtk_widget_show (cmaskint.opacityFrame);
+
+ l_env = g_getenv("USERNAME");
+ if(l_env != NULL)
+ {
+ if(strcmp(l_env, "hof") == 0)
+ {
+ gtk_widget_show (cmaskint.expertFrame);
+ gtk_widget_show (cmaskint.debugFrame);
+ return;
+ }
+ }
+
+ // Note that the colormask filter is not finished yet
+ // the experimental expert and debug options will be hidden
+ // from the public (all users != hof) until the code is ready.
+ // Expect some of those options to be removed and/or changed
+ // in the future....
+
+ gtk_widget_hide (cmaskint.expertFrame);
+ gtk_widget_hide (cmaskint.debugFrame);
+ }
+ else
+ {
+ gtk_widget_hide (cmaskint.filterFrame);
+ gtk_widget_hide (cmaskint.opacityFrame);
+ gtk_widget_hide (cmaskint.expertFrame);
+ gtk_widget_hide (cmaskint.debugFrame);
+ }
+
+
+} /* end p_update_option_frames_visiblity_callback */
+
+
+
+
+/* ---------------------------------
+ * p_keycolor_update_callback
+ * ---------------------------------
+ */
+static void
+p_keycolor_update_callback(GtkWidget *widget, gpointer val)
+{
+ gimp_color_button_get_color((GimpColorButton *)widget, (GimpRGB *)val);
+ //
+
+} /* end p_keycolor_update_callback */
+
+
+/* ---------------------------------
+ * p_color_update_callback
+ * ---------------------------------
+ */
+static void
+p_color_update_callback(GtkWidget *widget, gpointer val)
+{
+ gimp_color_button_get_color((GimpColorButton *)widget, (GimpRGB *)val);
+ p_update_colorfiff_labels(widget, val);
+
+} /* end p_color_update_callback */
+
+/* ---------------------------------
+ * p_fetch_layercolor_and_maskcolor
+ * ---------------------------------
+ * fetch colors at coordinate x/y from
+ * drawable and from the colormask
+ * and update both colorbuttons
+ * (the colordiff label shall update due to value change in the colorbuttons)
+ */
+static void
+p_fetch_layercolor_and_maskcolor(gint x, gint y)
+{
+ gint x1 = 0;
+ gint y1 = 0;
+
+ if(cmaskint.preview != NULL)
+ {
+ gimp_preview_get_position (cmaskint.preview, &x1, &y1);
+ }
+
+
+ //if(gap_debug)
+ {
+ printf("p_fetch_layercolor_and_maskcolor x:%d y:%d x1:%d y1:%d\n"
+ ,(int)x
+ ,(int)y
+ ,(int)x1
+ ,(int)y1
+ );
+ }
+
+ if ((cmaskint.pftDest != NULL) && (cmaskint.pftMask != NULL))
+ {
+ guchar pixels[3][4];
+
+ gimp_pixel_fetcher_get_pixel (cmaskint.pftMask
+ , x1 + x
+ , y1 + y
+ , pixels[0]
+ );
+ gimp_pixel_fetcher_get_pixel (cmaskint.pftDest
+ , x1 + x
+ , y1 + y
+ , pixels[1]
+ );
+ gimp_pixel_fetcher_get_pixel (cmaskint.pftDest
+ , x1 + x -1
+ , y1 + y
+ , pixels[2]
+ );
+
+
+ //if(gap_debug)
+ {
+ printf("\npixel[0] r:%d g:%d b:%d a:%d pixel[1] r:%d g:%d b:%d a:%d\n"
+ ,(int)pixels[0][0]
+ ,(int)pixels[0][1]
+ ,(int)pixels[0][2]
+ ,(int)pixels[0][3]
+ ,(int)pixels[1][0]
+ ,(int)pixels[1][1]
+ ,(int)pixels[1][2]
+ ,(int)pixels[1][3]
+ );
+ }
+
+
+ gimp_rgba_set_uchar (&cmaskint.maskcolor, pixels[0][0], pixels[0][1], pixels[0][2], pixels[0][3]);
+ gimp_rgba_set_uchar (&cmaskint.layercolor, pixels[1][0], pixels[1][1], pixels[1][2], pixels[1][3]);
+ gimp_rgba_set_uchar (&cmaskint.nbcolor, pixels[2][0], pixels[2][1], pixels[2][2], pixels[2][3]);
+
+ gimp_color_button_set_color ((GimpColorButton *)cmaskint.maskcolorButton, &cmaskint.maskcolor);
+ gimp_color_button_set_color ((GimpColorButton *)cmaskint.layercolorButton, &cmaskint.layercolor);
+ gimp_color_button_set_color ((GimpColorButton *)cmaskint.nbcolorButton, &cmaskint.nbcolor);
+
+ }
+} /* end p_fetch_layercolor_and_maskcolor */
+
+
+
+
+/* ---------------------------------
+ *
+ * ---------------------------------
+ */
+static gint
+dialog_preview_events (GtkWidget *area,
+ GdkEvent *event,
+ GimpPreview *preview)
+{
+ gint x, y;
+ gint dx, dy;
+ GdkEventButton *bevent;
+
+ gtk_widget_get_pointer (area, &x, &y);
+
+ bevent = (GdkEventButton *) event;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ switch (bevent->button)
+ {
+ case 2:
+ //cmaskint.drag_mode = DRAG_BUMPMAP;
+ break;
+
+ default:
+ p_fetch_layercolor_and_maskcolor(x, y);
+ return FALSE;
+ }
+
+ cmaskint.mouse_x = x;
+ cmaskint.mouse_y = y;
+
+ gtk_grab_add (area);
+
+ return TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+// if (cmaskint.drag_mode != DRAG_NONE)
+// {
+// gtk_grab_remove (area);
+// cmaskint.drag_mode = DRAG_NONE;
+// gimp_preview_invalidate (preview);
+//
+// return TRUE;
+// }
+
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ dx = x - cmaskint.mouse_x;
+ dy = y - cmaskint.mouse_y;
+
+ cmaskint.mouse_x = x;
+ cmaskint.mouse_y = y;
+
+ if ((dx == 0) && (dy == 0))
+ break;
+
+// switch (cmaskint.drag_mode)
+// {
+// case DRAG_BUMPMAP:
+// cmaskvals->xofs = CLAMP (cmaskvals->xofs - dx, -1000, 1000);
+// g_signal_handlers_block_by_func (cmaskint.offset_adj_x,
+// gimp_int_adjustment_update,
+// &cmaskvals->xofs);
+// gtk_adjustment_set_value (GTK_ADJUSTMENT (cmaskint.offset_adj_x),
+// cmaskvals->xofs);
+// g_signal_handlers_unblock_by_func (cmaskint.offset_adj_x,
+// gimp_int_adjustment_update,
+// &cmaskvals->xofs);
+//
+// cmaskvals->yofs = CLAMP (cmaskvals->yofs - dy, -1000, 1000);
+// g_signal_handlers_block_by_func (cmaskint.offset_adj_y,
+// gimp_int_adjustment_update,
+// &cmaskvals->yofs);
+// gtk_adjustment_set_value (GTK_ADJUSTMENT (cmaskint.offset_adj_y),
+// cmaskvals->yofs);
+// g_signal_handlers_unblock_by_func (cmaskint.offset_adj_y,
+// gimp_int_adjustment_update,
+// &cmaskvals->yofs);
+// break;
+//
+// default:
+// return FALSE;
+// }
+
+ gimp_preview_invalidate (preview);
+
+ return TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/* -------------------------
+ * p_dialog_new_colormask
+ * -------------------------
+ */
+static void
+p_dialog_new_colormask (gboolean init_offsets)
+{
+ if(cmaskint.pftDest != NULL)
+ {
+ gimp_pixel_fetcher_destroy (cmaskint.pftDest);
+ cmaskint.pftDest = NULL;
+ }
+
+ /* Get drawable */
+ if (cmaskint.colormask_drawable != NULL)
+ {
+ if (cmaskint.colormask_drawable != cmaskint.layer_drawable)
+ {
+ gimp_drawable_detach (cmaskint.colormask_drawable);
+ }
+ }
+
+ if(cmaskint.pftMask != NULL)
+ {
+ gimp_pixel_fetcher_destroy (cmaskint.pftMask);
+ cmaskint.pftMask = NULL;
+ }
+
+ if (cmaskint.cmaskvalsPtr->colormask_id != -1)
+ {
+ cmaskint.colormask_drawable = gimp_drawable_get (cmaskint.cmaskvalsPtr->colormask_id);
+ }
+ else
+ {
+ cmaskint.colormask_drawable = cmaskint.layer_drawable;
+ }
+
+ if (cmaskint.colormask_drawable == NULL)
+ {
+ return;
+ }
+
+ cmaskint.pftMask = gimp_pixel_fetcher_new (cmaskint.colormask_drawable, FALSE /* shadow */);
+ gimp_pixel_fetcher_set_edge_mode (cmaskint.pftMask, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+ if(cmaskint.layer_drawable != NULL)
+ {
+ cmaskint.pftDest = gimp_pixel_fetcher_new (cmaskint.layer_drawable, FALSE /* shadow */);
+ gimp_pixel_fetcher_set_edge_mode (cmaskint.pftDest, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+ }
+
+ /* Get sizes */
+ cmaskint.bm_width = gimp_drawable_width (cmaskint.colormask_drawable->drawable_id);
+ cmaskint.bm_height = gimp_drawable_height (cmaskint.colormask_drawable->drawable_id);
+ cmaskint.bm_bpp = gimp_drawable_bpp (cmaskint.colormask_drawable->drawable_id);
+ cmaskint.bm_has_alpha = gimp_drawable_has_alpha (cmaskint.colormask_drawable->drawable_id);
+
+// if (init_offsets)
+// {
+// GtkAdjustment *adj;
+// gint bump_offset_x;
+// gint bump_offset_y;
+// gint draw_offset_y;
+// gint draw_offset_x;
+//
+// gimp_drawable_offsets (cmaskint.colormask_drawable->drawable_id,
+// &bump_offset_x, &bump_offset_y);
+// gimp_drawable_offsets (cmaskint.layer_drawable->drawable_id,
+// &draw_offset_x, &draw_offset_y);
+//
+// cmaskvals->xofs = draw_offset_x - bump_offset_x;
+// cmaskvals->yofs = draw_offset_y - bump_offset_y;
+//
+// adj = (GtkAdjustment *) cmaskint.offset_adj_x;
+// if (adj)
+// {
+// adj->value = cmaskvals->xofs;
+// g_signal_handlers_block_by_func (adj,
+// gimp_int_adjustment_update,
+// &cmaskvals->xofs);
+// gtk_adjustment_value_changed (adj);
+// g_signal_handlers_unblock_by_func (adj,
+// gimp_int_adjustment_update,
+// &cmaskvals->xofs);
+// }
+//
+// adj = (GtkAdjustment *) cmaskint.offset_adj_y;
+// if (adj)
+// {
+// adj->value = cmaskvals->yofs;
+// g_signal_handlers_block_by_func (adj,
+// gimp_int_adjustment_update,
+// &cmaskvals->yofs);
+// gtk_adjustment_value_changed (adj);
+// g_signal_handlers_unblock_by_func (adj,
+// gimp_int_adjustment_update,
+// &cmaskvals->yofs);
+// }
+// }
+
+ /* Initialize pixel region */
+ gimp_pixel_rgn_init (&cmaskint.colormask_rgn, cmaskint.colormask_drawable,
+ 0, 0, cmaskint.bm_width, cmaskint.bm_height, FALSE, FALSE);
+
+} /* end p_dialog_new_colormask */
+
+static void
+dialog_update_preview (GimpPreview *preview)
+{
+ guchar *dest_row;
+ gint y;
+ gint x1, y1;
+ gint width, height;
+ gint bytes;
+
+ gimp_preview_get_position (preview, &x1, &y1);
+
+// if(gap_debug)
+ {
+ printf("dialog_update_preview x1:%d y1:%d\n"
+ ,(int)x1
+ ,(int)y1
+ );
+ }
+
+ if ((cmaskint.colormask_drawable != cmaskint.layer_drawable)
+ && (cmaskint.layer_drawable != NULL)
+ && (cmaskint.applyImmediate == TRUE)
+ && (cmaskint.cmaskvalsPtr->keepLayerMask == TRUE))
+ {
+ gint32 drawableId;
+
+ drawableId = cmaskint.layer_drawable->drawable_id;
+
+ gap_colormask_apply_to_layer_of_same_size (drawableId
+ , cmaskint.cmaskvalsPtr
+ , TRUE // doProgress
+ );
+ gimp_drawable_flush (cmaskint.layer_drawable);
+ gimp_displays_flush ();
+ }
+
+
+// gimp_preview_get_size (preview, &width, &height);
+// bytes = cmaskint.layer_drawable->bpp;
+//
+// /* Initialize source rows */
+// gimp_pixel_rgn_init (&cmaskint.src_rgn, cmaskint.layer_drawable,
+// sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE);
+//
+// cmaskint.src_rows = g_new (guchar *, height);
+//
+// for (y = 0; y < height; y++)
+// cmaskint.src_rows[y] = g_new (guchar, sel_width * 4);
+//
+// dialog_fill_src_rows (0, height, y1);
+//
+// /* Initialize bumpmap rows */
+// cmaskint.bm_rows = g_new (guchar *, height + 2);
+//
+// for (y = 0; y < height + 2; y++)
+// cmaskint.bm_rows[y] = g_new (guchar, cmaskint.bm_width * cmaskint.bm_bpp);
+//
+// bumpmap_init_params (&cmaskint.params);
+//
+// dialog_fill_bumpmap_rows (0, height, cmaskvals->yofs + y1);
+//
+// dest_row = g_new (guchar, width * height * 4);
+//
+// /* Bumpmap */
+//
+// for (y = 0; y < height; y++)
+// {
+// gint isfirst = ((y == - cmaskvals->yofs - y1)
+// && ! cmaskvals->tiled) ? 1 : 0;
+// gint islast = (y == (- cmaskvals->yofs - y1
+// + cmaskint.bm_height - 1) && ! cmaskvals->tiled) ? 1 : 0;
+// bumpmap_row (cmaskint.src_rows[y] + 4 * x1,
+// dest_row + 4 * width * y,
+// width, 4, TRUE,
+// cmaskint.bm_rows[y + isfirst],
+// cmaskint.bm_rows[y + 1],
+// cmaskint.bm_rows[y + 2 - islast],
+// cmaskint.bm_width, cmaskvals->xofs + x1,
+// cmaskvals->tiled,
+// y >= - cmaskvals->yofs - y1 &&
+// y < (- cmaskvals->yofs - y1 + cmaskint.bm_height),
+// &cmaskint.params);
+//
+// }
+//
+// gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->area),
+// 0, 0, width, height,
+// GIMP_RGBA_IMAGE,
+// dest_row,
+// 4 * width);
+//
+// g_free (dest_row);
+//
+// for (y = 0; y < height + 2; y++)
+// g_free (cmaskint.bm_rows[y]);
+// g_free (cmaskint.bm_rows);
+//
+// for (y = 0; y < height; y++)
+// g_free (cmaskint.src_rows[y]);
+// g_free (cmaskint.src_rows);
+}
+
+
+// static void
+// dialog_get_rows (GimpPixelRgn *pr,
+// guchar **rows,
+// gint x,
+// gint y,
+// gint width,
+// gint height)
+// {
+// /* This is shamelessly ripped off from gimp_pixel_rgn_get_rect().
+// * Its function is exactly the same, but it can fetch an image
+// * rectangle to a sparse buffer which is defined as separate
+// * rows instead of one big linear region.
+// */
+//
+// gint xstart, ystart;
+// gint xend, yend;
+// gint xboundary;
+// gint yboundary;
+// gint xstep, ystep;
+// gint b, bpp;
+// gint tx, ty;
+// gint tile_width = gimp_tile_width();
+// gint tile_height = gimp_tile_height();
+//
+// bpp = pr->bpp;
+//
+// xstart = x;
+// ystart = y;
+// xend = x + width;
+// yend = y + height;
+// ystep = 0; /* Shut up -Wall */
+//
+// while (y < yend)
+// {
+// x = xstart;
+//
+// while (x < xend)
+// {
+// GimpTile *tile;
+//
+// tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+// gimp_tile_ref (tile);
+//
+// xstep = tile->ewidth - (x % tile_width);
+// ystep = tile->eheight - (y % tile_height);
+// xboundary = x + xstep;
+// yboundary = y + ystep;
+// xboundary = MIN (xboundary, xend);
+// yboundary = MIN (yboundary, yend);
+//
+// for (ty = y; ty < yboundary; ty++)
+// {
+// const guchar *src;
+// guchar *dest;
+//
+// src = tile->data + tile->bpp * (tile->ewidth *
+// (ty % tile_height) +
+// (x % tile_width));
+// dest = rows[ty - ystart] + bpp * (x - xstart);
+//
+// for (tx = x; tx < xboundary; tx++)
+// for (b = bpp; b; b--)
+// *dest++ = *src++;
+// }
+//
+// gimp_tile_unref (tile, FALSE);
+//
+// x += xstep;
+// }
+//
+// y += ystep;
+// }
+// }
+//
+// static void
+// dialog_fill_src_rows (gint start,
+// gint how_many,
+// gint yofs)
+// {
+// gint x;
+// gint y;
+//
+// dialog_get_rows (&cmaskint.src_rgn,
+// cmaskint.src_rows + start,
+// 0/*sel_x1*/,
+// yofs,
+// sel_width,
+// how_many);
+//
+// /* Convert to RGBA. We move backwards! */
+//
+// for (y = start; y < (start + how_many); y++)
+// {
+// const guchar *sp = cmaskint.src_rows[y] + img_bpp * sel_width - 1;
+// guchar *p = cmaskint.src_rows[y] + 4 * sel_width - 1;
+//
+// for (x = 0; x < sel_width; x++)
+// {
+// if (img_has_alpha)
+// *p-- = *sp--;
+// else
+// *p-- = 255;
+//
+// if (img_bpp < 3)
+// {
+// *p-- = *sp;
+// *p-- = *sp;
+// *p-- = *sp--;
+// }
+// else
+// {
+// *p-- = *sp--;
+// *p-- = *sp--;
+// *p-- = *sp--;
+// }
+// }
+// }
+// }
+//
+// static void
+// dialog_fill_bumpmap_rows (gint start,
+// gint how_many,
+// gint yofs)
+// {
+// gint buf_row_ofs;
+// gint remaining;
+// gint this_pass;
+//
+// /* Adapt to offset of selection */
+// yofs = MOD (yofs + sel_y1, cmaskint.bm_height);
+//
+// buf_row_ofs = start;
+// remaining = how_many;
+//
+// while (remaining > 0)
+// {
+// this_pass = MIN (remaining, cmaskint.bm_height - yofs);
+//
+// dialog_get_rows (&cmaskint.colormask_rgn,
+// cmaskint.bm_rows + buf_row_ofs,
+// 0,
+// yofs,
+// cmaskint.bm_width,
+// this_pass);
+//
+// yofs = (yofs + this_pass) % cmaskint.bm_height;
+// remaining -= this_pass;
+// buf_row_ofs += this_pass;
+// }
+//
+// /* Convert rows */
+//
+// for (; how_many; how_many--)
+// {
+// bumpmap_convert_row (cmaskint.bm_rows[start],
+// cmaskint.bm_width,
+// cmaskint.bm_bpp,
+// cmaskint.bm_has_alpha,
+// cmaskint.params.lut);
+//
+// start++;
+// }
+// }
+
+
+
+
+
+static gboolean
+dialog_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ return (gimp_drawable_is_rgb (drawable_id));
+}
+
+static void
+dialog_colormask_callback (GtkWidget *widget,
+ GimpPreview *preview)
+{
+ gint32 drawable_id;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &drawable_id);
+
+ if (cmaskint.cmaskvalsPtr->colormask_id != drawable_id)
+ {
+ cmaskint.cmaskvalsPtr->colormask_id = drawable_id;
+ p_dialog_new_colormask (TRUE);
+ gimp_preview_invalidate (preview);
+ }
+}
+
+
+/* ---------------------------
+ * gap_colormask_create_dialog
+ * ---------------------------
+ */
+GtkWidget*
+gap_colormask_create_dialog (GapColormaskValues *cmaskvals)
+{
+ GtkWidget *dialog;
+ GtkWidget *paned;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *preview;
+ GtkWidget *frame;
+ GtkObject *adj;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Color Mask"), PLUG_IN_BINARY,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_NAME_HELP,
+
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ cmaskint.pftDest = NULL;
+ cmaskint.pftMask = NULL;
+
+ paned = gtk_hpaned_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (paned), 12);
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), paned);
+ gtk_widget_show (paned);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_paned_pack1 (GTK_PANED (paned), hbox, TRUE, FALSE);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
+ gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ preview = gimp_drawable_preview_new (cmaskint.layer_drawable, NULL);
+ gtk_container_add (GTK_CONTAINER (hbox), preview);
+ gtk_widget_show (preview);
+
+ g_signal_connect (preview, "invalidated",
+ G_CALLBACK (dialog_update_preview),
+ NULL);
+ g_signal_connect (GIMP_PREVIEW (preview)->area, "event",
+ G_CALLBACK (dialog_preview_events), preview);
+
+ cmaskint.preview = preview;
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_paned_pack2 (GTK_PANED (paned), hbox, FALSE, FALSE);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+
+ /* Base options */
+ frame = p_create_base_options(cmaskvals, preview);
+ cmaskint.baseFrame = frame;
+ gtk_widget_show (frame);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ /* Expert options */
+ frame = p_create_expert_options(cmaskvals, preview);
+ cmaskint.expertFrame = frame;
+ gtk_widget_show (frame);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (hbox), vbox);
+ gtk_widget_show (vbox);
+
+ /* Filter options */
+ frame = p_create_filter_options(cmaskvals, preview);
+ cmaskint.filterFrame = frame;
+ gtk_widget_hide (frame);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ /* Opacity options */
+ frame = p_create_opacity_options(cmaskvals, preview);
+ cmaskint.opacityFrame = frame;
+ gtk_widget_show (frame);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ /* Debug options */
+ frame = p_create_debug_options(cmaskvals, preview);
+ cmaskint.debugFrame = frame;
+ gtk_widget_hide (frame);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ p_update_option_frames_visiblity_callback(NULL, NULL);
+
+ /* Initialise drawable
+ * (don't initialize offsets if colormask_id is already known)
+ */
+ if (cmaskvals->colormask_id == -1)
+ {
+ p_dialog_new_colormask (TRUE);
+ }
+ else
+ {
+ p_dialog_new_colormask (FALSE);
+ }
+
+ return (dialog);
+} /* end gap_colormask_create_dialog */
+
+
+
+/* --------------------
+ * gap_colormask_dialog
+ * --------------------
+ */
+gboolean
+gap_colormask_dialog (GapColormaskValues *cmaskvals, GimpDrawable *layer_drawable)
+{
+ GtkWidget *dialog;
+ gboolean run;
+
+ cmaskint.applyImmediate = FALSE;
+ cmaskint.layer_drawable = layer_drawable;
+ cmaskint.paramFilename = g_strdup("colormask.params");
+ cmaskint.cmaskvalsPtr = cmaskvals;
+
+
+ dialog = gap_colormask_create_dialog(cmaskvals);
+
+ /* Done */
+
+ gtk_widget_show (dialog);
+
+ run = FALSE;
+ if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK)
+ {
+ // TODO param file save shall be done in the procedure that
+ // creates the dialog as pop-up for Storybord clip properties.
+ // for testpurpose saving is done here until the rest is implemented...
+ gap_colormask_file_save (cmaskint.paramFilename, cmaskvals);
+ run = TRUE;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ if (cmaskint.colormask_drawable != cmaskint.layer_drawable)
+ {
+ gimp_drawable_detach (cmaskint.colormask_drawable);
+ }
+
+ //if(gap_debug)
+ {
+ printf("gap_colormask_dialog run:%d\n", (int)run);
+ }
+
+ g_free(cmaskint.paramFilename);
+
+ return run;
+
+} /* end gap_colormask_dialog */
diff --git a/gap/gap_colormask_dialog.h b/gap/gap_colormask_dialog.h
new file mode 100644
index 0000000..407aee9
--- /dev/null
+++ b/gap/gap_colormask_dialog.h
@@ -0,0 +1,45 @@
+/* gap_colormask_dialog.h
+ * by hof (Wolfgang Hofer)
+ * colormask filter dialog handling procedures
+ * 2010/02/25
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#ifndef _GAP_COLORMASK_DIALOG_H
+#define _GAP_COLORMASK_DIALOG_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+
+GtkWidget* gap_colormask_create_dialog (GapColormaskValues *cmaskvals);
+
+gboolean gap_colormask_dialog (GapColormaskValues *cmaskvals, GimpDrawable *layer_drawable);
+
+#endif
diff --git a/gap/gap_colormask_exec.c b/gap/gap_colormask_exec.c
new file mode 100644
index 0000000..63d3cb3
--- /dev/null
+++ b/gap/gap_colormask_exec.c
@@ -0,0 +1,4650 @@
+/* gap_colormask_exec.c
+ * by hof (Wolfgang Hofer)
+ * color mask filter worker procedures
+ * to set alpha channel for a layer according to matching colors
+ * of color mask (image)
+ * 2010/03/21
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_pdb_calls.h"
+#include "gap_colordiff.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_exec.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+
+#define WORK_PROTECTED 255 /* marker for protected pixels */
+#define WORK_PART_OF_CURRENT_AREA 250 /* marker for pixels that are part of current processed area */
+#define WORK_PART_OF_BIG_AREA 245 /* marker for pixels that are part of another already processed area */
+#define WORK_PART_OF_SMALL_AREA 240 /* marker for pixels that are part of another already processed area */
+#define WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA 1
+#define WORK_UNCHECKED 0 /* marker for unchecked pixels */
+
+#define IDX_ALPHA 3 /* in RGBA apha channel has index 3 */
+
+#define COLORMASK_LAYER_NAME "colormask"
+#define COLORMASK_DIFF_LAYER_NAME "diffLayer"
+
+#define CLIP_AREA_MAX_RADIUS 3
+#define CLIP_AREA_MAX_SIZE (1 + (2 * CLIP_AREA_MAX_RADIUS))
+
+#define MI_IMAGE 0
+#define MI_MASK 1
+#define MI_MAX 2
+
+#define ISLE_AREA_MAX_RADIUS 10
+#define ISLE_AREA_MAX_SIZE (1 + (2 * ISLE_AREA_MAX_RADIUS))
+
+
+#define MATCHTYPE_UNDEFINED 0
+#define MATCHTYPE_MATCHING 1
+#define MATCHTYPE_NOT_MATCHING 2
+
+
+
+extern int gap_debug; /* ==0 ... dont print debug infos */
+
+
+typedef struct GapAreaCoordPoint {
+ gint x;
+ gint y;
+ void *next;
+} GapAreaCoordPoint;
+
+
+typedef struct GapColorMaskParams { /* nickname: cmaskParPtr */
+ gint32 dst_layer_id;
+ gint32 cmask_drawable_id;
+ gdouble colorThreshold; /* dynamic keycolor dependent color difference lower threshold 0.0 to 1.0 */
+ gdouble loColorThreshold; /* color difference lower threshold 0.0 to 1.0 */
+ gdouble hiColorThreshold; /* color difference upper threshold 0.0 to 1.0 */
+ gdouble colorSensitivity; /* 1.0 to 2.0 sensitivity for colr diff algorithm */
+ gdouble lowerOpacity; /* opacity for matching colors 0.0 to 1.0 */
+ gdouble upperOpacity; /* opacity for non matching colors 0.0 to 1.0 */
+ gdouble triggerAlpha; /* 0.0 to 1.0 mask opacity greater than this alpha value trigger color masking */
+ gdouble isleRadius; /* radius in pixels for checking isolated pixels */
+ gdouble isleAreaPixelLimit; /* size of area in pixels for remove isaolated pixel(area)s */
+ gdouble featherRadius; /* radius in pixels for feathering edges */
+ gdouble thresholdColorArea;
+ gboolean connectByCorner;
+ gdouble pixelDiagonal;
+ gdouble pixelAreaLimit;
+ gboolean keepLayerMask;
+ gboolean keepWorklayer;
+ gdouble edgeColorThreshold;
+ gint algorithm;
+
+ /* stuff for dynamic keycolor dependent threshold handling */
+ GimpHSV keyColorHsv;
+ gdouble loKeyColorThreshold; /* color difference lower threshold for keycolor 0.0 to 1.0 */
+ gdouble keyColorSensitivity; /* 0.1 upto 10.0 default 1.0 */
+ gboolean enableKeyColorThreshold;
+
+ /* internal and precalculated values */
+ guchar adjustOpacityLevel;
+ gint adjustLoopCount;
+
+ gdouble shadeRange;
+ guchar triggerAlpha255;
+ guchar lowerAlpha255;
+ guchar upperAlpha255;
+ guchar transparencyLevel255;
+ gdouble opacityFactor; /* 1.0 in case opacity lower/upper use the full range */
+ gint32 dst_image_id;
+ gboolean debugCoordinate;
+ gint guideRow;
+ gint guideCol;
+ gint x; /* current processed coordinate */
+ gint y;
+
+ gint width; /* current processed drawable width */
+ gint height;
+
+ GimpPixelFetcher *pftMask;
+ GimpPixelFetcher *pftDest;
+ GimpPixelFetcher *pftDestLayerMask;
+ gint32 dstLayerMaskId;
+
+ gint jaggedRadius;
+ gint jeggedLength;
+
+ /* area context stuff */
+
+ GapAreaCoordPoint *pointList;
+ gint32 pointCount;
+ gint32 pixelCount;
+ gint32 max_ix;
+ gint32 max_iy;
+ gint32 min_ix;
+ gint32 min_iy;
+ gint32 seed_ix;
+ gint32 seed_iy;
+ guchar marker;
+ gint currentAreaPass;
+
+ gint sel_x1;
+ gint sel_y1;
+ gint sel_x2;
+ gint sel_y2;
+
+ gint32 worklayerId;
+ guchar seedPixel[4];
+ GimpHSV seedHsv;
+ GimpPixelFetcher *pftWork;
+ gdouble sumColorDiff;
+ gdouble avgColorDiff;
+ guchar areaPrecalculatedOpacity;
+ gboolean usePrecalculatedOpacity;
+
+
+ /* stuff for average algorithm handling */
+ gint checkRadius;
+ gint checkMatchRadius;
+ gdouble *colordiffTable; // big colordiff lookup table in full image size
+ gdouble avgFactor; // == 0.5 // 0.0 to 1.0
+ gdouble avgColorThreshold; // == colorThreshold + ((hiColorThreshold - colorThreshold) * avgFactor)
+
+
+ gint32 areaMinimumPixels; // GUI Parameter 5 to (0.9 * CLIP_AREA_MAX_SIZE * CLIP_AREA_MAX_SIZE)
+
+ gint32 maskAreaSize; // ... area of similar color to current image pixel in the colormask
+ gint32 imgAreaSize; // ... area of similar color to current
+ gint32 maskOnlyAreaSize; // ... area pixels that are part of the mask area but NOT part of the image area
+ gint32 imgOnlyAreaSize; // ... area pixels that are part of the image area but NOT part of the mask area
+ gint32 commonAreaSize; // ... common pixels that are member in both image and mask area
+
+ gdouble sumMatch[MI_MAX];
+ gdouble sumRange[MI_MAX];
+ gdouble sumNoMatch[MI_MAX];
+ gdouble countMatch[MI_MAX];
+ gdouble countRange[MI_MAX];
+ gdouble countNoMatch[MI_MAX];
+
+ gdouble avgColordiffMaskArea;
+ gdouble avgColordiffImgArea;
+ gdouble avgColordiffMaskOnlyArea;
+ gdouble avgColordiffImgOnlyArea;
+ gdouble avgColordiffCommonArea;
+ gdouble sumColordiffMaskOnlyArea;
+ gdouble sumColordiffImgOnlyArea;
+ gdouble sumColordiffCommonArea;
+
+ /* stuff for GAP_COLORMASK_ALGO_AVG_CHANGE */
+ gint significantRadius; /* radius to limit search for significant brightness/colorchanges */
+ gdouble significantColordiff; /* threshold to define when colors are considered as significant different */
+ gdouble significantBrightnessDiff; /* threshold to define significant brightness changes */
+
+
+ gint32 seedOffsetX; /* for convert image coords to clipAreaTable coords */
+ gint32 seedOffsetY;
+ guchar clipAreaTable[CLIP_AREA_MAX_SIZE][CLIP_AREA_MAX_SIZE][MI_MAX];
+ guchar isleAreaTable[ISLE_AREA_MAX_SIZE][ISLE_AREA_MAX_SIZE];
+
+
+
+ /* stuff for progress handling */
+ gboolean doProgress;
+ gdouble pixelsToProcessInAllPasses;
+ gdouble pixelsDoneAllPasses;
+
+ } GapColorMaskParams;
+
+static inline void p_set_dynamic_threshold_by_KeyColor(guchar *pixel
+ , GapColorMaskParams *cmaskParPtr);
+
+
+static void p_colormask_avg_rgn_render_region (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *destPR
+ ,const GimpPixelRgn *lmskPR
+ ,GapColorMaskParams *cmaskParPtr);
+
+static gboolean p_check_matching_pixel_isolation(GapColorMaskParams *cmaskParPtr);
+static guchar p_calc_opacity_for_matching_color(GapColorMaskParams *cmaskParPtr);
+static guchar p_calc_opacity_for_range_color(GapColorMaskParams *cmaskParPtr);
+static void p_check_range_neighbour_sum(GapColorMaskParams *cmaskParPtr, gint dx, gint dy
+ , gint radius, gdouble *sumNbColorDiff, gdouble *countNbPixels);
+
+
+static void p_countPerColordiff(GapColorMaskParams *cmaskParPtr, gdouble colordiff, gint mi);
+static void p_avg_check_and_mark_nb_pixel(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint nx, gint ny, gint mi);
+static void p_find_pixel_area_of_similar_color(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint mi);
+static void p_calculate_clip_area_average_values(GapColorMaskParams *cmaskParPtr);
+
+
+static gboolean p_check_average_colordiff_incl_neighbours(GapColorMaskParams *cmaskParPtr);
+
+static gboolean p_check_significant_diff(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gint xx, gint yy
+ , GapColorMaskParams *cmaskParPtr);
+static guchar p_check_significant_changes_in_one_direction(GapColorMaskParams *cmaskParPtr, gint dx, gint dy);
+
+static inline void p_check_avg_add_one_pixel(GapColorMaskParams *cmaskParPtr, gint xx, gint yy // DEPRECATED
+ , gdouble *sumNbColorDiff, gdouble *countNbPixels);
+static gdouble p_check_avg_diff_within_radius(GapColorMaskParams *cmaskParPtr, gint radius // DEPRECATED
+ , gdouble *sumNbColorDiff, gdouble *countNbPixels);
+
+
+static void p_init_colordiffTable (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *destPR
+ ,GapColorMaskParams *cmaskParPtr);
+static inline gint32 p_getColordiffTableIndex(GapColorMaskParams *cmaskParPtr, gint x, gint y);
+
+
+static void p_difflayer_rgn_render (const GimpPixelRgn *diffPR
+ , const GimpPixelRgn *destPR, GapColorMaskParams *cmaskParPtr);
+
+
+static void p_create_colordiffTable_Layer(GapColorMaskParams *cmaskParPtr, GimpDrawable *dst_drawable);
+
+
+
+
+
+
+
+/* ---------------------------------
+ * p_handle_progress
+ * ---------------------------------
+ * handle progress based on the specified amount of processed pixels
+ */
+static void
+p_handle_progress(GapColorMaskParams *cmaskParPtr, gint pixels, const char *caller)
+{
+ gdouble progress;
+
+ cmaskParPtr->pixelsDoneAllPasses += pixels;
+ progress = cmaskParPtr->pixelsDoneAllPasses / cmaskParPtr->pixelsToProcessInAllPasses;
+ gimp_progress_update(progress);
+
+ return;
+
+ if(gap_debug)
+ {
+ printf("Progress: %s (%.1f / %.1f) pixels:%d %.3f\n"
+ , caller
+ , (float)cmaskParPtr->pixelsDoneAllPasses
+ , (float)cmaskParPtr->pixelsToProcessInAllPasses
+ , (int)pixels
+ , (float)progress
+ );
+ }
+} /* end p_handle_progress */
+
+/* --------------------------------------------
+ * p_create_empty_layer
+ * --------------------------------------------
+ * create an empty layer (the worklayer) and add it to the specified image
+ *
+ */
+static gint32
+p_create_empty_layer(gint32 image_id
+ , gint32 width
+ , gint32 height
+ , const char *name
+ )
+{
+ gint32 layer_id;
+ GimpImageBaseType l_basetype;
+
+ l_basetype = gimp_image_base_type(image_id);
+ if(l_basetype != GIMP_RGB)
+ {
+ printf("** ERROR image != RGB is not supported\n");
+ return -1;
+ }
+
+ layer_id = gimp_layer_new(image_id
+ , name
+ , width
+ , height
+ , GIMP_RGBA_IMAGE
+ , 100.0 /* full opacity */
+ , 0 /* normal mode */
+ );
+
+ gimp_image_add_layer (image_id, layer_id, 0 /* stackposition */ );
+
+ return (layer_id);
+
+} /* end p_create_empty_layer */
+
+
+
+
+
+/* ---------------------------------
+ * p_colordiff_guchar_cmask
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+p_colordiff_guchar_cmask(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , GapColorMaskParams *cmaskParPtr)
+{
+ gdouble colordiffRgb;
+ gdouble colordiffHsv;
+
+ colordiffRgb = 0.0;
+ if (cmaskParPtr->connectByCorner == TRUE) ///// #### TODO
+ {
+ colordiffRgb = gap_colordiff_simple_guchar(aPixelPtr
+ , bPixelPtr
+ , cmaskParPtr->debugCoordinate
+ );
+ }
+
+ colordiffHsv = gap_colordiff_guchar(aPixelPtr
+ , bPixelPtr
+ , cmaskParPtr->colorSensitivity
+ , cmaskParPtr->debugCoordinate
+ );
+
+ return (MAX(colordiffRgb, colordiffHsv));
+
+} /* end p_colordiff_guchar_cmask */
+
+
+/* ---------------------------------
+ * p_colordiff_guchar_cmask_RGBorHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+p_colordiff_guchar_cmask_RGBorHSV(GimpHSV *aHsvPtr, guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , GapColorMaskParams *cmaskParPtr)
+{
+ gdouble colordiffRgb;
+ gdouble colordiffHsv;
+
+ colordiffRgb = 0.0;
+ if (cmaskParPtr->connectByCorner == TRUE) ///// #### TODO
+ {
+ colordiffRgb = gap_colordiff_simple_guchar(aPixelPtr
+ , bPixelPtr
+ , cmaskParPtr->debugCoordinate
+ );
+ }
+ colordiffHsv = gap_colordiff_guchar_GimpHSV(aHsvPtr
+ , bPixelPtr
+ , cmaskParPtr->colorSensitivity
+ , cmaskParPtr->debugCoordinate
+ );
+
+ return (MAX(colordiffRgb, colordiffHsv));
+
+} /* end p_colordiff_guchar_cmask_RGBorHSV */
+
+
+
+/* ----------------------------------
+ * p_get_guides
+ * ----------------------------------
+ * get 1st horizontal and vertical guide.
+ *
+ * note that guides are not relevant for the productive processing
+ * but the 1st guide crossing is used to specify
+ * a coordinate where debug output shall be printed.
+ */
+static void
+p_get_guides(GapColorMaskParams *cmaskParPtr)
+{
+ gint32 guide_id;
+
+ guide_id = 0;
+
+ cmaskParPtr->guideRow = -1;
+ cmaskParPtr->guideCol = -1;
+
+ if(cmaskParPtr->dst_image_id < 0)
+ {
+ return;
+ }
+
+
+ while(TRUE)
+ {
+ guide_id = gimp_image_find_next_guide(cmaskParPtr->dst_image_id, guide_id);
+
+ if (guide_id < 1)
+ {
+ break;
+ }
+ else
+ {
+ gint32 orientation;
+
+ orientation = gimp_image_get_guide_orientation(cmaskParPtr->dst_image_id, guide_id);
+ if(orientation != 0)
+ {
+ if(cmaskParPtr->guideCol < 0)
+ {
+ cmaskParPtr->guideCol = gimp_image_get_guide_position(cmaskParPtr->dst_image_id, guide_id);
+ }
+ }
+ else
+ {
+ if(cmaskParPtr->guideRow < 0)
+ {
+ cmaskParPtr->guideRow = gimp_image_get_guide_position(cmaskParPtr->dst_image_id, guide_id);
+ }
+ }
+ }
+
+ }
+
+ if(gap_debug)
+ {
+ printf("dst_image_id:%d guideCol:%d :%d\n"
+ ,(int)cmaskParPtr->dst_image_id
+ ,(int)cmaskParPtr->guideCol
+ ,(int)cmaskParPtr->guideRow
+ );
+ }
+
+} /* end p_get_guides */
+
+/* ----------------------------------
+ * p_is_debug_active_on_coordinate
+ * ----------------------------------
+ * check if current processed coords (x/y) are on first guide crossing
+ * (for debug purpuse to force logging at koordinates selected via guides)
+ */
+static gboolean
+p_is_debug_active_on_coordinate(GapColorMaskParams *cmaskParPtr)
+{
+ if(cmaskParPtr->x == cmaskParPtr->guideCol)
+ {
+ if(cmaskParPtr->y == cmaskParPtr->guideRow)
+ {
+ return(TRUE);
+ }
+ if (cmaskParPtr->guideRow == -1)
+ {
+ /* there is no vertical guide available, in this case match all rows */
+ return(TRUE);
+ }
+ }
+ else
+ {
+ if((cmaskParPtr->y == cmaskParPtr->guideRow) && (cmaskParPtr->guideCol == -1))
+ {
+ /* there is no horizontal guide available, in this case match all coloumns */
+ return(TRUE);
+ }
+ }
+ return (FALSE);
+
+} /* end p_is_debug_active_on_coordinate */
+
+
+
+
+/* --------------------------------------
+ * p_calculate_alpha_from_colormask_pixel (SIMPLE VARIANT)
+ * --------------------------------------
+ * calculates transparency according to
+ * color difference of individual pixel in the processed
+ * layer and its corresponding pixel in the colormask.
+ *
+ * returns the alpha channel as byte value.
+ */
+static inline guchar
+p_calculate_alpha_from_colormask_pixel(gint maskBpp
+ , guchar *maskPtr
+ , guchar *origPtr
+ , GapColorMaskParams *cmaskParPtr
+ )
+{
+ gdouble colorDiff;
+
+ if (maskBpp > 3)
+ {
+ /* colormask has an alpha channel */
+ if (maskPtr[3] <= cmaskParPtr->triggerAlpha255)
+ {
+ /* transparent mask (below or equal trigger value)
+ * switches off color masking for that pixel
+ * return 255 (full opaque) as opacity value
+ * for the layermask.
+ * Note that an opaque layermask pixel
+ * preserves opacity as set via alpha channel unchanged when the layermask is applied.
+ * (no need to check color differences in that case)
+ */
+ return (255);
+ }
+ }
+
+ colorDiff = p_colordiff_guchar_cmask(maskPtr
+ , origPtr
+ , cmaskParPtr
+ );
+
+ p_set_dynamic_threshold_by_KeyColor(origPtr, cmaskParPtr);
+
+ if(colorDiff <= cmaskParPtr->colorThreshold)
+ {
+ /* color matches good */
+ return (cmaskParPtr->lowerAlpha255);
+ }
+
+ if ((colorDiff < cmaskParPtr->hiColorThreshold)
+ && (cmaskParPtr->shadeRange > 0.0))
+ {
+ /* color matches extended range (calculate corresponding alpha shade value) */
+ gdouble opacity255; /* range 0.0 to 255.0 */
+ guchar alpha;
+ gdouble shadeFactor;
+
+ shadeFactor = (colorDiff - cmaskParPtr->colorThreshold) / cmaskParPtr->shadeRange;
+
+
+ opacity255 = GAP_BASE_MIX_VALUE(shadeFactor
+ , (gdouble)cmaskParPtr->lowerAlpha255
+ , (gdouble)cmaskParPtr->upperAlpha255
+ );
+ alpha = CLAMP((guchar)opacity255, 0, 255);
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("p_calculate_alpha x:%03d y:%03d shadeFactor:%.4f shadeRange:%.4f colorDiff:%.6f ALPHA:%d \n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)shadeFactor
+ ,(float)cmaskParPtr->shadeRange
+ ,(float)colorDiff
+ ,(int)alpha
+ );
+ }
+
+ return (alpha);
+ }
+
+ /* color does not match */
+ return (cmaskParPtr->upperAlpha255);
+
+} /* end p_calculate_alpha_from_colormask_pixel */
+
+
+
+
+/* ---------------------------------
+ * p_colormask_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_colormask_rgn_render_region (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *destPR
+ ,const GimpPixelRgn *lmskPR
+ ,GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* mask = maskPR->data; /* the colormask */
+ guchar* dest = destPR->data; /* the destination drawable */
+ guchar* lmsk = lmskPR->data; /* the layermask of the destination drawable */
+
+
+ for (row = 0; row < destPR->h; row++)
+ {
+ guint col;
+ guint idxMask;
+ guint idxDest;
+ guint idxLmsk;
+
+ idxMask = 0;
+ idxDest = 0;
+ idxLmsk = 0;
+ for(col = 0; col < destPR->w; col++)
+ {
+ cmaskParPtr->x = destPR->x + col;
+ cmaskParPtr->y = destPR->y + row;
+ lmsk[idxLmsk] = p_calculate_alpha_from_colormask_pixel(maskPR->bpp
+ , &mask[idxMask] /* maskPtr */
+ , &dest[idxDest] /* origPtr */
+ , cmaskParPtr
+ );
+
+ /* ------ start debug code block -----*/
+ //if(gap_debug)
+ {
+ cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+ if (cmaskParPtr->debugCoordinate)
+ {
+ gdouble colorDiff;
+ colorDiff = p_colordiff_guchar_cmask( &mask[idxMask]
+ , &dest[idxDest]
+ , cmaskParPtr
+ );
+
+ printf("colormask x:%03d y:%03d ALPHA:%d colorDiff:%.6f (colorThreshold:%.6f)"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(int)lmsk[idxLmsk]
+ ,(float)colorDiff
+ ,(float)cmaskParPtr->colorThreshold
+ );
+
+
+ if (maskPR->bpp > 3)
+ {
+ printf(" TRIGGER_ALPHA:%d MASK_ALPHA:%d"
+ ,(int)cmaskParPtr->triggerAlpha255
+ ,(int)mask[idxMask +3]
+ );
+ }
+
+ printf("\n");
+ }
+ }
+ /* ------ end debug code block -----*/
+
+
+ idxDest += destPR->bpp;
+ idxMask += maskPR->bpp;
+ idxLmsk += lmskPR->bpp;
+ }
+
+ mask += maskPR->rowstride;
+ dest += destPR->rowstride;
+ lmsk += lmskPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, maskPR->w, "p_colormask_rgn_render_region");
+ }
+ }
+
+} /* end p_colormask_rgn_render_region */
+
+
+
+
+
+
+
+
+
+
+/* --------------------------------
+ * p_add_point
+ * --------------------------------
+ * add specified coords as new element to the begin of the pointList
+ */
+static inline void
+p_add_point(GapColorMaskParams *cmaskParPtr, gint32 ix, gint32 iy)
+{
+ GapAreaCoordPoint *point;
+// static gint32 highWaterMark = 0;
+
+ point = g_new ( GapAreaCoordPoint, 1 );
+
+ point->x = ix;
+ point->y = iy;
+
+ point->next = cmaskParPtr->pointList;
+
+ cmaskParPtr->pointList = point;
+
+ cmaskParPtr->pointCount++;
+
+// //if(gap_debug)
+// {
+// if(cmaskParPtr->pointCount > highWaterMark)
+// {
+// highWaterMark = cmaskParPtr->pointCount;
+// printf("(%d) add pointCount:%d Seed x/y: (%d/%d) x/y: (%d/%d)\n"
+// ,(int)cmaskParPtr->currentAreaPass
+// ,(int)cmaskParPtr->pointCount
+// ,(int)cmaskParPtr->seed_ix
+// ,(int)cmaskParPtr->seed_iy
+// ,(int)ix
+// ,(int)iy
+// );
+// }
+// }
+//
+// if(cmaskParPtr->pointCount > (cmaskParPtr->width * cmaskParPtr->height))
+// {
+// printf("(%d) internal error... pointCount:%d seed x/y: (%d/%d)\n"
+// ,(int)cmaskParPtr->currentAreaPass
+// ,(int)cmaskParPtr->pointCount
+// ,(int)cmaskParPtr->seed_ix
+// ,(int)cmaskParPtr->seed_iy
+// );
+// exit(44);
+// }
+
+} /* end p_add_point */
+
+
+/* --------------------------------
+ * p_remove_first_point
+ * --------------------------------
+ * remove first element from the pointList
+ */
+static inline void
+p_remove_first_point(GapColorMaskParams *cmaskParPtr)
+{
+ GapAreaCoordPoint *point;
+
+ point = cmaskParPtr->pointList;
+
+ if (point != NULL)
+ {
+// if(gap_debug)
+// {
+// printf("(%d) remove pointCount:%d point x/y: (%d/%d)\n"
+// ,(int)cmaskParPtr->currentAreaPass
+// ,(int)cmaskParPtr->pointCount
+// ,(int)point->x
+// ,(int)point->y
+// );
+// }
+ cmaskParPtr->pointCount--;
+ cmaskParPtr->pointList = (GapAreaCoordPoint *)point->next;
+ g_free(point);
+ }
+ else
+ {
+ //if(gap_debug)
+ {
+ printf("(%d) remove pointCount:%d seed x/y: (%d/%d) pointList is NULL!\n"
+ ,(int)cmaskParPtr->currentAreaPass
+ ,(int)cmaskParPtr->pointCount
+ ,(int)cmaskParPtr->seed_ix
+ ,(int)cmaskParPtr->seed_iy
+ );
+ }
+ }
+
+} /* end p_remove_first_point */
+
+
+/* --------------------------------------------
+ * p_free_point_list
+ * --------------------------------------------
+ */
+static void
+p_free_point_list(GapColorMaskParams *cmaskParPtr)
+{
+ while(cmaskParPtr->pointList != NULL)
+ {
+ p_remove_first_point(cmaskParPtr);
+ }
+} /* end p_free_point_list */
+
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for EDGE ALGORITHM ----- START ------------ */
+/* -------------------------------------------------------------------- */
+
+/* --------------------------------------
+ * p_edge_calculate_pixel_opacity (EDGE VARIANT)
+ * --------------------------------------
+ * calculates transparency according to
+ * color difference of individual pixel in the processed
+ * layer and its corresponding pixel in the colormask.
+ *
+ * returns the calculated opacity as byte value.
+ */
+static inline guchar
+p_edge_calculate_pixel_opacity(gint maskBpp
+ , guchar *maskPtr
+ , guchar *origPtr
+ , guchar *origUpper
+ , guchar *origLeft
+ , guchar *lmskUpper
+ , guchar *lmskLeft
+ , GapColorMaskParams *cmaskParPtr
+ )
+{
+ gdouble colorDiff;
+ guchar nbAlpha;
+ guchar pixel[4];
+ guchar layermaskPixel[4];
+
+ if (maskBpp > 3)
+ {
+ /* colormask has an alpha channel */
+ if (maskPtr[3] <= cmaskParPtr->triggerAlpha255)
+ {
+ /* transparent mask (below or equal trigger value)
+ * switches off color masking for that pixel
+ * return 255 (full opaque) as opacity value
+ * for the layermask.
+ * Note that an opaque layermask pixel
+ * preserves opacity as set via alpha channel unchanged when the layermask is applied.
+ * (no need to check color differences in that case)
+ */
+ return (255);
+ }
+ }
+
+ colorDiff = p_colordiff_guchar_cmask(maskPtr
+ , origPtr
+ , cmaskParPtr
+ );
+
+ p_set_dynamic_threshold_by_KeyColor(origPtr, cmaskParPtr);
+
+ if(colorDiff <= cmaskParPtr->colorThreshold)
+ {
+ /* color matches good */
+ return (cmaskParPtr->lowerAlpha255);
+ }
+
+ if (colorDiff >= cmaskParPtr->hiColorThreshold)
+ {
+ /* color does not match */
+ return (cmaskParPtr->upperAlpha255);
+ }
+
+
+///////////////////////////// DEBUG
+// if(TRUE)
+// {
+// gdouble shadeFactor;
+// gdouble green;
+//
+// shadeFactor = (colorDiff - cmaskParPtr->colorThreshold) / cmaskParPtr->shadeRange;
+// green = GAP_BASE_MIX_VALUE(shadeFactor
+// , (gdouble)128.0
+// , (gdouble)255.0
+// );
+//
+// origPtr[0] = 32; /* origRed */
+// origPtr[1] = CLAMP((guchar)green, 0, 255);; /* origGreen */
+// origPtr[2] = 32; /* origBlue */
+//
+// return (cmaskParPtr->lowerAlpha255);
+// }
+///////////////////////////// DEBUG
+
+
+
+ /* color matches range between the thresholds
+ * in this case check if the current pixel is an edge pixel
+ * for edge pixels use lower alpha,
+ * for non-edge pixels use same alpha as the already processed left (or upper) neighbour pixel
+ */
+
+ nbAlpha = cmaskParPtr->lowerAlpha255;
+
+ /* check upper neigbour */
+// if(gap_debug)
+// {
+// printf("X/Y: %d/%d origUpper:%d lmskUpper:%d\n"
+// , (int)cmaskParPtr->x
+// , (int)cmaskParPtr->y
+// , (int)origUpper
+// , (int)lmskUpper
+// );
+// }
+
+ if (origUpper != NULL)
+ {
+ colorDiff = p_colordiff_guchar_cmask(origUpper
+ , origPtr
+ , cmaskParPtr
+ );
+ nbAlpha = lmskUpper[0];
+ }
+ else
+ {
+ /* current pixel is first in tile column
+ * in this special case use pixel fether to access neighbour pixel
+ */
+ if(cmaskParPtr->y > 0)
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest
+ , cmaskParPtr->x
+ , cmaskParPtr->y-1
+ , &pixel[0]
+ );
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , cmaskParPtr->x
+ , cmaskParPtr->y-1
+ , &layermaskPixel[0]
+ );
+ nbAlpha = layermaskPixel[0];
+ colorDiff = p_colordiff_guchar_cmask(&pixel[0]
+ , origPtr
+ , cmaskParPtr
+ );
+ }
+ else
+ {
+ colorDiff = 1.0; /* coordinate x == 0 is considered as edge pixel */
+ }
+ }
+
+ if (colorDiff >= cmaskParPtr->edgeColorThreshold)
+ {
+ return (cmaskParPtr->lowerAlpha255);
+ }
+
+
+
+ /* check left neigbour */
+
+// if(gap_debug)
+// {
+// printf("X/Y: %d/%d origLeft:%d lmskLeft:%d\n"
+// , (int)cmaskParPtr->x
+// , (int)cmaskParPtr->y
+// , (int)origLeft
+// , (int)lmskLeft
+// );
+// }
+
+ if (origLeft != NULL)
+ {
+ colorDiff = p_colordiff_guchar_cmask(origLeft
+ , origPtr
+ , cmaskParPtr
+ );
+ nbAlpha = lmskLeft[0];
+ }
+ else
+ {
+ /* current pixel is first in tile row
+ * in this special case use pixel fether to access neighbour pixel
+ */
+ if(cmaskParPtr->x > 0)
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest
+ , cmaskParPtr->x-1
+ , cmaskParPtr->y
+ , &pixel[0]
+ );
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , cmaskParPtr->x-1
+ , cmaskParPtr->y
+ , &layermaskPixel[0]
+ );
+ nbAlpha = layermaskPixel[0];
+ colorDiff = p_colordiff_guchar_cmask(&pixel[0]
+ , origPtr
+ , cmaskParPtr
+ );
+ }
+ else
+ {
+ colorDiff = 1.0; /* coordinate x == 0 is considered as edge pixel */
+ }
+ }
+
+ if (colorDiff >= cmaskParPtr->edgeColorThreshold)
+ {
+ return (cmaskParPtr->lowerAlpha255);
+ }
+
+
+ return (nbAlpha);
+} /* end p_edge_calculate_pixel_opacity */
+
+
+
+/* ---------------------------------
+ * p_colormask_edge_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_colormask_edge_rgn_render_region (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *origPR
+ ,const GimpPixelRgn *lmskPR
+ ,GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* mask = maskPR->data; /* the colormask */
+ guchar* orig = origPR->data; /* the drawable */
+ guchar* lmsk = lmskPR->data; /* the layermask of the drawable */
+
+ guchar* origUpper = NULL; /* the upper pixelrow of the drawable */
+ guchar* lmskUpper = NULL; /* the upper pixelrow of the layermask of the drawable */
+
+ for (row = 0; row < origPR->h; row++)
+ {
+ guchar* origLeft; /* the left neighbor pixel of the drawable */
+ guchar* lmskLeft; /* the upper pixelrow of the layermask of the drawable */
+ guint col;
+ guint idxMask;
+ guint idxOrig;
+ guint idxLmsk;
+
+ idxMask = 0;
+ idxOrig = 0;
+ idxLmsk = 0;
+ origLeft = NULL;
+ lmskLeft = NULL;
+
+ cmaskParPtr->y = origPR->y + row;
+
+// if(gap_debug)
+// {
+// printf("ROW:%d Y: %d origUpper:%d lmskUpper:%d\n"
+// , (int)row
+// , (int)cmaskParPtr->y
+// , (int)origUpper
+// , (int)lmskUpper
+// );
+// }
+
+ for(col = 0; col < origPR->w; col++)
+ {
+ cmaskParPtr->x = origPR->x + col;
+ if(origUpper != NULL)
+ {
+ lmsk[idxLmsk] = p_edge_calculate_pixel_opacity(maskPR->bpp
+ , &mask[idxMask] /* maskPtr */
+ , &orig[idxOrig] /* origPtr */
+ , &origUpper[idxOrig]
+ , origLeft
+ , &lmskUpper[idxLmsk]
+ , lmskLeft
+ , cmaskParPtr
+ );
+ }
+ else
+ {
+ lmsk[idxLmsk] = p_edge_calculate_pixel_opacity(maskPR->bpp
+ , &mask[idxMask] /* maskPtr */
+ , &orig[idxOrig] /* origPtr */
+ , NULL
+ , origLeft
+ , NULL
+ , lmskLeft
+ , cmaskParPtr
+ );
+ }
+
+ origLeft = &orig[idxOrig];
+ lmskLeft = &lmsk[idxLmsk];
+
+ idxOrig += origPR->bpp;
+ idxMask += maskPR->bpp;
+ idxLmsk += lmskPR->bpp;
+ }
+ lmskUpper = lmsk;
+ origUpper = orig;
+
+
+ mask += maskPR->rowstride;
+ orig += origPR->rowstride;
+ lmsk += lmskPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, maskPR->w, "p_colormask_edge_rgn_render_region");
+ }
+ }
+
+} /* end p_colormask_edge_rgn_render_region */
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for EDGE ALGORITHM ----- END ------------- */
+/* -------------------------------------------------------------------- */
+
+
+
+
+
+
+/* ---------------------------------------------
+ * p_get_distances_to_nearest_transparent_pixels
+ * ---------------------------------------------
+ * check distances to next transparent neghbour pixels (where opacity value in the layermask
+ * is lower or equal to the specified transparencyLevel value)
+ * in all 4 directions.
+ * [0] right neigbour
+ * [1] left neigbour
+ * [2] lower neigbour
+ * [3] upper neigbour
+ *
+ * Note: in case there is no transparent pixel found within featherRadius
+ * the nbDistance value for that direction is set to 100 + featherRadius
+ * to indicate that no transparent pixel is near.
+ */
+static inline void
+p_get_distances_to_nearest_transparent_pixels(GapColorMaskParams *cmaskParPtr, gdouble *nbDistance)
+{
+ guchar nbOrigPixels[4][4];
+ gint ii;
+ gint xx;
+ gint yy;
+
+ nbDistance[0] = 100 + cmaskParPtr->featherRadius;
+ nbDistance[1] = 100 + cmaskParPtr->featherRadius;
+ nbDistance[2] = 100 + cmaskParPtr->featherRadius;
+ nbDistance[3] = 100 + cmaskParPtr->featherRadius;
+
+
+ for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+ {
+ xx = cmaskParPtr->x + ii;
+ if (xx >= cmaskParPtr->width)
+ {
+ break;
+ }
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , cmaskParPtr->y
+ , nbOrigPixels[0]
+ );
+ if(nbOrigPixels[0][0] <= cmaskParPtr->transparencyLevel255)
+ {
+ nbDistance[0] = ii;
+ break;
+ }
+ }
+
+ for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+ {
+ xx = cmaskParPtr->x - ii;
+ if (xx < 0)
+ {
+ break;
+ }
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , cmaskParPtr->y
+ , nbOrigPixels[1]
+ );
+ if(nbOrigPixels[1][0] <= cmaskParPtr->transparencyLevel255)
+ {
+ nbDistance[1] = ii;
+ break;
+ }
+ }
+
+ for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+ {
+ yy = cmaskParPtr->y + ii;
+ if (yy >= cmaskParPtr->height)
+ {
+ break;
+ }
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , cmaskParPtr->x
+ , yy
+ , nbOrigPixels[2]
+ );
+ if(nbOrigPixels[2][0] <= cmaskParPtr->transparencyLevel255)
+ {
+ nbDistance[2] = ii;
+ break;
+ }
+ }
+
+ for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+ {
+ yy = cmaskParPtr->y - ii;
+ if (yy < 0)
+ {
+ break;
+ }
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , cmaskParPtr->x
+ , yy
+ , nbOrigPixels[3]
+ );
+ if(nbOrigPixels[3][0] <= cmaskParPtr->transparencyLevel255)
+ {
+ nbDistance[3] = ii;
+ break;
+ }
+ }
+
+
+} /* end p_get_distances_to_nearest_transparent_pixels */
+
+
+
+/* ---------------------------------
+ * p_blur_alpha_at_edge_pixels
+ * ---------------------------------
+ * calculate blurred alpha value depentent to distance to next
+ * transparent pixel (whre transparency is below level)
+ * to the layermask of the destination drawable.
+ */
+static inline guchar
+p_blur_alpha_at_edge_pixels(guchar currentAlpha, GapColorMaskParams *cmaskParPtr)
+{
+ gdouble distance[4];
+ gdouble minDistance;
+ gdouble opacity255; /* range 0.0 to 255.0 */
+ guchar alpha;
+ gdouble radiusFactor;
+ gint ii;
+
+
+ p_get_distances_to_nearest_transparent_pixels(cmaskParPtr, &distance[0]);
+
+ minDistance = 100 + cmaskParPtr->featherRadius;
+ for(ii=0; ii < 4; ii++)
+ {
+ if (distance[ii] < minDistance)
+ {
+ minDistance = distance[ii];
+ }
+ }
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("p_smooth_alpha_at_edges distances: %.1f %.1f %.1f %.1f minDistance %.1f\n"
+ ,(float)distance[0]
+ ,(float)distance[1]
+ ,(float)distance[2]
+ ,(float)distance[3]
+ ,(float)minDistance
+ );
+ }
+
+ if(minDistance > cmaskParPtr->featherRadius)
+ {
+ /* no transparent pixel is near, return the unchanged alpha value */
+ return (currentAlpha);
+ }
+
+ /* one of the neigbours within featherRadius is transparent (below transparencyLevel)
+ * now calculate the fading opacity for this current edge pixel
+ * (the bigger the distande, the more opaque)
+ * note that fading is done within the range of specified upper and lower opacity parameters
+ */
+ radiusFactor = minDistance / (1 + cmaskParPtr->featherRadius);
+ opacity255 = (gdouble)(currentAlpha - cmaskParPtr->transparencyLevel255);
+ opacity255 *= radiusFactor;
+ opacity255 *= cmaskParPtr->opacityFactor;
+
+ alpha = CLAMP(((guchar)opacity255 + cmaskParPtr->transparencyLevel255), 0, 255);
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("p_blur_alpha_at_edge_pixels at xy:(%d / %d) radiusFactor:%.4f opacityFactor:%.4f"
+ " transparencyLevel:%d opacity255: %.1f blured_ALPHA %d\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)radiusFactor
+ ,(float)cmaskParPtr->opacityFactor
+ ,(int)cmaskParPtr->transparencyLevel255
+ ,(float)opacity255
+ ,(int)alpha
+ );
+ }
+
+ return (alpha);
+
+} /* end p_blur_alpha_at_edge_pixels */
+
+
+
+/* ---------------------------------
+ * p_smooth_edges_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_smooth_edges_rgn_render_region (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *lmskPR
+ ,GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* mask = maskPR->data; /* the colormask */
+ guchar* lmsk = lmskPR->data; /* the layermask of the destination drawable */
+
+
+ for (row = 0; row < lmskPR->h; row++)
+ {
+ guint col;
+ guint idxMask;
+ guint idxLmsk;
+
+ idxMask = 0;
+ idxLmsk = 0;
+ for(col = 0; col < lmskPR->w; col++)
+ {
+ /* process only non transparent pixels in the layermask */
+ if (lmsk[idxLmsk] > cmaskParPtr->transparencyLevel255)
+ {
+ gboolean isProtected;
+
+ isProtected = FALSE;
+ if (maskPR->bpp > 3)
+ {
+ if (mask[idxMask +3] <= cmaskParPtr->triggerAlpha255)
+ {
+ isProtected = TRUE;
+ }
+ }
+
+ if (!isProtected)
+ {
+ cmaskParPtr->x = maskPR->x + col;
+ cmaskParPtr->y = maskPR->y + row;
+
+ //if(gap_debug)
+ {
+ cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+ }
+
+ /* re-calculate opacity respecting featherRadius near edges */
+ lmsk[idxLmsk] = p_blur_alpha_at_edge_pixels(lmsk[idxLmsk], cmaskParPtr);
+ }
+ }
+
+ idxMask += maskPR->bpp;
+ idxLmsk += lmskPR->bpp;
+ }
+
+ mask += maskPR->rowstride;
+ lmsk += lmskPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, maskPR->w, "p_smooth_edges_rgn_render_region");
+ }
+ }
+
+} /* end p_smooth_edges_rgn_render_region */
+
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for remove ISOLATED PIXELS ---------------- */
+/* -------------------------------------------------------------------- */
+
+/* --------------------------------------
+ * p_map_alpha
+ * --------------------------------------
+ * returns 0 if alpha considerd as transparent
+ * otherwise 255
+ */
+static inline guchar
+p_map_alpha(GapColorMaskParams *cmaskParPtr, guchar value)
+{
+ if (value <= cmaskParPtr->transparencyLevel255)
+ {
+ return (0);
+ }
+ return (255);
+} /* end p_map_alpha */
+
+
+
+
+/* ---------------------------------------------
+ * p_is_isolated_pixel
+ * ---------------------------------------------
+ * returns TRUE in case pixel is isolated
+ * the isolation check is done by comparing the currentAlpha value
+ * against neighbour pixels in the layermask.
+ * In case there are no (or too few) neigbours with matching opacity
+ * the pixel is considered as isolated pixel
+ *
+ */
+static inline gboolean
+p_is_isolated_pixel(GapColorMaskParams *cmaskParPtr, guchar currentAlpha)
+{
+ static gboolean nbCountLookup[5][5] = {
+ /* 0 1 2 3 4 countCornerNb
+ *
+ * ... #.. #.. #.# #.#
+ * .o. .o. .o. .o. .o.
+ * ... ... ..# ..# #.#
+ */
+ { TRUE, TRUE, TRUE, TRUE, TRUE } /* countEdgeNb == 0 */
+
+
+ /* 0 1 2 3 4 countCornerNb
+ *
+ * .#. ##. ##. ### ###
+ * .o. .o. .o. .o. .o.
+ * ... ... ..# ..# #.#
+ */
+ ,{ TRUE, TRUE, TRUE, FALSE, FALSE } /* countEdgeNb == 1 */
+
+
+
+ /* 0 1 2 3 4 countCornerNb
+ *
+ * .#. ##. ##. ### ###
+ * .o. .o. .o. .o. .o.
+ * .#. .#. .## .## ###
+ */
+ ,{ TRUE, FALSE, FALSE, FALSE, FALSE } /* countEdgeNb == 2 */
+
+
+ /* 0 1 2 3 4 countCornerNb
+ *
+ * .#. ##. ##. ### ###
+ * .o# .o# .o# .o# .o#
+ * .#. .#. .## .## ###
+ */
+ ,{ FALSE, FALSE, FALSE, FALSE, FALSE } /* countEdgeNb == 3 */
+
+
+
+ /* 0 1 2 3 4 countCornerNb
+ *
+ * .#. ##. ##. ### ###
+ * #o# #o# #o# #o# #o#
+ * .#. .#. .## .## ###
+ */
+ ,{ FALSE, FALSE, FALSE, FALSE, FALSE } /* countEdgeNb == 4 */
+ };
+
+ gboolean isIsolated;
+ gint countCornerNb;
+ gint countEdgeNb;
+
+ guchar nbLayermaskPixel[4];
+ guchar refAlphaValue;
+ gint xx;
+ gint yy;
+
+ refAlphaValue = p_map_alpha(cmaskParPtr, currentAlpha);
+ countEdgeNb = 0;
+
+ /* right neighbor */
+ xx = cmaskParPtr->x + 1;
+ if (xx < cmaskParPtr->width)
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , cmaskParPtr->y
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countEdgeNb++;
+ }
+ }
+
+ /* left neighbor */
+ xx = cmaskParPtr->x - 1;
+ if (xx >= 0)
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , cmaskParPtr->y
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countEdgeNb++;
+ }
+ }
+
+
+ /* lower neighbor */
+ yy = cmaskParPtr->y + 1;
+ if (yy < cmaskParPtr->height)
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , cmaskParPtr->x
+ , yy
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countEdgeNb++;
+ }
+ }
+
+ if(countEdgeNb > 2)
+ {
+ return (FALSE);
+ }
+
+ /* upper neighbor */
+ yy = cmaskParPtr->y - 1;
+ if (yy >= 0)
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , cmaskParPtr->x
+ , yy
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countEdgeNb++;
+ }
+ }
+
+ if(countEdgeNb > 2)
+ {
+ return (FALSE);
+ }
+
+ /* there are 2 or less neighbours
+ * check corner neighbours in that case
+ */
+
+ countCornerNb = 0;
+
+ /* right lower neighbor */
+ xx = cmaskParPtr->x + 1;
+ yy = cmaskParPtr->y + 1;
+ if ((xx < cmaskParPtr->width) && (yy < cmaskParPtr->height))
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , yy
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countCornerNb++;
+ }
+ }
+
+ /* left upper neighbor */
+ xx = cmaskParPtr->x - 1;
+ yy = cmaskParPtr->y - 1;
+ if ((xx >= 0) && (yy >= 0))
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , yy
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countCornerNb++;
+ }
+ }
+
+
+ /* left lower neighbor */
+ xx = cmaskParPtr->x - 1;
+ yy = cmaskParPtr->y + 1;
+ if ((xx >= 0) && (yy < cmaskParPtr->height))
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , yy
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countCornerNb++;
+ }
+ }
+
+
+ /* right upper neighbor */
+ xx = cmaskParPtr->x + 1;
+ yy = cmaskParPtr->y - 1;
+ if ((xx < cmaskParPtr->width) && (yy >= 0))
+ {
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , xx
+ , yy
+ , &nbLayermaskPixel[0]
+ );
+
+ if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+ {
+ countCornerNb++;
+ }
+ }
+
+ isIsolated = nbCountLookup[countEdgeNb][countCornerNb];
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("countEdgeNb:%d countCornerNb:%d isIsolated:%d\n"
+ ,(int)countEdgeNb
+ ,(int)countCornerNb
+ ,(int)isIsolated
+ );
+ }
+
+ return (isIsolated);
+
+} /* end p_is_isolated_pixel */
+
+
+
+/* --------------------------------------------
+ * p_isle_check_and_mark_nb_pixel
+ * --------------------------------------------
+ * checks if the specified neighbor pixel at coords nx, ny
+ * has same opacity value as refOpacity (e.g the same opacity as the seed pixel).
+ * If this is the case increment pixel count and
+ * and add nx/ny to the point list (to initiate check of further neighbour pixels)
+ *
+ * The isleAreaTable is updated to mark already processed pixels
+ * (either as WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA or as WORK_PART_OF_CURRENT_AREA)
+ *
+ */
+static void
+p_isle_check_and_mark_nb_pixel(GapColorMaskParams *cmaskParPtr, gint nx, gint ny, guchar refOpacity)
+{
+ guchar pixel[4];
+ gint32 ax;
+ gint32 ay;
+
+
+ ax = nx + cmaskParPtr->seedOffsetX;
+ ay = ny + cmaskParPtr->seedOffsetY;
+
+ if (cmaskParPtr->isleAreaTable[ax][ay] != WORK_UNCHECKED)
+ {
+ /* this neighbor pixel is already processed (nothing to be done in that case) */
+ return;
+ }
+
+
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+ , nx
+ , ny
+ , &pixel[0]
+ );
+
+ if (pixel[0] != refOpacity)
+ {
+ /* currently processed layermask pixel has other opacity than current seed
+ * and is not member of the checked isle area
+ */
+ cmaskParPtr->isleAreaTable[ax][ay] = WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA;
+ return;
+ }
+
+ /* mark this neighbor pixel */
+ cmaskParPtr->isleAreaTable[ax][ay] = WORK_PART_OF_CURRENT_AREA;
+ cmaskParPtr->pixelCount++;
+ p_add_point(cmaskParPtr, nx, ny);
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("ClipArea Check MATCH at nx:%d ny:%d pixelCount:%d refOpacity:%d opacity:%d\n"
+ ,(int)nx
+ ,(int)ny
+ ,(int)cmaskParPtr->pixelCount
+ ,(int)refOpacity
+ ,(int)pixel[0]
+ );
+ }
+
+} /* p_isle_check_and_mark_nb_pixel */
+
+
+
+/* --------------------------------------------
+ * p_is_pixel_a_small_isolated_area
+ * --------------------------------------------
+ *
+ * find pixels of same opacity as the specified refOpacity of the current processed seed pixel (cmaskParPtr->x / y)
+ * and count neighbor pixel (connected at edges) with same opacity value.
+ * Note that the search is limited to a small clipping area that fits into isleAreaTable size
+ * This is done for performance reasons.
+ *
+ * return TRUE in case the seed pixel is isolated
+ * return FALSE in case seed pixel is part of an area with same opacity
+ * that is larger than the specified isleAreaPixelLimit,
+ * OR in case the area touches the border of the checked clipping area rectangle.
+ *
+ *
+ */
+static gboolean
+p_is_pixel_a_small_isolated_area(GapColorMaskParams *cmaskParPtr, guchar refOpacity)
+{
+ gint32 ix;
+ gint32 iy;
+ gint32 ax;
+ gint32 ay;
+
+ gint clipSelX1;
+ gint clipSelX2;
+ gint clipSelY1;
+ gint clipSelY2;
+ gint maxClipRadius;
+ gint usedArraySize;
+ gboolean isBorderReached;
+
+
+ maxClipRadius = MIN(ISLE_AREA_MAX_RADIUS, cmaskParPtr->isleRadius);
+ usedArraySize = (1 + (2 * maxClipRadius));
+
+ cmaskParPtr->pointCount = 0;
+
+ cmaskParPtr->seedOffsetX = maxClipRadius - cmaskParPtr->x;
+ cmaskParPtr->seedOffsetY = maxClipRadius - cmaskParPtr->y;
+
+
+ /* clipping area boundaries to limit area checks
+ * for a small rectangular area +- maxClipRadius around current pixel
+ */
+ clipSelX1 = MAX(cmaskParPtr->sel_x1, (cmaskParPtr->x - maxClipRadius));
+ clipSelY1 = MAX(cmaskParPtr->sel_y1, (cmaskParPtr->y - maxClipRadius));
+
+ clipSelX2 = MIN(cmaskParPtr->sel_x2, (cmaskParPtr->x + maxClipRadius));
+ clipSelY2 = MIN(cmaskParPtr->sel_y2, (cmaskParPtr->y + maxClipRadius));
+
+
+ /* reset area context, clear used part of the isleAreaTable */
+ for(ax=0; ax < usedArraySize; ax++)
+ {
+ for(ay=0; ay < usedArraySize; ay++)
+ {
+ cmaskParPtr->isleAreaTable[ax][ay] = WORK_UNCHECKED;
+ }
+ }
+
+ cmaskParPtr->pixelCount = 1;
+ cmaskParPtr->pointList = NULL;
+
+ /* mark seed pixel */
+ ax = cmaskParPtr->x + cmaskParPtr->seedOffsetX;
+ ay = cmaskParPtr->y + cmaskParPtr->seedOffsetY;
+ cmaskParPtr->isleAreaTable[ax][ay] = WORK_PART_OF_CURRENT_AREA +1;
+
+ ix = cmaskParPtr->x;
+ iy = cmaskParPtr->y;
+
+ isBorderReached = FALSE;
+
+ while (TRUE)
+ {
+ /* check of the neighbours */
+ if (ix > clipSelX1)
+ {
+ p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix-1, iy, refOpacity);
+
+ if (ix < clipSelX2)
+ {
+ p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix+1, iy, refOpacity);
+
+ if (iy > clipSelY1)
+ {
+ p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix, iy-1, refOpacity);
+
+ if (iy < clipSelY2)
+ {
+ p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix, iy+1, refOpacity);
+ }
+ else { isBorderReached = TRUE; }
+
+ }
+ else { isBorderReached = TRUE; }
+
+ }
+ else { isBorderReached = TRUE; }
+ }
+ else { isBorderReached = TRUE; }
+
+
+
+
+
+ if((cmaskParPtr->pixelCount > cmaskParPtr->isleAreaPixelLimit)
+ || (isBorderReached == TRUE))
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("NOT_ISOLATED_AREA x:%d y:%d ix:%d iy:%d pixelCount:%d isleAreaPixelLimit:%d isBorderReached:%d\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(int)ix
+ ,(int)iy
+ ,(int)cmaskParPtr->pixelCount
+ ,(int)cmaskParPtr->isleAreaPixelLimit
+ ,(int)isBorderReached
+ );
+ }
+ /* break because at least one of the non-isolation conditions was detected */
+ p_free_point_list(cmaskParPtr);
+ return (FALSE);
+ }
+
+ if (cmaskParPtr->pointList == NULL)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("ISOLATED_AREA x:%d y:%d ix:%d iy:%d pixelCount:%d isleAreaPixelLimit:%d isBorderReached:%d\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(int)ix
+ ,(int)iy
+ ,(int)cmaskParPtr->pixelCount
+ ,(int)cmaskParPtr->isleAreaPixelLimit
+ ,(int)isBorderReached
+ );
+ }
+ /* area completely checked and pixelCount is bleow isleAreaPixelLimit */
+ return (TRUE);
+ }
+
+ /* continue checking neigbor pixels of same opacity in the point list */
+ ix = cmaskParPtr->pointList->x;
+ iy = cmaskParPtr->pointList->y;
+ p_remove_first_point(cmaskParPtr);
+
+
+
+ }
+
+ return (FALSE); /* never reached code */
+
+
+} /* end p_is_pixel_a_small_isolated_area */
+
+
+
+
+/* ---------------------------------
+ * p_remove_isolated_pixels
+ * ---------------------------------
+ * calculate new alpha value in case the current processed pixel is an
+ * isolated pixel
+ * return the (possibly changed) alpha value.
+ *
+ */
+static inline guchar
+p_remove_isolated_pixels(guchar currentAlpha, GapColorMaskParams *cmaskParPtr)
+{
+ guchar invertedAlpha;
+ gboolean isIsolated;
+
+ isIsolated = p_is_isolated_pixel(cmaskParPtr, currentAlpha);
+ if((!isIsolated) && (cmaskParPtr->isleAreaPixelLimit > 1))
+ {
+ isIsolated = p_is_pixel_a_small_isolated_area(cmaskParPtr, currentAlpha);
+ }
+
+ /* check distance >= isleRadius
+ * return currentAlpha unchanged
+ * in case extension of area with same value is large enough in any of the 4 directions
+ */
+ if(!isIsolated)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("p_remove_isolated_pixels NOT isolated currentAlpha:%d\n"
+ ,(int)currentAlpha
+ );
+ }
+ return (currentAlpha);
+ }
+
+
+ /* the current pixel is an isolated pixel
+ * therefore invert its opacity to join with neighbor pixel environment
+ */
+ if (currentAlpha == cmaskParPtr->lowerAlpha255)
+ {
+ invertedAlpha = cmaskParPtr->upperAlpha255;
+ }
+ else
+ {
+ invertedAlpha = cmaskParPtr->lowerAlpha255;
+ }
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("p_remove_isolated_pixels ISOLATED currentAlpha:%d inverted:%d\n"
+ ,(int)currentAlpha
+ ,(int)invertedAlpha
+ );
+ }
+
+ return (invertedAlpha);
+
+} /* end p_remove_isolated_pixels */
+
+
+
+/* ------------------------------------------
+ * p_remove_isolates_pixels_rgn_render_region
+ * ------------------------------------------
+ * remove isolated pixels (e.g. areas smaller than isleRadius)
+ * from the layermask
+ * Note that protected pixels (that are transparent in the colormask)
+ * are not affected.
+ */
+static void
+p_remove_isolates_pixels_rgn_render_region (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *lmskPR
+ ,GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* mask = maskPR->data; /* the colormask (RGB or RGBA) */
+ guchar* lmsk = lmskPR->data; /* the layermask of the destination drawable */
+
+
+ for (row = 0; row < lmskPR->h; row++)
+ {
+ guint col;
+ guint idxMask;
+ guint idxLmsk;
+
+ idxMask = 0;
+ idxLmsk = 0;
+ for(col = 0; col < lmskPR->w; col++)
+ {
+ gboolean isProtected;
+
+ isProtected = FALSE;
+ if (maskPR->bpp > 3)
+ {
+ if (mask[idxMask + 3] <= cmaskParPtr->triggerAlpha255)
+ {
+ isProtected = TRUE;
+ }
+ }
+
+ if (!isProtected)
+ {
+ cmaskParPtr->x = maskPR->x + col;
+ cmaskParPtr->y = maskPR->y + row;
+
+ //if(gap_debug)
+ {
+ cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+ }
+
+ /* re-calculate opacity respecting isolated pixels */
+ lmsk[idxLmsk] = p_remove_isolated_pixels(lmsk[idxLmsk], cmaskParPtr);
+ }
+
+ idxMask += maskPR->bpp;
+ idxLmsk += lmskPR->bpp;
+ }
+
+ mask += maskPR->rowstride;
+ lmsk += lmskPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, maskPR->w, "p_remove_isolates_pixels_rgn_render_region");
+ }
+ }
+
+} /* end p_remove_isolates_pixels_rgn_render_region */
+
+
+
+
+
+
+
+
+
+
+/* ============================ Stuff for average algorithm START ======= */
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for average algorithm START ------------ */
+/* -------------------------------------------------------------------- */
+
+
+/* ------------------------------------------
+ * p_set_dynamic_threshold_by_KeyColor
+ * ------------------------------------------
+ * in case enableKeyColorThreshold is true
+ * set colorThreshold and avgColorThreshold
+ * dynamic by colrdifference of current color versus KeyColor
+ */
+static inline void
+p_set_dynamic_threshold_by_KeyColor(guchar *pixel
+ , GapColorMaskParams *cmaskParPtr)
+{
+ GimpRGB bRgb;
+ GimpHSV bHsv;
+ gdouble colordiff;
+ gdouble factor;
+
+ if(!cmaskParPtr->enableKeyColorThreshold)
+ {
+ return; /* dynamic threshold for keycolor feature is disabled */
+ }
+
+ gimp_rgba_set_uchar (&bRgb, pixel[0], pixel[1], pixel[2], 255);
+ gimp_rgb_to_hsv(&bRgb, &bHsv);
+
+ colordiff = gap_colordiff_GimpHSV(&cmaskParPtr->keyColorHsv, &bHsv, cmaskParPtr->colorSensitivity, cmaskParPtr->debugCoordinate);
+
+ factor = CLAMP(colordiff * cmaskParPtr->keyColorSensitivity, 0.0, 1.0);
+ cmaskParPtr->colorThreshold = GAP_BASE_MIX_VALUE(factor
+ , cmaskParPtr->loKeyColorThreshold
+ , cmaskParPtr->loColorThreshold
+ );
+
+ cmaskParPtr->avgColorThreshold = cmaskParPtr->colorThreshold
+ + ((cmaskParPtr->hiColorThreshold - cmaskParPtr->colorThreshold) * cmaskParPtr->avgFactor);
+
+
+ if(cmaskParPtr->debugCoordinate)
+ {
+ printf("colordiff at x:%d y:%d (compared to Keycolor hue:%.3f sat:%.3f val:%.3f):%.4f factor:%.4f colorThreshold:%.4f avgColorThreshold:%.4f\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)cmaskParPtr->keyColorHsv.h
+ ,(float)cmaskParPtr->keyColorHsv.s
+ ,(float)cmaskParPtr->keyColorHsv.v
+ ,(float)colordiff
+ ,(float)factor
+ ,(float)cmaskParPtr->colorThreshold
+ ,(float)cmaskParPtr->avgColorThreshold
+ );
+ }
+
+} /* end p_set_dynamic_threshold_by_KeyColor */
+
+
+/* ---------------------------------
+ * p_colormask_avg_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_colormask_avg_rgn_render_region (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *destPR
+ ,const GimpPixelRgn *lmskPR
+ ,GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* mask = maskPR->data; /* the colormask */
+ guchar* dest = destPR->data; /* the destination drawable */
+ guchar* lmsk = lmskPR->data; /* the layermask of the destination drawable */
+
+
+ for (row = 0; row < destPR->h; row++)
+ {
+ guint col;
+ guint idxMask;
+ guint idxDest;
+ guint idxLmsk;
+
+ idxMask = 0;
+ idxDest = 0;
+ idxLmsk = 0;
+ for(col = 0; col < destPR->w; col++)
+ {
+ gint32 iTab;
+ gboolean isPixelProtected;
+
+ isPixelProtected = FALSE;
+ cmaskParPtr->x = destPR->x + col;
+ cmaskParPtr->y = destPR->y + row;
+
+
+ /* ------ start debug code block -----*/
+ //if(gap_debug)
+ {
+ cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+ }
+ /* ------ end debug code block -----*/
+
+ if (maskPR->bpp > 3)
+ {
+ /* colormask has an alpha channel */
+ if (mask[idxMask +3] <= cmaskParPtr->triggerAlpha255)
+ {
+ /* transparent mask (below or equal trigger value)
+ * switches off color masking for that pixel
+ * Note that an opaque layermask pixel
+ * preserves opacity as set via alpha channel unchanged when the layermask is applied.
+ * (no need to check color differences in that case)
+ */
+ isPixelProtected = TRUE;
+ }
+ }
+
+
+ if (isPixelProtected)
+ {
+ /* protected pixels are set full opaque in the layermask
+ * (e.g. they keep their original opacity when layermask is applied)
+ */
+ lmsk[idxLmsk] = 255;
+ if(cmaskParPtr->debugCoordinate)
+ {
+ printf("ALGO_AVG: x:%d y:%d pixel IS PROTECTED alpha:%d triggerAlpha255:%d\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(int)mask[idxMask +4]
+ ,(int)cmaskParPtr->triggerAlpha255
+ );
+ }
+ }
+ else
+ {
+ iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+ if(cmaskParPtr->debugCoordinate)
+ {
+ printf("ALGO_AVG: x:%d y:%d iTab:%d colordiff:%.5f \n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(int)iTab
+ ,(float)cmaskParPtr->colordiffTable[iTab]
+ );
+ }
+
+
+ if (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->hiColorThreshold)
+ {
+ /* NOMATCH pixel color differs significant from colormask pixelcolor
+ * set pixel to upper alpha (typically full opaque)
+ */
+ lmsk[idxLmsk] = cmaskParPtr->upperAlpha255;
+ }
+ else
+ {
+ p_set_dynamic_threshold_by_KeyColor(&dest[idxDest], cmaskParPtr);
+ if (cmaskParPtr->colordiffTable[iTab] < cmaskParPtr->colorThreshold)
+ {
+ /* MATCH pixel color equal or nearly equal to colormask pixelcolor */
+ lmsk[idxLmsk] = p_calc_opacity_for_matching_color(cmaskParPtr);
+ }
+ else
+ {
+ /* RANGE pixel color falls in the range of similar but not best matching colors
+ */
+ lmsk[idxLmsk] = p_calc_opacity_for_range_color(cmaskParPtr);
+ }
+ }
+ }
+
+
+
+ idxDest += destPR->bpp;
+ idxMask += maskPR->bpp;
+ idxLmsk += lmskPR->bpp;
+ }
+
+ mask += maskPR->rowstride;
+ dest += destPR->rowstride;
+ lmsk += lmskPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, maskPR->w, "p_colormask_avg_rgn_render_region");
+ }
+ }
+
+} /* end p_colormask_avg_rgn_render_region */
+
+
+/* -----------------------------------------
+ * p_check_matching_pixel_isolation
+ * -----------------------------------------
+ * check if matching pixel is embedded into non matching pixels
+ * and return TRUE in this case.
+ * if there are more matching or range pixels in one horizontal or vertical row,
+ * the pixel is considered as not isolated and FALSE is returned.
+ */
+static gboolean
+p_check_matching_pixel_isolation(GapColorMaskParams *cmaskParPtr)
+{
+ gdouble sumNbColorDiff;
+ gdouble countNbPixels;
+ gdouble matchWidth;
+ gdouble matchHeight;
+
+ /* check matching width */
+ p_check_range_neighbour_sum(cmaskParPtr
+ , -1 /* dx */
+ , 0 /* dy */
+ , cmaskParPtr->checkMatchRadius
+ , &sumNbColorDiff
+ , &countNbPixels
+ );
+ if (countNbPixels >= cmaskParPtr->checkMatchRadius)
+ {
+ return (FALSE);
+ }
+
+ matchWidth = countNbPixels;
+
+ p_check_range_neighbour_sum(cmaskParPtr
+ , 1 /* dx */
+ , 0 /* dy */
+ , cmaskParPtr->checkMatchRadius
+ , &sumNbColorDiff
+ , &countNbPixels
+ );
+ matchWidth += (countNbPixels -1);
+ if (matchWidth >= cmaskParPtr->checkMatchRadius)
+ {
+ return (FALSE);
+ }
+
+ /* check matching height */
+ p_check_range_neighbour_sum(cmaskParPtr
+ , 0 /* dx */
+ , -1 /* dy */
+ , cmaskParPtr->checkMatchRadius
+ , &sumNbColorDiff
+ , &countNbPixels
+ );
+ if (countNbPixels >= cmaskParPtr->checkMatchRadius)
+ {
+ return (FALSE);
+ }
+
+ matchHeight = countNbPixels;
+
+ p_check_range_neighbour_sum(cmaskParPtr
+ , 0 /* dx */
+ , 1 /* dy */
+ , cmaskParPtr->checkMatchRadius
+ , &sumNbColorDiff
+ , &countNbPixels
+ );
+ matchHeight += (countNbPixels -1);
+ if (matchHeight >= cmaskParPtr->checkMatchRadius)
+ {
+ return (FALSE);
+ }
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("matching pixel is isolated matchWidth:%d matchHeight:%d radius:%d\n"
+ , (int)matchWidth
+ , (int)matchHeight
+ , (int)cmaskParPtr->checkMatchRadius
+ );
+ }
+
+ return (TRUE);
+} /* end p_check_matching_pixel_isolation */
+
+
+/* ---------------------------------
+ * p_calc_opacity_for_matching_color
+ * ---------------------------------
+ * in case all or nearly all pixels within radius do NOT MATCH set
+ * current pixel opaque
+ */
+static guchar
+p_calc_opacity_for_matching_color(GapColorMaskParams *cmaskParPtr)
+{
+ if (p_check_matching_pixel_isolation(cmaskParPtr) == TRUE)
+ {
+ return (cmaskParPtr->upperAlpha255);
+ }
+
+ return (cmaskParPtr->lowerAlpha255);
+} /* end p_calc_opacity_for_matching_color */
+
+
+/* ---------------------------------
+ * p_calc_opacity_for_range_color
+ * ---------------------------------
+ * range pixels are set to matching lower alpha (transparent) in case where
+ * the configured average algorithm check returns TRUE (considered as matching)
+ * otherwise range pixels are set to upper alpha (opaque)
+ */
+static guchar
+p_calc_opacity_for_range_color(GapColorMaskParams *cmaskParPtr)
+{
+ if(p_check_average_colordiff_incl_neighbours(cmaskParPtr) == TRUE)
+ {
+ /* average matches good */
+ return (cmaskParPtr->lowerAlpha255);
+ }
+
+
+
+ return (cmaskParPtr->upperAlpha255);
+
+} /* end p_calc_opacity_for_range_color */
+
+
+/* ---------------------------------
+ * p_check_range_neighbour_sum
+ * ---------------------------------
+ */
+static void
+p_check_range_neighbour_sum(GapColorMaskParams *cmaskParPtr, gint dx, gint dy, gint radius, gdouble *sumNbColorDiff, gdouble *countNbPixels)
+{
+ gint iTab;
+ gint ii;
+
+ iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+ *sumNbColorDiff = cmaskParPtr->colordiffTable[iTab];
+ *countNbPixels = 1.0;
+
+ for(ii=1; ii < radius; ii++)
+ {
+ gint xx;
+ gint yy;
+
+ xx = cmaskParPtr->x + (ii * dx);
+ yy = cmaskParPtr->y + (ii * dy);
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("CHK ii:%d radius:%d\n"
+ , (int)ii
+ , (int)radius
+ );
+ }
+
+
+ /* clipping check */
+ if ((xx < 0) || (yy < 0) || (xx >= cmaskParPtr->width) || (yy >= cmaskParPtr->height))
+ {
+ return;
+ }
+
+ iTab = p_getColordiffTableIndex(cmaskParPtr, xx, yy);
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("CHK xx:%d yy:%d countNbPixels:%.1f cmaskParPtr:%d hiColorThreshold:%.4f colordiffTab:%.4f\n"
+ , (int)xx
+ , (int)yy
+ , *countNbPixels
+ , (int)cmaskParPtr
+ , (float)cmaskParPtr->hiColorThreshold
+ , (float)cmaskParPtr->colordiffTable[iTab]
+ );
+ }
+
+ if (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->hiColorThreshold)
+ {
+ /* stop checking because we have reached a non matching pixel */
+ return;
+ }
+
+ *sumNbColorDiff += cmaskParPtr->colordiffTable[iTab];
+ *countNbPixels += 1.0;
+ }
+
+} /* end p_check_range_neighbour_sum */
+
+
+
+
+/* --------------------------------------------
+ * p_countPerColordiff
+ * --------------------------------------------
+ * count and update colordiff statistics for matching, range and not matching pixels
+ * within the checked area. (typical used to analyze a small area of same or similar
+ * color within a clipping rectangle)
+ */
+static void
+p_countPerColordiff(GapColorMaskParams *cmaskParPtr, gdouble colordiff, gint mi)
+{
+ if (colordiff < cmaskParPtr->avgColorThreshold) ////## ?? loColorThreshold)
+ {
+ cmaskParPtr->sumMatch[mi] += colordiff;
+ cmaskParPtr->countMatch[mi]++;
+ return;
+ }
+
+ if (colordiff >= cmaskParPtr->hiColorThreshold)
+ {
+ cmaskParPtr->sumNoMatch[mi] += colordiff;
+ cmaskParPtr->countNoMatch[mi]++;
+ return;
+ }
+
+ cmaskParPtr->sumRange[mi] += colordiff;
+ cmaskParPtr->countRange[mi]++;
+
+} /* end p_countPerColordiff */
+
+
+
+
+
+/* --------------------------------------------
+ * p_avg_check_and_mark_nb_pixel
+ * --------------------------------------------
+ * checks if the specified neighbor pixel at coords nx, ny
+ * has similar color as the seed pixel.
+ * If this is the case increment pixel count and summary colordiffrenece
+ * and add nx/ny to the point list (to initiate check of further neighbour pixels)
+ *
+ * The clipAreaTable is updated to mark already processed pixels
+ * (either as WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA or as WORK_PART_OF_CURRENT_AREA)
+ */
+static void
+p_avg_check_and_mark_nb_pixel(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint nx, gint ny, gint mi)
+{
+ guchar pixel[4];
+ gdouble colorDiff;
+
+ gint32 ax;
+ gint32 ay;
+ gint32 iTab;
+
+ ax = nx + cmaskParPtr->seedOffsetX;
+ ay = ny + cmaskParPtr->seedOffsetY;
+
+ if (cmaskParPtr->clipAreaTable[ax][ay][mi] != WORK_UNCHECKED)
+ {
+ /* this neighbor pixel is already processed (nothing to be done in that case) */
+ return;
+ }
+
+ gimp_pixel_fetcher_get_pixel (pft, nx, ny, &pixel[0]);
+
+ /* check if color matches with currently processed area */
+ colorDiff = p_colordiff_guchar_cmask_RGBorHSV(&cmaskParPtr->seedHsv
+ , &cmaskParPtr->seedPixel[0]
+ , &pixel[0]
+ , cmaskParPtr
+ );
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("ClipArea neighbor Check ax:%d ay:%d nx:%d ny:%d thresholdColorArea:%.4f colorDiff:%.4f\n"
+ ,(int)ax
+ ,(int)ay
+ ,(int)nx
+ ,(int)ny
+ ,(float)cmaskParPtr->thresholdColorArea
+ ,(float)colorDiff
+ );
+ }
+ if (colorDiff > cmaskParPtr->thresholdColorArea)
+ {
+ /* currently processed pixel has other color than current seed
+ * and is not member of the same color-area
+ */
+ cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA;
+ return;
+ }
+
+ /* mark this neigbor pixel */
+ cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_PART_OF_CURRENT_AREA;
+
+ iTab = p_getColordiffTableIndex(cmaskParPtr, nx, ny);
+
+ cmaskParPtr->pixelCount++;
+ cmaskParPtr->sumColorDiff += cmaskParPtr->colordiffTable[iTab];
+
+ p_countPerColordiff(cmaskParPtr, cmaskParPtr->colordiffTable[iTab], mi); /// ####
+
+ p_add_point(cmaskParPtr, nx, ny);
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("ClipArea Check MATCH at nx:%d ny:%d sumColorDiff:%.4f count:%d avgDiff:%.4f iTab:%d\n"
+ ,(int)nx
+ ,(int)ny
+ ,(float)cmaskParPtr->sumColorDiff
+ ,(int)cmaskParPtr->pixelCount
+ ,(float)cmaskParPtr->sumColorDiff / cmaskParPtr->pixelCount
+ ,(int)iTab
+ );
+ }
+
+} /* p_avg_check_and_mark_nb_pixel */
+
+
+/* --------------------------------------------
+ * p_find_pixel_area_of_similar_color
+ * --------------------------------------------
+ *
+ * find pixels of same or similar color to the current processed seed pixel (cmaskParPtr->x / y)
+ * and count pixels that are part of similar color area and calculate summary colordiff (dest layer versus colormask)
+ * for all the pixels in the similar color area.
+ * Note that the search is limited to a small clipping area that fits into clipAreaTable size
+ * This is done both for performance reasons, and to limit results to local area near the current pixel.
+ *
+ */
+static void
+p_find_pixel_area_of_similar_color(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint mi)
+{
+ gint32 ix;
+ gint32 iy;
+ gint32 ax;
+ gint32 ay;
+ gint32 iTab;
+ GimpRGB rgb;
+
+ gint clipSelX1;
+ gint clipSelX2;
+ gint clipSelY1;
+ gint clipSelY2;
+
+ cmaskParPtr->pointCount = 0;
+ cmaskParPtr->seed_ix = cmaskParPtr->x;
+ cmaskParPtr->seed_iy = cmaskParPtr->y;
+
+ cmaskParPtr->seedOffsetX = CLIP_AREA_MAX_RADIUS - cmaskParPtr->x;
+ cmaskParPtr->seedOffsetY = CLIP_AREA_MAX_RADIUS - cmaskParPtr->y;
+
+
+ /* clipping area boundaries to limit area checks
+ * for a small rectangular area +- CLIP_AREA_MAX_RADIUS around current pixel
+ */
+ clipSelX1 = MAX(cmaskParPtr->sel_x1, (cmaskParPtr->x - CLIP_AREA_MAX_RADIUS));
+ clipSelY1 = MAX(cmaskParPtr->sel_y1, (cmaskParPtr->y - CLIP_AREA_MAX_RADIUS));
+
+ clipSelX2 = MIN(cmaskParPtr->sel_x2, (cmaskParPtr->x + CLIP_AREA_MAX_RADIUS));
+ clipSelY2 = MIN(cmaskParPtr->sel_y2, (cmaskParPtr->y + CLIP_AREA_MAX_RADIUS));
+
+
+ /* reset area context, clear clipAreaTable */
+ for(ax=0; ax < CLIP_AREA_MAX_SIZE; ax++)
+ {
+ for(ay=0; ay < CLIP_AREA_MAX_SIZE; ay++)
+ {
+ cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_UNCHECKED;
+ }
+ }
+
+ /* fetch the seed pixel color */
+ gimp_pixel_fetcher_get_pixel (pft, cmaskParPtr->x, cmaskParPtr->y, &cmaskParPtr->seedPixel[0]);
+ gimp_rgba_set_uchar (&rgb
+ , cmaskParPtr->seedPixel[0]
+ , cmaskParPtr->seedPixel[1]
+ , cmaskParPtr->seedPixel[2]
+ , 255
+ );
+ gimp_rgb_to_hsv(&rgb, &cmaskParPtr->seedHsv);
+
+
+ iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+ cmaskParPtr->sumColorDiff = cmaskParPtr->colordiffTable[iTab];
+ cmaskParPtr->pixelCount = 1;
+
+ p_countPerColordiff(cmaskParPtr, cmaskParPtr->colordiffTable[iTab], mi); /// ####
+
+
+
+
+
+ cmaskParPtr->pointList = NULL;
+
+ /* mark seed pixel */
+ ax = cmaskParPtr->x + cmaskParPtr->seedOffsetX;
+ ay = cmaskParPtr->y + cmaskParPtr->seedOffsetY;
+ cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_PART_OF_CURRENT_AREA +1;
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("Seed at x:%d y:%d (ax:%d ay:%d seedOffsetX:%d Y:%d) sel_y1:%d clipSelX1:%d X2:%d Y1:%d Y2:%d initial sumColorDiff:%.4f iTab:%d RGB: %03d %03d %03d \n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(int)ax
+ ,(int)ay
+ ,(int)cmaskParPtr->seedOffsetX
+ ,(int)cmaskParPtr->seedOffsetY
+ ,(int)cmaskParPtr->sel_y1
+ ,(int)clipSelX1
+ ,(int)clipSelX2
+ ,(int)clipSelY1
+ ,(int)clipSelY2
+ ,(float)cmaskParPtr->sumColorDiff
+ ,(int)iTab
+ ,(int)cmaskParPtr->seedPixel[0]
+ ,(int)cmaskParPtr->seedPixel[1]
+ ,(int)cmaskParPtr->seedPixel[2]
+ );
+ }
+
+
+ ix = cmaskParPtr->x;
+ iy = cmaskParPtr->y;
+
+ while (TRUE)
+ {
+ /* check of the neighbours */
+ if (ix > clipSelX1)
+ {
+ p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix-1, iy, mi);
+ }
+
+ if (ix < clipSelX2)
+ {
+ p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix+1, iy, mi);
+ }
+
+ if (iy > clipSelY1)
+ {
+ p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix, iy-1, mi);
+ }
+
+ if (iy < clipSelY2)
+ {
+ p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix, iy+1, mi);
+ }
+
+
+ if (cmaskParPtr->pointList == NULL)
+ {
+ /* area completely done (all connected neigbour pixels of the seed are processed */
+ break;
+ }
+
+ /* continue checking neigbor pixels of similar color in the point list */
+ ix = cmaskParPtr->pointList->x;
+ iy = cmaskParPtr->pointList->y;
+ p_remove_first_point(cmaskParPtr);
+
+
+
+ }
+
+
+
+} /* end p_find_pixel_area_of_similar_color */
+
+
+
+/* ------------------------------------
+ * p_calculate_clip_area_average_values
+ * ------------------------------------
+ *
+ */
+static void
+p_calculate_clip_area_average_values(GapColorMaskParams *cmaskParPtr)
+{
+ cmaskParPtr->sumMatch[MI_IMAGE] = 0.0;
+ cmaskParPtr->sumRange[MI_IMAGE] = 0.0;
+ cmaskParPtr->sumNoMatch[MI_IMAGE] = 0.0;
+ cmaskParPtr->countMatch[MI_IMAGE] = 0.0;
+ cmaskParPtr->countRange[MI_IMAGE] = 0.0;
+ cmaskParPtr->countNoMatch[MI_IMAGE] = 0.0;
+
+
+ p_find_pixel_area_of_similar_color(cmaskParPtr, cmaskParPtr->pftDest, MI_IMAGE);
+
+ cmaskParPtr->imgAreaSize = cmaskParPtr->pixelCount;
+ cmaskParPtr->avgColordiffImgArea = cmaskParPtr->sumColorDiff / ((gdouble)cmaskParPtr->pixelCount);
+
+
+ /* debug code to print the clipAreaTable and results */
+ if (cmaskParPtr->debugCoordinate)
+ {
+ gint mi;
+ gint ax;
+ gint ay;
+
+ for(mi=0; mi < 1; mi++)
+ {
+ if(mi==0)
+ {
+ printf("Image clipAreaTable\n");
+ }
+ if(mi==1)
+ {
+ printf("Mask clipAreaTable\n");
+ }
+ for(ay=0; ay < CLIP_AREA_MAX_SIZE; ay++)
+ {
+ printf(" clipAreaTable_%d[%02d]: ", (int)mi, (int)ay);
+ for(ax=0; ax < CLIP_AREA_MAX_SIZE; ax++)
+ {
+ printf(" %03d", (int)cmaskParPtr->clipAreaTable[ax][ay][mi]);
+ }
+ printf("\n");
+ }
+ }
+
+ printf("ClipArea RESULTS x:%d y:%d : avgColordiffImgArea:%.4f imgAreaSize:%d \n"
+ " avgColorThreshold:%.4f \n"
+ " countMatch:%.0f countRange:%.0f countNoMatch:%.0f\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)cmaskParPtr->avgColordiffImgArea
+ ,(int)cmaskParPtr->imgAreaSize
+ ,(float)cmaskParPtr->avgColorThreshold
+ ,(float)cmaskParPtr->countMatch[MI_IMAGE]
+ ,(float)cmaskParPtr->countRange[MI_IMAGE]
+ ,(float)cmaskParPtr->countNoMatch[MI_IMAGE]
+ );
+
+ if(cmaskParPtr->countMatch[MI_IMAGE] > 0.0)
+ {
+ printf(" avgMatch: %.5f\n"
+ , (float)(cmaskParPtr->sumMatch[MI_IMAGE] / cmaskParPtr->countMatch[MI_IMAGE])
+ );
+ }
+ if(cmaskParPtr->countRange[MI_IMAGE] > 0.0)
+ {
+ printf(" avgRange: %.5f\n"
+ , (float)(cmaskParPtr->sumRange[MI_IMAGE] / cmaskParPtr->countRange[MI_IMAGE])
+ );
+ }
+ if(cmaskParPtr->countNoMatch[MI_IMAGE] > 0.0)
+ {
+ printf(" avgNoMatch: %.5f\n"
+ , (float)(cmaskParPtr->sumNoMatch[MI_IMAGE] / cmaskParPtr->countNoMatch[MI_IMAGE])
+ );
+ }
+
+ }
+
+} /* end p_calculate_clip_area_average_values */
+
+
+
+
+/* -----------------------------------------
+ * p_check_average_colordiff_incl_neighbours
+ * -----------------------------------------
+ * decide if color is matching by checking neighbor pixels
+ * using the configured type of average algorithm
+ *
+ * GAP_COLORMASK_ALGO_AVG_CHANGE_1
+ * check for significant brightness and colorchanges
+ * in 4 directions, starting at current pixel up to a distance of
+ * the configured significantRadius.
+ *
+ * (MATCHTYPE_MATCHING)
+ * in case the check finds 2 good matching pixels (image layer versus colormask)
+ * and there was no significant brightness or colorchange
+ * (checked image pixel versus previous image pixel in processed direction)
+ * then consider the starting pixel as good matching.
+ *
+ * (MATCHTYPE_NOT_MATCHING)
+ * in case the check finds 2 NOT-matching pixels (image layer versus colormask)
+ * and there was no significant brightness or colorchange
+ * (checked image pixel versus previous image pixel in processed direction)
+ * then consider the starting pixel as not matching.
+ *
+ * (MATCHTYPE_UNDEFINED)
+ * this undefined result occurs in case the check in the current direction
+ * o) has stopped at the configured significantRadius or image borders
+ * o) has found a significant brightness or colorchange
+ * continue with GAP_COLORMASK_ALGO_AVG_SMART in case
+ * MATCHTYPE_UNDEFINED is the result in all 4 directions.
+ *
+ *
+ * GAP_COLORMASK_ALGO_AVG_CHANGE_2
+ * similar to GAP_COLORMASK_ALGO_AVG_CHANGE_1
+ * but use results of all 4 directions
+ * (while variant 1 stops check at the first direction
+ * that delivers a result != MATCHTYPE_UNDEFINED)
+ *
+ * GAP_COLORMASK_ALGO_AVG_AREA:
+ * check if the pixel is part of an area of similar colors
+ * within the the processed image layer.
+ * in case the pixel is part of such an area then use the
+ * most significant type of colordiff (matching, NotMatching)
+ * within the area.
+ * In case colordiff type 'Range' is most significant
+ * (or number of pixels with colordiff type matching and NotMatching are equal)
+ * use the average colordiff of the area(s) or to decide.
+ *
+ * GAP_COLORMASK_ALGO_AVG_SMART:
+ * check average colordiff of neighbor pixels
+ * in upper, lower, left and right direction.
+ * in case the average colordiff is below the threshold
+ * return TRUE (e.g. the pixel is considered as matching good enough
+ * with the colormask)
+ * Otherwise return FALSE.
+ * This algorithm is also used as fallback strategy
+ * in case the other algortihms could not deliver a valid result.
+ */
+static gboolean
+p_check_average_colordiff_incl_neighbours(GapColorMaskParams *cmaskParPtr)
+{
+ gdouble sumNbColorDiff;
+ gdouble countNbPixels;
+ gdouble avgColordiff[4];
+
+ gint32 iTab;
+
+ if ((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_1)
+ || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_2))
+ {
+ static gint dxTab[4] = { 1, -1, 0, 0 };
+ static gint dyTab[4] = { 0, 0, 1, -1 };
+ gint direction;
+ gint matchCount;
+ gint notMatchCount;
+
+
+ if (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_1)
+ {
+ /* simple varaiant based on the first defined result */
+ for (direction=0; direction < 4; direction++)
+ {
+ guchar rc;
+
+ rc = p_check_significant_changes_in_one_direction(cmaskParPtr
+ , dxTab[direction], dyTab[direction]);
+
+ if(rc == MATCHTYPE_MATCHING)
+ {
+ return (TRUE); /* good matching */
+ }
+
+ if(rc == MATCHTYPE_NOT_MATCHING)
+ {
+ return (FALSE); /* NOT matching */
+ }
+ }
+ }
+
+ /* full varaiant based on all defined results */
+
+ matchCount = 0;
+ notMatchCount = 0;
+
+
+ for (direction=0; direction < 4; direction++)
+ {
+ guchar rc;
+
+ rc = p_check_significant_changes_in_one_direction(cmaskParPtr
+ , dxTab[direction], dyTab[direction]);
+
+ if(rc == MATCHTYPE_MATCHING)
+ {
+ matchCount++;
+ }
+ else if(rc == MATCHTYPE_NOT_MATCHING)
+ {
+ notMatchCount++;
+ }
+ }
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("SIG2 result: x:%d y:%d matchCount:%d notMatchCount:%d\n"
+ , (int)cmaskParPtr->x
+ , (int)cmaskParPtr->y
+ , matchCount
+ , notMatchCount
+ );
+ }
+
+ if(matchCount > notMatchCount)
+ {
+ return (TRUE); /* good matching */
+ }
+ else if (notMatchCount > 0)
+ {
+ return (FALSE); /* NOT matching */
+ }
+
+ /* pixel is not part of an area of similar colors (or area too small)
+ * continue with GAP_COLORMASK_ALGO_AVG_SMART algorithm
+ */
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("continue with standard AVG_SMART algorithm: ");
+ }
+ }
+
+
+
+ //iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+ //if ((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA)
+ //&& (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->avgColorThreshold))
+
+ if (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA)
+ {
+ /* handle GAP_COLORMASK_ALGO_AVG_AREA algorithm */
+
+ p_calculate_clip_area_average_values(cmaskParPtr);
+
+
+ if (cmaskParPtr->imgAreaSize >= cmaskParPtr->areaMinimumPixels)
+ {
+ if((cmaskParPtr->countMatch[MI_IMAGE] > cmaskParPtr->countRange[MI_IMAGE])
+ && (cmaskParPtr->countMatch[MI_IMAGE] > cmaskParPtr->countNoMatch[MI_IMAGE]))
+ {
+ /* most pixels of the similar colored area are good matching */
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("MATCHING is most significant\n");
+ }
+ return (TRUE); /* good matching */
+ }
+
+ if((cmaskParPtr->countNoMatch[MI_IMAGE] > cmaskParPtr->countRange[MI_IMAGE])
+ && (cmaskParPtr->countNoMatch[MI_IMAGE] > cmaskParPtr->countMatch[MI_IMAGE]))
+ {
+ /* most pixels of the similar colored area are NOT matching */
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("NOT_MATCHING is most significant\n");
+ }
+ return (FALSE); /* not matching */
+ }
+
+ /* most pixels of the similar colored area within range or
+ * there are exactly the same number of good and non matching pixels within this area.
+ * therfore next check the overall average colordiff of the area.
+ */
+ if (cmaskParPtr->avgColordiffImgArea < cmaskParPtr->avgColorThreshold)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("MATCHING due to overall avgColordiffImgArea:%.5f avgColorThreshold:%.5f\n"
+ , (float)cmaskParPtr->avgColordiffImgArea
+ , (float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ return (TRUE); /* good matching */
+ }
+ if (cmaskParPtr->avgColordiffImgArea >= cmaskParPtr->hiColorThreshold)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("NOT_MATCHING due to overall avgColordiffImgArea:%.5f avgColorThreshold:%.5f\n"
+ , (float)cmaskParPtr->avgColordiffImgArea
+ , (float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ return (FALSE); /* not matching */
+ }
+
+ if (cmaskParPtr->countRange[MI_IMAGE] > 1)
+ {
+ gdouble avgRangeColordiff;
+ gdouble avgMixColordiff;
+
+ /* check average colordiff limited to just those similar color area pixels
+ * that match within range
+ */
+ avgRangeColordiff = cmaskParPtr->sumRange[MI_IMAGE] / cmaskParPtr->countRange[MI_IMAGE];
+
+ if (avgRangeColordiff < cmaskParPtr->avgColorThreshold)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("MATCHING due to range avgRangeColordiff:%.5f avgColorThreshold:%.5f\n"
+ , (float)avgRangeColordiff
+ , (float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ return (TRUE); /* good matching */
+ }
+ if (avgRangeColordiff >= cmaskParPtr->hiColorThreshold)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("NOT_MATCHING due to range avgRangeColordiff:%.5f avgColorThreshold:%.5f\n"
+ , (float)avgRangeColordiff
+ , (float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ return (FALSE); /* not matching */
+ }
+
+ if(cmaskParPtr->countMatch[MI_IMAGE] >= cmaskParPtr->countNoMatch[MI_IMAGE])
+ {
+ avgMixColordiff = (cmaskParPtr->sumRange[MI_IMAGE] + cmaskParPtr->sumMatch[MI_IMAGE])
+ / (cmaskParPtr->countRange[MI_IMAGE] + cmaskParPtr->countMatch[MI_IMAGE]);
+ if (avgMixColordiff < cmaskParPtr->avgColorThreshold)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("MATCHING due to mix range+match avgMixColordiff:%.5f avgColorThreshold:%.5f\n"
+ , (float)avgMixColordiff
+ , (float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ return (TRUE); /* good matching */
+ }
+ }
+
+ if(cmaskParPtr->countNoMatch[MI_IMAGE] >= cmaskParPtr->countMatch[MI_IMAGE])
+ {
+ avgMixColordiff = (cmaskParPtr->sumRange[MI_IMAGE] + cmaskParPtr->sumNoMatch[MI_IMAGE])
+ / (cmaskParPtr->countRange[MI_IMAGE] + cmaskParPtr->countNoMatch[MI_IMAGE]);
+ if (avgMixColordiff >= cmaskParPtr->hiColorThreshold)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("NOT MATCHING due to mix range+NoMatch avgMixColordiff:%.5f avgColorThreshold:%.5f\n"
+ , (float)avgMixColordiff
+ , (float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ return (FALSE); /* not matching */
+ }
+ }
+
+
+
+
+
+ }
+
+ if(cmaskParPtr->countNoMatch[MI_IMAGE] > cmaskParPtr->countMatch[MI_IMAGE])
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("NOT MATCHING more NoMatch pixels than Match pixels\n");
+ }
+ return (FALSE); /* not matching */
+ }
+ if(cmaskParPtr->countMatch[MI_IMAGE] > cmaskParPtr->countNoMatch[MI_IMAGE])
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("MATCHING more Match pixels than NoMatch pixels\n");
+ }
+ return (TRUE); /* matching */
+ }
+
+ }
+
+
+ /* pixel is not part of an area of similar colors (or area too small)
+ * continue with GAP_COLORMASK_ALGO_AVG_SMART algorithm
+ */
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("AREA algorithm could not decide, continue with AVG_SMART algorithm: ");
+ }
+
+ }
+
+ /* following code handles the algorithm
+ * GAP_COLORMASK_ALGO_AVG_SMART and
+ * GAP_COLORMASK_ALGO_AVG_AREA (part2 that is ident to the smart algorithm )
+ */
+
+ p_check_range_neighbour_sum(cmaskParPtr
+ , 0 /* dx */
+ , 1 /* dy */
+ , cmaskParPtr->checkRadius
+ , &sumNbColorDiff, &countNbPixels);
+ avgColordiff[0] = sumNbColorDiff / countNbPixels;
+ if ((avgColordiff[0] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)countNbPixels
+ ,(float)avgColordiff[0]
+ ,(float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ /* average matches good */
+ return (TRUE);
+ }
+
+ p_check_range_neighbour_sum(cmaskParPtr
+ , 0 /* dx */
+ ,-1 /* dy */
+ , cmaskParPtr->checkRadius
+ , &sumNbColorDiff, &countNbPixels);
+ avgColordiff[1] = sumNbColorDiff / countNbPixels;
+ if ((avgColordiff[1] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)countNbPixels
+ ,(float)avgColordiff[1]
+ ,(float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ /* average matches good */
+ return (TRUE);
+ }
+
+
+
+ /* horizontal */
+
+ p_check_range_neighbour_sum(cmaskParPtr
+ , 1 /* dx */
+ , 0 /* dy */
+ , cmaskParPtr->checkRadius
+ , &sumNbColorDiff, &countNbPixels);
+ avgColordiff[2] = sumNbColorDiff / countNbPixels;
+ if ((avgColordiff[2] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)countNbPixels
+ ,(float)avgColordiff[2]
+ ,(float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ /* average matches good */
+ return (TRUE);
+ }
+
+
+ p_check_range_neighbour_sum(cmaskParPtr
+ ,-1 /* dx */
+ , 0 /* dy */
+ , cmaskParPtr->checkRadius
+ , &sumNbColorDiff, &countNbPixels);
+ avgColordiff[3] = sumNbColorDiff / countNbPixels;
+ if ((avgColordiff[3] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)countNbPixels
+ ,(float)avgColordiff[3]
+ ,(float)cmaskParPtr->avgColorThreshold
+ );
+ }
+ /* average matches good */
+ return (TRUE);
+ }
+
+
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("x:%d y:%d avgColordiff:[%.4f %.4f %.4f %.4f] at least one is above Thres:%.4f\n"
+ ,(int)cmaskParPtr->x
+ ,(int)cmaskParPtr->y
+ ,(float)avgColordiff[0]
+ ,(float)avgColordiff[1]
+ ,(float)avgColordiff[2]
+ ,(float)avgColordiff[3]
+ ,(float)cmaskParPtr->avgColorThreshold
+ );
+ }
+
+ return (FALSE);
+} /* end p_check_average_colordiff_incl_neighbours */
+
+
+
+
+
+
+
+/* ---------------------------------
+ * p_check_significant_diff
+ * ---------------------------------
+ * returns TRUE in case the specified colors differ significant
+ * (according to curent parameter settings)
+ */
+static gboolean
+p_check_significant_diff(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gint xx, gint yy
+ , GapColorMaskParams *cmaskParPtr)
+{
+ gdouble nbColorDiff;
+ gdouble nbBrightnessDiff;
+ gdouble lum1;
+ gdouble lum2;
+
+ lum1 = (gdouble)(aPixelPtr[0] + aPixelPtr[1] + aPixelPtr[2]) / 765.0; // (255 * 3);
+ lum2 = (gdouble)(bPixelPtr[0] + bPixelPtr[1] + bPixelPtr[2]) / 765.0; // (255 * 3);
+
+ nbBrightnessDiff = fabs(lum1 - lum2);
+ if(nbBrightnessDiff > cmaskParPtr->significantBrightnessDiff)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("SIG Brightnessdiff x:%d y:%d xx:%d yy:%d lum1:%.3f lum2:%.3f nbBrightnessDiff:%.4f significantBrightnessDiff:%.4f\n"
+ , (int)cmaskParPtr->x
+ , (int)cmaskParPtr->y
+ , (int)xx
+ , (int)yy
+ , (float)lum1
+ , (float)lum2
+ , (float)nbBrightnessDiff
+ , (float)cmaskParPtr->significantBrightnessDiff
+ );
+ }
+
+ return (TRUE);
+ }
+
+ nbColorDiff = p_colordiff_guchar_cmask(aPixelPtr
+ , bPixelPtr
+ , cmaskParPtr
+ );
+
+ if(nbColorDiff > cmaskParPtr->significantColordiff)
+ {
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("SIG Colordiff x:%d y:%d nbColorDiff:%.4f significantColordiff:%.4f\n"
+ , (int)cmaskParPtr->x
+ , (int)cmaskParPtr->y
+ , (float)nbColorDiff
+ , (float)cmaskParPtr->significantColordiff
+ );
+ }
+
+ return (TRUE);
+ }
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("SIG similar color x:%d y:%d xx:%d yy:%d lum1:%.3f lum2:%.3f nbBrightnessDiff:%.4f significantBrightnessDiff:%.4f"
+ " nbColorDiff:%.4f significantColordiff:%.4f\n"
+ , (int)cmaskParPtr->x
+ , (int)cmaskParPtr->y
+ , (int)xx
+ , (int)yy
+ , (float)lum1
+ , (float)lum2
+ , (float)nbBrightnessDiff
+ , (float)cmaskParPtr->significantBrightnessDiff
+ , (float)nbColorDiff
+ , (float)cmaskParPtr->significantColordiff
+ );
+ }
+
+
+ return (FALSE);
+} /* end p_check_significant_diff */
+
+
+
+/* --------------------------------------------
+ * p_check_significant_changes_in_one_direction
+ * --------------------------------------------
+ * returns TRUE in case the specified colors differ significant
+ * (according to curent parameter settings)
+ */
+static guchar
+p_check_significant_changes_in_one_direction(GapColorMaskParams *cmaskParPtr, gint dx, gint dy)
+{
+ guchar currPixel[4];
+ guchar prevPixel[4];
+ gint distance;
+ gdouble prevColordiff;
+
+
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest, cmaskParPtr->x, cmaskParPtr->y, &prevPixel[0]);
+ prevColordiff = 0.0;
+
+ for(distance = 1; distance < cmaskParPtr->significantRadius; distance++)
+ {
+ gboolean significantDiff;
+ gdouble currColordiff;
+ gint xx;
+ gint yy;
+ gint32 iTab;
+
+ xx = cmaskParPtr->x + (distance * dx);
+ yy = cmaskParPtr->y + (distance * dy);
+
+
+ /* clipping check test position outside image boudaries */
+ if ((xx < 0) || (yy < 0) || (xx >= cmaskParPtr->width) || (yy >= cmaskParPtr->height))
+ {
+ return (MATCHTYPE_UNDEFINED);
+ }
+
+ gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest, xx, yy, &currPixel[0]);
+
+ significantDiff = p_check_significant_diff(&prevPixel[0]
+ , &currPixel[0]
+ , xx
+ , yy
+ , cmaskParPtr
+ );
+ if(significantDiff)
+ {
+ return (MATCHTYPE_UNDEFINED);
+ }
+
+
+ iTab = p_getColordiffTableIndex(cmaskParPtr, xx, yy);
+ currColordiff = cmaskParPtr->colordiffTable[iTab];
+
+ if (distance > 1)
+ {
+ if ((currColordiff > cmaskParPtr->hiColorThreshold)
+ && (prevColordiff > cmaskParPtr->hiColorThreshold))
+ {
+ /* found "NN" pattern (2 Non matching pixels in series) */
+ return (MATCHTYPE_NOT_MATCHING);
+ }
+
+
+
+// if ((currColordiff < cmaskParPtr->colorThreshold)
+// && (prevColordiff < cmaskParPtr->colorThreshold))
+// {
+// /* found "mm" pattern (2 matching pixels in series) */
+// return (MATCHTYPE_MATCHING);
+// }
+ }
+
+ prevPixel[0] = currPixel[0];
+ prevPixel[1] = currPixel[1];
+ prevPixel[2] = currPixel[2];
+
+ prevColordiff = currColordiff;
+ }
+
+ return (MATCHTYPE_UNDEFINED);
+
+} /* end p_check_significant_changes_in_one_direction */
+
+
+
+
+
+
+
+
+
+
+/* ---------------------------------
+ * p_check_avg_add_one_pixel DEPRECATED
+ * ---------------------------------
+ *
+ */
+static inline void
+p_check_avg_add_one_pixel(GapColorMaskParams *cmaskParPtr, gint xx, gint yy
+ , gdouble *sumNbColorDiff, gdouble *countNbPixels)
+{
+ gint iTab;
+
+ /* clipping check */
+ if ((xx < 0) || (yy < 0) || (xx >= cmaskParPtr->width) || (yy >= cmaskParPtr->height))
+ {
+ return;
+ }
+
+ iTab = p_getColordiffTableIndex(cmaskParPtr, xx, yy);
+ *sumNbColorDiff += cmaskParPtr->colordiffTable[iTab];
+ *countNbPixels += 1.0;
+
+ if (cmaskParPtr->debugCoordinate)
+ {
+ printf("AVCHK xx:%d yy:%d countNbPixels:%.1f colordiffTab:%.4f\n"
+ , (int)xx
+ , (int)yy
+ , *countNbPixels
+ , (float)cmaskParPtr->colordiffTable[iTab]
+ );
+ }
+
+} /* end p_check_avg_add_one_pixel */
+
+
+
+
+/* ---------------------------------
+ * p_init_colordiffTable
+ * ---------------------------------
+ * initialize colordiffTable at full image size
+ * with calculate color differences for all pixels.
+ * (this table will not work for huge images on machines
+ * with limited memory resouces but allows fast
+ * processing by avoiding multiple colordiff processing
+ * and pixel fetch steps and shall do it for
+ * typical video or HD video image sizes.
+ */
+static void
+p_init_colordiffTable (const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *destPR
+ ,GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* mask = maskPR->data; /* the colormask */
+ guchar* dest = destPR->data; /* the destination drawable */
+
+
+ for (row = 0; row < destPR->h; row++)
+ {
+ guint col;
+ guint idxMask;
+ guint idxDest;
+
+ idxMask = 0;
+ idxDest = 0;
+ for(col = 0; col < destPR->w; col++)
+ {
+ gint32 iTab;
+
+ cmaskParPtr->x = destPR->x + col;
+ cmaskParPtr->y = destPR->y + row;
+ iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+
+ {
+ cmaskParPtr->colordiffTable[iTab] =
+ p_colordiff_guchar_cmask( &mask[idxMask]
+ , &dest[idxDest]
+ , cmaskParPtr
+ );
+
+ }
+
+
+
+ idxDest += destPR->bpp;
+ idxMask += maskPR->bpp;
+ }
+
+ mask += maskPR->rowstride;
+ dest += destPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, maskPR->w, "p_init_colordiffTable");
+ }
+ }
+
+} /* end p_init_colordiffTable */
+
+
+/* ---------------------------------
+ * p_getColordiffTableIndex
+ * ---------------------------------
+ */
+static inline gint32
+p_getColordiffTableIndex(GapColorMaskParams *cmaskParPtr, gint x, gint y)
+{
+ gint32 iTab;
+
+ iTab = (y * cmaskParPtr->width) + x;
+ return (iTab);
+}
+
+
+/* --------------------------------------------
+ * p_difflayer_rgn_render
+ * --------------------------------------------
+ * render difflayer to visualize
+ * the colordiffTable in shades of green (MATCHING pixels) yellow (tolrated RANGE) and red. (bad matching RANGE pixels)
+ * or transparent (NON-MATCHING pixels)
+ * The difflayer is not required for productive processing,
+ * this layer is for debug and tuning purpose while development.
+ */
+static void
+p_difflayer_rgn_render (const GimpPixelRgn *diffPR
+ , const GimpPixelRgn *destPR, GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* diff = diffPR->data;
+ guchar* dest = destPR->data;
+
+
+ for (row = 0; row < diffPR->h; row++)
+ {
+ guint col;
+ guint idxDiff;
+ guint idxDest;
+
+ idxDiff = 0;
+ idxDest = 0;
+ for(col = 0; col < diffPR->w; col++)
+ {
+ gint32 iTab;
+ guchar red, green, blue, alpha;
+
+ cmaskParPtr->x = diffPR->x + col;
+ cmaskParPtr->y = diffPR->y + row;
+ iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+ red = 0;
+ green = 0;
+ blue = 0;
+ alpha = 255;
+
+ if (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->hiColorThreshold)
+ {
+ /* NOMATCH pixel color differs significant from colormask pixelcolor
+ * render those pixels fully transparent.
+ */
+ alpha = 0;
+ }
+ else
+ {
+ gdouble intensity;
+
+ intensity = 1.0;
+
+ if(cmaskParPtr->connectByCorner) //// DEBUG
+ {
+ p_set_dynamic_threshold_by_KeyColor(&dest[idxDest], cmaskParPtr);
+ }
+
+ if (cmaskParPtr->colordiffTable[iTab] < cmaskParPtr->colorThreshold)
+ {
+ /* MATCH pixel color equal or nearly equal to colormask pixelcolor
+ * render those pixels in shades of green
+ */
+ if (cmaskParPtr->colorThreshold != 0)
+ {
+ intensity = cmaskParPtr->colordiffTable[iTab] / cmaskParPtr->colorThreshold;
+ }
+ intensity *= 127.0;
+ green = 127 + intensity;
+ }
+ else if (cmaskParPtr->colordiffTable[iTab] <= cmaskParPtr->avgColorThreshold)
+ {
+ gdouble loThres, hiThres;
+
+ loThres = MIN(cmaskParPtr->colorThreshold , cmaskParPtr->avgColorThreshold);
+ hiThres = MAX(cmaskParPtr->colorThreshold , cmaskParPtr->avgColorThreshold);
+
+ /* RANGE pixel color falls in the range of matching colors (below the tolerated avgColorThreshold level)
+ * render those pixels in shades of blue
+ */
+ if (hiThres - loThres)
+ {
+ intensity = (cmaskParPtr->colordiffTable[iTab] - loThres)
+ / (hiThres - loThres);
+ }
+ intensity *= 127.0;
+ blue = 127 + intensity;
+ }
+ else
+ {
+ gdouble loThres, hiThres;
+
+ loThres = MIN(cmaskParPtr->avgColorThreshold , cmaskParPtr->hiColorThreshold);
+ hiThres = MAX(cmaskParPtr->avgColorThreshold , cmaskParPtr->hiColorThreshold);
+
+ /* RANGE pixel color falls in the range of similar but not good matching colors
+ * render those pixels in shades of red
+ */
+ if (hiThres - loThres)
+ {
+ intensity = (cmaskParPtr->colordiffTable[iTab] - loThres)
+ / (hiThres - loThres);
+ }
+ intensity *= 127.0;
+ red = 127 + intensity;
+ }
+ }
+
+
+ diff[idxDiff] = red;
+ diff[idxDiff +1] = green;
+ diff[idxDiff +2] = blue;
+ diff[idxDiff +3] = alpha;
+
+
+ idxDiff += diffPR->bpp;
+ idxDest += destPR->bpp;
+ }
+
+ diff += diffPR->rowstride;
+ dest += destPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, diffPR->w, "p_difflayer_rgn_render");
+ }
+ }
+
+} /* end p_difflayer_rgn_render */
+
+
+/* --------------------------------------------
+ * p_create_colordiffTable_Layer
+ * --------------------------------------------
+ * create difflayer to visualize the colordiffTable
+ * The difflayer is not required for productive processing,
+ * this layer is for debug and tuning purpose while development.
+ */
+static void
+p_create_colordiffTable_Layer(GapColorMaskParams *cmaskParPtr, GimpDrawable *dst_drawable)
+{
+ GimpDrawable *diffLayerDrawable;
+ gpointer pr;
+ GimpPixelRgn diffPR;
+ GimpPixelRgn destPR;
+ gint32 diffLayerId;
+
+ diffLayerId = gap_layer_find_by_name(cmaskParPtr->dst_image_id, COLORMASK_DIFF_LAYER_NAME);
+ if (diffLayerId < 0)
+ {
+ diffLayerId = p_create_empty_layer(cmaskParPtr->dst_image_id
+ , cmaskParPtr->width
+ , cmaskParPtr->height
+ , COLORMASK_DIFF_LAYER_NAME);
+ }
+
+ if (diffLayerId < 0)
+ {
+ return;
+ }
+
+
+ diffLayerDrawable = gimp_drawable_get(diffLayerId);
+
+ gimp_pixel_rgn_init (&diffPR, diffLayerDrawable, 0, 0
+ , diffLayerDrawable->width, diffLayerDrawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+ gimp_pixel_rgn_init (&destPR, dst_drawable, 0, 0
+ , dst_drawable->width, dst_drawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+
+
+ /* init the the worklayer (copy content and setup alpha channel) */
+ for (pr = gimp_pixel_rgns_register (2, &diffPR, &destPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_difflayer_rgn_render (&diffPR, &destPR, cmaskParPtr);
+ }
+
+ gimp_drawable_detach(diffLayerDrawable);
+
+
+} /* end p_create_colordiffTable_Layer */
+
+
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for average algorithm END ------------ */
+/* -------------------------------------------------------------------- */
+
+/* ============================ Stuff for average algorithm END ======= */
+
+
+
+
+/* ============================ Stuff for create or replace individual per frame colormask layer ======= */
+
+/* --------------------------------------------
+ * p_mix_newmask_rgn_render
+ * --------------------------------------------
+ * create individual (per frame) colormask in the work drawable.
+ * as mix of the previous colormask the initial reference colormask and the original layer
+ */
+static void
+p_mix_newmask_rgn_render(const GimpPixelRgn *pmskPR
+ ,const GimpPixelRgn *maskPR
+ ,const GimpPixelRgn *origPR
+ ,const GimpPixelRgn *workPR
+ ,GapColorMaskParams *cmaskParPtr)
+{
+ guint row;
+ guchar* pmsk = pmskPR->data; /* the colormask from previous frame */
+ guchar* mask = maskPR->data; /* the initial reference colormask */
+ guchar* orig = origPR->data; /* the current frame original */
+ guchar* work = workPR->data; /* the destination drawable (e.g. new colormask) */
+
+
+ for (row = 0; row < workPR->h; row++)
+ {
+ guint col;
+ guint idxPmsk;
+ guint idxMask;
+ guint idxOrig;
+ guint idxWork;
+
+ idxPmsk = 0;
+ idxMask = 0;
+ idxOrig = 0;
+ idxWork = 0;
+ for(col = 0; col < workPR->w; col++)
+ {
+ gboolean useNewIndividualPixel;
+ gboolean usePrevMaskPixel;
+ gboolean isProtected;
+
+ //if(gap_debug)
+ {
+ cmaskParPtr->x = workPR->x + col;
+ cmaskParPtr->y = workPR->y + row;
+ cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+ }
+
+
+ useNewIndividualPixel = FALSE;
+ usePrevMaskPixel = FALSE;
+ isProtected = FALSE;
+ if (maskPR->bpp > 3)
+ {
+ if (mask[idxMask + 3] <= cmaskParPtr->triggerAlpha255)
+ {
+ isProtected = TRUE;
+ }
+ }
+
+
+ if (!isProtected)
+ {
+ gdouble colorDiff;
+
+ colorDiff = p_colordiff_guchar_cmask(&mask[idxMask]
+ , &orig[idxOrig]
+ , cmaskParPtr);
+
+ if(colorDiff < cmaskParPtr->hiColorThreshold)
+ {
+ colorDiff = p_colordiff_guchar_cmask(&pmsk[idxPmsk]
+ , &orig[idxOrig]
+ , cmaskParPtr);
+ if(colorDiff < cmaskParPtr->colorThreshold)
+ {
+ useNewIndividualPixel = TRUE;
+ }
+ else if(colorDiff < cmaskParPtr->edgeColorThreshold)
+ {
+ usePrevMaskPixel = TRUE;
+ }
+ }
+ else if(colorDiff < cmaskParPtr->colorThreshold)
+ {
+ useNewIndividualPixel = TRUE;
+ }
+ }
+
+ if(useNewIndividualPixel)
+ {
+ work[idxWork] = orig[idxOrig];
+ work[idxWork +1] = orig[idxOrig +1];
+ work[idxWork +2] = orig[idxOrig +2];
+ }
+ else
+ {
+ if (usePrevMaskPixel)
+ {
+ /* for this pixel copy from previous colormask rgb values */
+ work[idxWork] = pmsk[idxPmsk];
+ work[idxWork +1] = pmsk[idxPmsk +1];
+ work[idxWork +2] = pmsk[idxPmsk +2];
+ }
+ else
+ {
+ /* for this pixel copy from initial colormask rgb values */
+ work[idxWork] = mask[idxMask];
+ work[idxWork +1] = mask[idxMask +1];
+ work[idxWork +2] = mask[idxMask +2];
+ }
+ }
+
+ if (maskPR->bpp > 3)
+ {
+ work[idxWork +3] = mask[idxMask + 3]; /* copy alpha channel from original colormask */
+ }
+ else
+ {
+ work[idxWork +3] = 255;
+ }
+
+
+ idxPmsk += pmskPR->bpp;
+ idxMask += maskPR->bpp;
+ idxOrig += origPR->bpp;
+ idxWork += workPR->bpp;
+ }
+
+ pmsk += pmskPR->rowstride;
+ mask += maskPR->rowstride;
+ orig += origPR->rowstride;
+ work += workPR->rowstride;
+
+ if(cmaskParPtr->doProgress)
+ {
+ p_handle_progress(cmaskParPtr, maskPR->w, "p_mix_newmask_rgn_render");
+ }
+ }
+
+} /* end p_mix_newmask_rgn_render */
+
+
+/* --------------------------------------------
+ * p_fetch_colormask_in_previous_frame
+ * --------------------------------------------
+ */
+static gint32
+p_fetch_colormask_in_previous_frame(gint32 orig_layer_id)
+{
+ gint32 image_id;
+ gint32 prevCmskId;
+ GapAnimInfo *ainfo_ptr;
+
+ prevCmskId = -1;
+ image_id = gimp_drawable_get_image(orig_layer_id);
+ ainfo_ptr = gap_lib_alloc_ainfo(image_id, GIMP_RUN_NONINTERACTIVE);
+ if(ainfo_ptr != NULL)
+ {
+ char *prevFrameName;
+
+ ainfo_ptr->frame_nr = ainfo_ptr->curr_frame_nr - 1;
+
+ prevFrameName = gap_lib_alloc_fname(ainfo_ptr->basename,
+ ainfo_ptr->frame_nr,
+ ainfo_ptr->extension);
+ if(0 == gap_lib_file_exists(prevFrameName))
+ {
+ //if(gap_debug)
+ {
+ printf("previous frame %s does not exist (or is empty)\n"
+ , prevFrameName
+ );
+ }
+ prevCmskId = -1;
+ }
+ else
+ {
+ gint32 image_id;
+
+ image_id = gap_lib_load_image(prevFrameName);
+ if (image_id >= 0)
+ {
+ prevCmskId = gap_layer_find_by_name(image_id, COLORMASK_LAYER_NAME);
+ }
+
+ }
+
+ gap_lib_free_ainfo(&ainfo_ptr);
+ }
+
+ return(prevCmskId);
+
+} /* end p_fetch_colormask_in_previous_frame */
+
+
+/* -----------------------------------------
+ * p_remove_layer_by_name
+ * -----------------------------------------
+ * remove layer from the specified image_id that has
+ * the specified name, AND has a layer_id that is different from orig_layer_id
+ */
+static void
+p_remove_layer_by_name(gint32 image_id, gint32 orig_layer_id, const char *name)
+{
+ gint32 layer_id;
+
+ layer_id = gap_layer_find_by_name(image_id, name);
+ if(layer_id < 0)
+ {
+ return;
+ }
+ if (layer_id != orig_layer_id)
+ {
+ gimp_image_remove_layer(image_id, layer_id);
+ }
+} /* end p_remove_layer_by_name */
+
+/* ============================ Stuff for create or replace individual perf frame colormask layer ======= */
+
+
+/* ------------------------------------
+ * p_gdouble_to_uchar255
+ * ------------------------------------
+ * convert from range 0.0 .. 1.0 to 0 .. 255
+ */
+static guchar
+p_gdouble_to_uchar255(gdouble value)
+{
+ gdouble value255;
+ guchar ret;
+
+ value255 = value * 255.0;
+ ret = CLAMP((guchar)value255, 0, 255);
+
+ if(gap_debug)
+ {
+ printf("value:%f val255:%f ret:%d\n"
+ , (float)value
+ , (float)value255
+ , (int)ret
+ );
+ }
+ return (ret);
+}
+
+/* -----------------------------------------
+ * p_copy_cmaskvals_to_colorMaskParams
+ * -----------------------------------------
+ */
+static void
+p_copy_cmaskvals_to_colorMaskParams(GapColormaskValues *cmaskvals, GapColorMaskParams *cmaskParPtr, gint32 dst_layer_id, gboolean doProgress)
+{
+ //if(gap_debug)
+ {
+ printf("algorithm: %d\n", (int)cmaskvals->algorithm);
+ printf("colormask_id: %d\n", (int)cmaskvals->colormask_id);
+ printf("loColorThreshold: %f\n", (float)cmaskvals->loColorThreshold);
+ printf("hiColorThreshold: %f\n", (float)cmaskvals->hiColorThreshold);
+ printf("colorSensitivity: %f\n", (float)cmaskvals->colorSensitivity);
+ printf("lowerOpacity: %f\n", (float)cmaskvals->lowerOpacity);
+ printf("upperOpacity: %f\n", (float)cmaskvals->upperOpacity);
+ printf("triggerAlpha: %f\n", (float)cmaskvals->triggerAlpha);
+ printf("isleRadius: %f\n", (float)cmaskvals->isleRadius);
+ printf("isleAreaPixelLimit: %f\n", (float)cmaskvals->isleAreaPixelLimit);
+ printf("featherRadius: %f\n", (float)cmaskvals->featherRadius);
+
+ printf("edgeColorThreshold: %f\n", (float)cmaskvals->edgeColorThreshold);
+ printf("thresholdColorArea: %f\n", (float)cmaskvals->thresholdColorArea);
+ printf("pixelDiagonal: %f\n", (float)cmaskvals->pixelDiagonal);
+ printf("pixelAreaLimit: %f\n", (float)cmaskvals->pixelAreaLimit);
+ printf("connectByCorner: %d\n", (int)cmaskvals->connectByCorner);
+ printf("keepLayerMask: %d\n", (int)cmaskvals->keepLayerMask);
+ printf("keepWorklayer: %d\n", (int)cmaskvals->keepWorklayer);
+ printf("enableKeyColorThreshold: %d\n", (int)cmaskvals->enableKeyColorThreshold);
+ printf("loKeyColorThreshold: %f\n", (float)cmaskvals->loKeyColorThreshold);
+ printf("keyColorSensitivity: %f\n", (float)cmaskvals->keyColorSensitivity);
+
+ printf("jaggedRadius: %f\n", (float)cmaskvals->pixelDiagonal); /// TODO
+ printf("jeggedLength: %f\n", (float)cmaskvals->pixelAreaLimit); /// TODO
+
+ printf("avgFactor: %f\n", (float)cmaskvals->edgeColorThreshold); /// TODO
+ printf("checkRadius: %f\n", (float)cmaskvals->pixelDiagonal); /// TODO
+ printf("checkMatchRadius: %f\n", (float)cmaskvals->pixelDiagonal); /// TODO
+ printf("areaMinimumPixels: %f\n", (float)cmaskvals->pixelAreaLimit); /// TODO
+ }
+
+
+ cmaskParPtr->algorithm = cmaskvals->algorithm;
+ cmaskParPtr->dst_layer_id = dst_layer_id;
+ cmaskParPtr->dst_image_id = gimp_drawable_get_image(dst_layer_id);
+ cmaskParPtr->cmask_drawable_id = cmaskvals->colormask_id;
+ cmaskParPtr->colorThreshold = cmaskvals->loColorThreshold; /* use loColorThreshold as default for colorThreshold */
+ cmaskParPtr->loColorThreshold = cmaskvals->loColorThreshold;
+ cmaskParPtr->hiColorThreshold = cmaskvals->hiColorThreshold;
+ cmaskParPtr->colorSensitivity = CLAMP(cmaskvals->colorSensitivity, 1.0, 2.0);
+
+ cmaskParPtr->enableKeyColorThreshold = cmaskvals->enableKeyColorThreshold;
+ cmaskParPtr->loKeyColorThreshold = cmaskvals->loKeyColorThreshold;
+ cmaskParPtr->keyColorSensitivity = cmaskvals->keyColorSensitivity;
+ gimp_rgb_to_hsv(&cmaskvals->keycolor, &cmaskParPtr->keyColorHsv);
+
+
+ cmaskParPtr->lowerOpacity = cmaskvals->lowerOpacity;
+ cmaskParPtr->upperOpacity = cmaskvals->upperOpacity;
+ cmaskParPtr->triggerAlpha = cmaskvals->triggerAlpha;
+ cmaskParPtr->isleRadius = cmaskvals->isleRadius;
+ cmaskParPtr->isleAreaPixelLimit = cmaskvals->isleAreaPixelLimit;
+ cmaskParPtr->featherRadius = cmaskvals->featherRadius;
+
+ cmaskParPtr->edgeColorThreshold = cmaskParPtr->edgeColorThreshold;
+ cmaskParPtr->thresholdColorArea = cmaskvals->thresholdColorArea;
+ cmaskParPtr->pixelDiagonal = cmaskvals->pixelDiagonal;
+ cmaskParPtr->pixelAreaLimit = cmaskvals->pixelAreaLimit;
+ cmaskParPtr->connectByCorner = cmaskvals->connectByCorner;
+ cmaskParPtr->keepLayerMask = cmaskvals->keepLayerMask;
+ cmaskParPtr->keepWorklayer = cmaskvals->keepWorklayer;
+ cmaskParPtr->doProgress = doProgress;
+
+
+ /* precalculate values for per-pixel calculations (performance) */
+ cmaskParPtr->dstLayerMaskId = -1;
+ cmaskParPtr->triggerAlpha255 = p_gdouble_to_uchar255(cmaskParPtr->triggerAlpha);
+ cmaskParPtr->lowerAlpha255 = p_gdouble_to_uchar255(cmaskParPtr->lowerOpacity);
+ cmaskParPtr->upperAlpha255 = p_gdouble_to_uchar255(cmaskParPtr->upperOpacity);
+ cmaskParPtr->transparencyLevel255 = MIN(cmaskParPtr->lowerAlpha255, cmaskParPtr->upperAlpha255);
+ cmaskParPtr->opacityFactor = MAX(cmaskParPtr->lowerOpacity, cmaskParPtr->upperOpacity)
+ - MIN(cmaskParPtr->lowerOpacity, cmaskParPtr->upperOpacity);
+ cmaskParPtr->shadeRange = fabs(cmaskParPtr->hiColorThreshold - cmaskParPtr->loColorThreshold);
+
+ cmaskParPtr->jaggedRadius = (gint)cmaskvals->pixelDiagonal; /// TODO
+ cmaskParPtr->jeggedLength = (gint)cmaskvals->pixelAreaLimit; /// TODO
+
+
+
+ cmaskParPtr->checkRadius = (gint)cmaskvals->pixelDiagonal; /// TODO
+ cmaskParPtr->checkMatchRadius = (gint)cmaskvals->pixelDiagonal; /// TODO
+ cmaskParPtr->areaMinimumPixels = (gint)cmaskvals->pixelAreaLimit; /// TODO
+ cmaskParPtr->avgFactor = cmaskvals->edgeColorThreshold; // TODO
+ cmaskParPtr->avgColorThreshold = cmaskParPtr->colorThreshold
+ + ((cmaskParPtr->hiColorThreshold - cmaskParPtr->colorThreshold) * cmaskParPtr->avgFactor);
+
+
+ cmaskParPtr->significantRadius = (gint)cmaskvals->significantRadius; /// TODO
+ cmaskParPtr->significantColordiff = cmaskvals->significantColordiff; /// TODO
+ cmaskParPtr->significantBrightnessDiff = cmaskvals->significantBrightnessDiff; /// TODO
+
+
+ //if(gap_debug)
+ {
+
+ printf("significantRadius: %d\n", (int)cmaskParPtr->significantRadius);
+ printf("significantColordiff: %f\n", (float)cmaskParPtr->significantColordiff);
+ printf("significantBrightnessDiff: %f\n", (float)cmaskParPtr->significantBrightnessDiff);
+ printf("checkRadius: %f\n", (float)cmaskParPtr->checkRadius);
+ printf("checkMatchRadius: %f\n", (float)cmaskParPtr->checkMatchRadius);
+ printf("areaMinimumPixels: %f\n", (float)cmaskParPtr->areaMinimumPixels);
+ printf("avgFactor: %f\n", (float)cmaskParPtr->avgFactor);
+ printf("avgColorThreshold: %f\n", (float)cmaskParPtr->avgColorThreshold);
+ }
+
+ cmaskParPtr->debugCoordinate = FALSE;
+ p_get_guides(cmaskParPtr);
+
+} /* end p_copy_cmaskvals_to_colorMaskParams */
+
+
+
+/* -----------------------------------------
+ * gap_colormask_apply_to_layer_of_same_size
+ * -----------------------------------------
+ *
+ * handles transparency by applying a color image (RGB) or (RGBA) as mask,
+ * The pixels that are transparent (below or equal triggerAlpha) in
+ * the alpha channel of the colormask are considered as PROTECTED
+ * e.g are not affected by this filter.
+ * (those pixels keep their original alpha channel)
+ *
+ * The resulting transparency is generated as new layermask
+ * that can be optionally applied at end of processing (keepLayerMask FALSE)
+ * Note that an already existing layermask will be applied before processing
+ * and is replaced by the new generated one, even if keepLayerMask FALSE is used.
+ *
+ * current implementation provides 2 algorithms
+ * a) by checking indiviual pixels
+ * b) by checking areas of similar colors (triggered by thresholdColorArea > 0.0
+ *
+ * preconditions:
+ * destination layer (dst_layer_id) and colormask (cmask_drawable_id)
+ * must be of same size.
+ * destination layer must be of type RGBA.
+ * colormask drawable must be of type RGB or RGBA.
+ *
+ * returns -1 on failure or dst_layer_id on success
+ *
+ */
+gint32
+gap_colormask_apply_to_layer_of_same_size (gint32 dst_layer_id
+ , GapColormaskValues *cmaskvals
+ , gboolean doProgress
+ )
+{
+ GapColorMaskParams colorMaskParams;
+ GapColorMaskParams *cmaskParPtr;
+ GimpPixelRgn colormaskPR, dstPR, lmskPR;
+ GimpDrawable *colormask_drawable;
+ GimpDrawable *dst_drawable;
+ GimpDrawable *dstLayerMask_drawable;
+ gint32 oldLayerMaskId;
+ gpointer pr;
+ GimpRGB background;
+ gint32 retLayerId;
+
+
+ retLayerId = dst_layer_id;
+
+ cmaskParPtr = &colorMaskParams;
+ p_copy_cmaskvals_to_colorMaskParams(cmaskvals, cmaskParPtr, dst_layer_id, doProgress);
+
+ dst_drawable = gimp_drawable_get (dst_layer_id);
+ colormask_drawable = gimp_drawable_get (cmaskParPtr->cmask_drawable_id);
+ //if(gap_debug)
+ {
+ printf("gap_colormask_apply_to_layer_of_same_size checking preconditions \n"
+ " DST: width:%d height: %d BPP:%d \n"
+ " MSK: width:%d height: %d BPP:%d \n"
+ " triggerAlpha:%d lowerAlpha:%d upperAlpha:%d algorithm:%d loColorThreshold:%f\n"
+ " cmaskParPtr:%d hiColorThreshold: %f\n"
+ , (int)dst_drawable->width
+ , (int)dst_drawable->height
+ , (int)dst_drawable->bpp
+ , (int)colormask_drawable->width
+ , (int)colormask_drawable->height
+ , (int)colormask_drawable->bpp
+ , (int)cmaskParPtr->triggerAlpha255
+ , (int)cmaskParPtr->lowerAlpha255
+ , (int)cmaskParPtr->upperAlpha255
+ , (int)cmaskParPtr->algorithm
+ , (float)cmaskParPtr->loColorThreshold
+ , (int)cmaskParPtr
+ , (float)cmaskParPtr->hiColorThreshold
+ );
+ }
+
+ if((colormask_drawable->width != dst_drawable->width)
+ || (colormask_drawable->height != dst_drawable->height)
+ || (dst_drawable->bpp != 4)
+ || (colormask_drawable->bpp < 3))
+ {
+ if(gap_debug)
+ {
+ printf("gap_colormask_apply_to_layer_of_same_size preconditions not ok, operation NOT performed\n");
+ }
+ gimp_drawable_detach(colormask_drawable);
+ gimp_drawable_detach(dst_drawable);
+ return (-1);
+ }
+
+ gimp_image_undo_group_start (cmaskParPtr->dst_image_id);
+
+
+
+ cmaskParPtr->width = dst_drawable->width;
+ cmaskParPtr->height = dst_drawable->height;
+ /* setup selected area
+ * current implementation operates always on full layer size
+ */
+ cmaskParPtr->sel_x1 = 0;
+ cmaskParPtr->sel_y1 = 0;
+ cmaskParPtr->sel_x2 = cmaskParPtr->width -1;
+ cmaskParPtr->sel_y2 = cmaskParPtr->height -1;
+
+ /* progress init */
+ {
+ gint pixelSize;
+
+ pixelSize = cmaskParPtr->height * cmaskParPtr->width;
+
+ cmaskParPtr->pixelsDoneAllPasses = 0.0;
+ cmaskParPtr->pixelsToProcessInAllPasses = pixelSize;
+
+ if((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_SMART)
+ || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA))
+ {
+ /* those algorithms have an extra pass to create the colordiffTable */
+ cmaskParPtr->pixelsToProcessInAllPasses += pixelSize;
+ }
+
+
+
+ if(cmaskParPtr->featherRadius >= 1.0)
+ {
+ cmaskParPtr->pixelsToProcessInAllPasses += pixelSize;
+ }
+
+ if((cmaskParPtr->isleRadius >= 1) && (cmaskParPtr->isleAreaPixelLimit >= 1))
+ {
+ cmaskParPtr->pixelsToProcessInAllPasses += pixelSize;
+ }
+ }
+
+ /* remove old layermask if there is one */
+ oldLayerMaskId = gimp_layer_get_mask(cmaskParPtr->dst_layer_id);
+ if (oldLayerMaskId >= 0)
+ {
+ gimp_layer_remove_mask (cmaskParPtr->dst_layer_id, GIMP_MASK_DISCARD);
+ }
+
+
+ /* create a new layermask (white is full opaque */
+ cmaskParPtr->dstLayerMaskId = gimp_layer_create_mask(cmaskParPtr->dst_layer_id, GIMP_ADD_WHITE_MASK);
+ gimp_layer_add_mask(cmaskParPtr->dst_layer_id, cmaskParPtr->dstLayerMaskId);
+ dstLayerMask_drawable = gimp_drawable_get(cmaskParPtr->dstLayerMaskId);
+
+
+ /* create and init pixelfetchers */
+ cmaskParPtr->pftDest = gimp_pixel_fetcher_new (dst_drawable, FALSE /* shadow */);
+ cmaskParPtr->pftMask = gimp_pixel_fetcher_new (colormask_drawable, FALSE /* shadow */);
+ cmaskParPtr->pftDestLayerMask = gimp_pixel_fetcher_new (dstLayerMask_drawable, FALSE /* shadow */);
+
+ gimp_context_get_background (&background);
+ gimp_pixel_fetcher_set_bg_color (cmaskParPtr->pftDest, &background);
+ gimp_pixel_fetcher_set_bg_color (cmaskParPtr->pftMask, &background);
+ gimp_pixel_fetcher_set_bg_color (cmaskParPtr->pftDestLayerMask, &background);
+ gimp_pixel_fetcher_set_edge_mode (cmaskParPtr->pftDest, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+ gimp_pixel_fetcher_set_edge_mode (cmaskParPtr->pftMask, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+ gimp_pixel_fetcher_set_edge_mode (cmaskParPtr->pftDestLayerMask, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+
+ gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+ , colormask_drawable->width, colormask_drawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&dstPR, dst_drawable, 0, 0
+ , dst_drawable->width, dst_drawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&lmskPR, dstLayerMask_drawable, 0, 0
+ , dstLayerMask_drawable->width, dstLayerMask_drawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+
+
+ if((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_SMART)
+ || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_1)
+ || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_2)
+ || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA))
+ {
+ /* code for the average colordiff based algorithms
+ * (all of them are using a colordiffTable) */
+
+ //if(gap_debug)
+ {
+ printf("BEFORE alloc colordiffTable\n");
+ }
+
+ cmaskParPtr->colordiffTable = g_malloc(cmaskParPtr->width * cmaskParPtr->height * sizeof(gdouble));
+
+
+ /* 1.st pass to create a ColordiffTable and initialize with color differences foreach pixel
+ */
+ for (pr = gimp_pixel_rgns_register (2, &colormaskPR, &dstPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_init_colordiffTable (&colormaskPR, &dstPR, cmaskParPtr);
+ }
+
+
+ gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+ , colormask_drawable->width, colormask_drawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&dstPR, dst_drawable, 0, 0
+ , dst_drawable->width, dst_drawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+
+ /* 2.nd pass to render layermask (by average colordiff within radius)
+ */
+ for (pr = gimp_pixel_rgns_register (3, &colormaskPR, &dstPR, &lmskPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_colormask_avg_rgn_render_region (&colormaskPR, &dstPR, &lmskPR, cmaskParPtr);
+ }
+
+
+ if(cmaskParPtr->keepWorklayer)
+ {
+ p_create_colordiffTable_Layer(cmaskParPtr, dst_drawable);
+ }
+
+ g_free(cmaskParPtr->colordiffTable);
+
+
+ }
+ else if(cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_EDGE)
+ {
+ /* edge algorithm basic pass to render layermask (by compare colormask and destination layer colors)
+ */
+ for (pr = gimp_pixel_rgns_register (3, &colormaskPR, &dstPR, &lmskPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_colormask_edge_rgn_render_region (&colormaskPR, &dstPR, &lmskPR, cmaskParPtr);
+ }
+ }
+ else /* handles GAP_COLORMASK_ALGO_SIMPLE */
+ {
+ /* simple basic pass to render layermask (by compare colormask and destination layer colors)
+ */
+ for (pr = gimp_pixel_rgns_register (3, &colormaskPR, &dstPR, &lmskPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_colormask_rgn_render_region (&colormaskPR, &dstPR, &lmskPR, cmaskParPtr);
+ }
+ }
+
+ /* optional pass to remove isolated pixels in the layermask */
+ if((cmaskParPtr->isleRadius >= 1) && (cmaskParPtr->isleAreaPixelLimit >= 1))
+ {
+ gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+ , colormask_drawable->width, colormask_drawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&lmskPR, dstLayerMask_drawable, 0, 0
+ , dstLayerMask_drawable->width, dstLayerMask_drawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+
+ for (pr = gimp_pixel_rgns_register (2, &colormaskPR, &lmskPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_remove_isolates_pixels_rgn_render_region (&colormaskPR, &lmskPR, cmaskParPtr);
+ }
+ }
+
+
+
+ /* final optional pass to render smooth edges in the layermask */
+ if(cmaskParPtr->featherRadius >= 1.0)
+ {
+ gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+ , colormask_drawable->width, colormask_drawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&lmskPR, dstLayerMask_drawable, 0, 0
+ , dstLayerMask_drawable->width, dstLayerMask_drawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+
+ for (pr = gimp_pixel_rgns_register (2, &colormaskPR, &lmskPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_smooth_edges_rgn_render_region (&colormaskPR, &lmskPR, cmaskParPtr);
+ }
+ }
+
+
+ gimp_pixel_fetcher_destroy (cmaskParPtr->pftDestLayerMask);
+ gimp_pixel_fetcher_destroy (cmaskParPtr->pftDest);
+ gimp_pixel_fetcher_destroy (cmaskParPtr->pftMask);
+
+ gimp_drawable_detach(dst_drawable);
+ gimp_drawable_detach(colormask_drawable);
+ gimp_drawable_detach(dstLayerMask_drawable);
+
+
+ if(cmaskParPtr->dstLayerMaskId >= 0)
+ {
+ if(!cmaskParPtr->keepLayerMask)
+ {
+ gimp_layer_remove_mask (cmaskParPtr->dst_layer_id, GIMP_MASK_APPLY);
+ }
+ }
+
+ gimp_image_undo_group_end (cmaskParPtr->dst_image_id);
+
+ return (retLayerId);
+
+} /* end gap_colormask_apply_to_layer_of_same_size */
+
+
+/* ---------------------------------------------------
+ * gap_colormask_apply_to_layer_of_same_size_from_file
+ * ---------------------------------------------------
+ * call gap_colormask_apply_to_layer_of_same_size
+ * with parameters loaded from specified filename
+ */
+gint32
+gap_colormask_apply_to_layer_of_same_size_from_file (gint32 dst_layer_id
+ , gint32 colormask_id
+ , const char *filename
+ , gboolean keepLayerMask
+ , gboolean doProgress
+ )
+{
+ GapColormaskValues cmaskvals;
+ gboolean isLoadOk;
+
+ if(filename != NULL)
+ {
+ isLoadOk = gap_colormask_file_load (filename, &cmaskvals);
+ }
+ else
+ {
+ /* set default values */
+ cmaskvals.loColorThreshold = 0.11;
+ cmaskvals.hiColorThreshold = 0.11;
+ cmaskvals.colorSensitivity = 1.35;
+ cmaskvals.lowerOpacity = 0.0;
+ cmaskvals.upperOpacity = 1.0;
+ cmaskvals.triggerAlpha = 0.8;
+ cmaskvals.isleRadius = 1.0;
+ cmaskvals.isleAreaPixelLimit = 7.0;
+ cmaskvals.featherRadius = 3.0;
+ cmaskvals.thresholdColorArea = 0.0;
+ cmaskvals.pixelDiagonal = 10.0;
+ cmaskvals.pixelAreaLimit = 25.0;
+
+ isLoadOk = TRUE;
+ }
+
+ cmaskvals.colormask_id = colormask_id;
+ cmaskvals.keepLayerMask = keepLayerMask;
+ cmaskvals.keepWorklayer = FALSE; /* dont keep debug worklayer when using the param file interface */
+ if (isLoadOk)
+ {
+ return (gap_colormask_apply_to_layer_of_same_size (dst_layer_id
+ , &cmaskvals
+ , doProgress
+ ));
+ }
+
+ return -1;
+
+} /* end gap_colormask_apply_to_layer_of_same_size_from_file */
+
+
+
+
+/* ======= */
+
+
+/* -----------------------------------------
+ * gap_create_or_replace_colormask_layer
+ * -----------------------------------------
+ * this procedure creates (or replaces) a layer named "colormask" and adds
+ * it on top of the layerstack.
+ * the new colormask layer is made as mix of the specified dst_layer_id
+ * and the colormask layer of the previous frame.
+ * (therefore the frame image with the previous frame number
+ * is loladed and searched for a layer with the name "colormask"
+ *
+ */
+gint32
+gap_create_or_replace_colormask_layer (gint32 orig_layer_id
+ , GapColormaskValues *cmaskvals
+ , gboolean doProgress
+ )
+{
+ GapColorMaskParams colorMaskParams;
+ GapColorMaskParams *cmaskParPtr;
+ GimpPixelRgn prevCmaskPR, colormaskPR, origPR, workPR;
+ GimpDrawable *prevCmask_drawable;
+ GimpDrawable *colormask_drawable;
+ GimpDrawable *orig_drawable;
+ GimpDrawable *work_drawable;
+ gpointer pr;
+ gint32 workLayerId;
+ gint32 prevCmaskId;
+
+
+
+ prevCmaskId = p_fetch_colormask_in_previous_frame(orig_layer_id);
+ //if(gap_debug)
+ {
+ printf("gap_create_or_replace_colormask_layer prevCmaskId:%d\n"
+ , (int)prevCmaskId
+ );
+ }
+
+ if (prevCmaskId < 0)
+ {
+ prevCmaskId = cmaskvals->colormask_id;
+ }
+ cmaskParPtr = &colorMaskParams;
+ p_copy_cmaskvals_to_colorMaskParams(cmaskvals, cmaskParPtr, orig_layer_id, doProgress);
+
+ orig_drawable = gimp_drawable_get (orig_layer_id);
+ colormask_drawable = gimp_drawable_get (cmaskParPtr->cmask_drawable_id);
+ if(gap_debug)
+ {
+ printf("gap_create_or_replace_colormask_layer checking preconditions \n"
+ " DST: width:%d height: %d BPP:%d \n"
+ " MSK: width:%d height: %d BPP:%d \n"
+ " triggerAlpha:%d lowerAlpha:%d upperAlpha:%d colorThreshold:%f\n"
+ , (int)orig_drawable->width
+ , (int)orig_drawable->height
+ , (int)orig_drawable->bpp
+ , (int)colormask_drawable->width
+ , (int)colormask_drawable->height
+ , (int)colormask_drawable->bpp
+ , (int)cmaskParPtr->triggerAlpha255
+ , (int)cmaskParPtr->lowerAlpha255
+ , (int)cmaskParPtr->upperAlpha255
+ , (float)cmaskParPtr->colorThreshold
+ );
+ }
+
+ if((colormask_drawable->width != orig_drawable->width)
+ || (colormask_drawable->height != orig_drawable->height)
+ || (orig_drawable->bpp != 4)
+ || (colormask_drawable->bpp < 3))
+ {
+ //if(gap_debug)
+ {
+ printf("gap_create_or_replace_colormask_layer preconditions not ok, operation NOT performed\n");
+ }
+ gimp_drawable_detach(colormask_drawable);
+ gimp_drawable_detach(orig_drawable);
+ return (-1);
+ }
+
+ gimp_image_undo_group_start (cmaskParPtr->dst_image_id);
+
+
+
+ cmaskParPtr->width = orig_drawable->width;
+ cmaskParPtr->height = orig_drawable->height;
+ /* progress init */
+ {
+ gint pixelSize;
+
+ pixelSize = cmaskParPtr->height * cmaskParPtr->width;
+
+ cmaskParPtr->pixelsDoneAllPasses = 0.0;
+ cmaskParPtr->pixelsToProcessInAllPasses = pixelSize;
+ }
+
+
+ p_remove_layer_by_name(cmaskParPtr->dst_image_id, orig_layer_id, COLORMASK_LAYER_NAME);
+ workLayerId = p_create_empty_layer(cmaskParPtr->dst_image_id
+ , cmaskParPtr->width
+ , cmaskParPtr->height
+ , COLORMASK_LAYER_NAME
+ );
+
+ work_drawable = gimp_drawable_get (workLayerId);
+ prevCmask_drawable = gimp_drawable_get (prevCmaskId);
+
+
+ gimp_pixel_rgn_init (&prevCmaskPR, prevCmask_drawable, 0, 0
+ , prevCmask_drawable->width, prevCmask_drawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+ , colormask_drawable->width, colormask_drawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&origPR, orig_drawable, 0, 0
+ , orig_drawable->width, orig_drawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+ gimp_pixel_rgn_init (&workPR, work_drawable, 0, 0
+ , work_drawable->width, work_drawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+
+ for (pr = gimp_pixel_rgns_register (4, &prevCmaskPR, &colormaskPR, &origPR, &workPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_mix_newmask_rgn_render (&prevCmaskPR, &colormaskPR, &origPR, &workPR, cmaskParPtr);
+ }
+
+
+ gimp_drawable_detach(prevCmask_drawable);
+ gimp_drawable_detach(colormask_drawable);
+ gimp_drawable_detach(orig_drawable);
+ gimp_drawable_detach(work_drawable);
+
+ gimp_image_undo_group_end (cmaskParPtr->dst_image_id);
+
+ if(prevCmaskId != cmaskParPtr->cmask_drawable_id)
+ {
+ /* delete (e.g. close) the previous frame image (form memory not from disk)
+ */
+ gap_image_delete_immediate(gimp_drawable_get_image(prevCmaskId));
+ }
+ return (workLayerId);
+
+} /* end gap_create_or_replace_colormask_layer */
+
+
+/* -----------------------------------------
+ * gap_colormask_apply_by_name
+ * -----------------------------------------
+ * find colormask in the layerstack by name
+ * and apply to dst_layer_id if found.
+ * return -1 if not found
+ *
+ */
+gint32
+gap_colormask_apply_by_name (gint32 dst_layer_id
+ , GapColormaskValues *cmaskvals
+ , gboolean doProgress
+ )
+{
+ gint32 colormaskId;
+
+
+ colormaskId = gap_layer_find_by_name(gimp_drawable_get_image(dst_layer_id), COLORMASK_LAYER_NAME);
+ if (colormaskId < 0)
+ {
+ return (-1);
+ }
+
+ cmaskvals->colormask_id = colormaskId;
+ return (gap_colormask_apply_to_layer_of_same_size (dst_layer_id
+ , cmaskvals
+ , doProgress
+ ));
+
+} /* end gap_colormask_apply_by_name */
+
+
diff --git a/gap/gap_colormask_exec.h b/gap/gap_colormask_exec.h
new file mode 100644
index 0000000..b8d0ac5
--- /dev/null
+++ b/gap/gap_colormask_exec.h
@@ -0,0 +1,62 @@
+/* gap_colormask_exec.h
+ * by hof (Wolfgang Hofer)
+ * color mask filter worker procedures
+ * to set alpha channel for a layer according to matching colors
+ * of color mask (image)
+ * 2010/02/21
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#ifndef _GAP_COLORMASK_EXEC_H
+#define _GAP_COLORMASK_EXEC_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+gint32 gap_colormask_apply_to_layer_of_same_size (gint32 dst_layer_id
+ ,GapColormaskValues *cmaskvals
+ ,gboolean doProgress
+ );
+gint32 gap_colormask_apply_to_layer_of_same_size_from_file (gint32 dst_layer_id
+ , gint32 colormask_id
+ , const char *filename
+ , gboolean keepLayerMask
+ , gboolean doProgress
+ );
+
+gint32 gap_create_or_replace_colormask_layer (gint32 orig_layer_id
+ , GapColormaskValues *cmaskvals
+ , gboolean doProgress
+ );
+gint32 gap_colormask_apply_by_name (gint32 dst_layer_id
+ , GapColormaskValues *cmaskvals
+ , gboolean doProgress
+ );
+
+#endif
diff --git a/gap/gap_colormask_file.c b/gap/gap_colormask_file.c
new file mode 100644
index 0000000..0db7bdc
--- /dev/null
+++ b/gap/gap_colormask_file.c
@@ -0,0 +1,166 @@
+/* gap_colormask_file.c
+ * by hof (Wolfgang Hofer)
+ * colormask filter parameter file handling procedures (load/save)
+ * 2010/02/25
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "config.h"
+#include "gap_lib_common_defs.h"
+#include "gap_libgimpgap.h"
+#include "gap_colormask_file.h"
+
+extern int gap_debug;
+
+
+/* --------------------------
+ * p_set_master_keywords
+ * --------------------------
+ * set keywords for the colordiff filter parameter file format
+ * adresspointers to the relevant parameter, datatype (how to scan value when parameter are read from file)
+ * and comment string (that is added to the parameter when saved to file)
+ */
+static void
+p_set_master_keywords(GapValKeyList *keylist, GapColormaskValues *cmaskvals)
+{
+ gap_val_set_keyword(keylist, "(algorithm ", &cmaskvals->algorithm, GAP_VAL_GINT, 0, "# 0 simple algorithm");
+ gap_val_set_keyword(keylist, "(loColorThreshold ", &cmaskvals->loColorThreshold, GAP_VAL_GDOUBLE, 0, "# lower color threshold (0.0 to 1.0)");
+ gap_val_set_keyword(keylist, "(hiColorThreshold ", &cmaskvals->hiColorThreshold, GAP_VAL_GDOUBLE, 0, "# upper color threshold (0.0 to 1.0)");
+ gap_val_set_keyword(keylist, "(colorSensitivity ", &cmaskvals->colorSensitivity, GAP_VAL_GDOUBLE, 0, "# gamma valid range is (1.0 to 2.0)");
+ gap_val_set_keyword(keylist, "(lowerOpacity ", &cmaskvals->lowerOpacity, GAP_VAL_GDOUBLE, 0, "# color threshold (0.0 to 1.0)");
+ gap_val_set_keyword(keylist, "(upperOpacity ", &cmaskvals->upperOpacity, GAP_VAL_GDOUBLE, 0, "# color threshold (0.0 to 1.0)");
+ gap_val_set_keyword(keylist, "(triggerAlpha ", &cmaskvals->triggerAlpha, GAP_VAL_GDOUBLE, 0, "# opacity protection threshold (0.0 to 1.0)");
+ gap_val_set_keyword(keylist, "(isleRadius ", &cmaskvals->isleRadius, GAP_VAL_GDOUBLE, 0, "# radius for isolated pixel removal. (0.0 to 10.0)");
+ gap_val_set_keyword(keylist, "(isleAreaPixelLimit ", &cmaskvals->isleAreaPixelLimit, GAP_VAL_GDOUBLE, 0, "# size of area in pixels for remove isaolated pixel(area)s (0.0 to 100.0)");
+ gap_val_set_keyword(keylist, "(featherRadius ", &cmaskvals->featherRadius, GAP_VAL_GDOUBLE, 0, "# radius for feathering edges (0.0 to 10.0)");
+
+ gap_val_set_keyword(keylist, "(edgeColorThreshold ", &cmaskvals->edgeColorThreshold, GAP_VAL_GDOUBLE, 0, "# threshold for edge detection (relevant for algorithm 1)");
+ gap_val_set_keyword(keylist, "(thresholdColorArea ", &cmaskvals->thresholdColorArea, GAP_VAL_GDOUBLE, 0, "# threshold for color difference colors belonging to same area.(algorithm 2)");
+ gap_val_set_keyword(keylist, "(pixelDiagonal ", &cmaskvals->pixelDiagonal, GAP_VAL_GDOUBLE, 0, "# areas with smaller diagonale are considered small isles (algorithm 2, 0.0 to 100.0)");
+ gap_val_set_keyword(keylist, "(pixelAreaLimit ", &cmaskvals->pixelAreaLimit, GAP_VAL_GDOUBLE, 0, "# areas with less pixels are considered as small isles (algorithm 2, 0.0 to 1000.0)");
+ gap_val_set_keyword(keylist, "(connectByCorner ", &cmaskvals->connectByCorner, GAP_VAL_GBOOLEAN, 0, "# pixels touching only at corners are of the area. (algorithm 2, 0 to 1)");
+
+ gap_val_set_keyword(keylist, "(enableKeyColorThreshold ", &cmaskvals->enableKeyColorThreshold, GAP_VAL_GBOOLEAN, 0, "# enable individual low threshold for key color");
+ gap_val_set_keyword(keylist, "(loKeyColorThreshold ", &cmaskvals->loKeyColorThreshold, GAP_VAL_GDOUBLE, 0, "# the individual threshold for keycolor (relevant when enableKeyColorThreshold == 1)");
+ gap_val_set_keyword(keylist, "(keyColorSensitivity ", &cmaskvals->keyColorSensitivity, GAP_VAL_GDOUBLE, 0, "# sensitivity for keycolor (relevant when enableKeyColorThreshold == 1 0.0 to 10.0)");
+ gap_val_set_keyword(keylist, "(keyColor.r ", &cmaskvals->keycolor.r, GAP_VAL_GDOUBLE, 0, "# keycolor red channel (0.0 to 1.0)");
+ gap_val_set_keyword(keylist, "(keyColor.g ", &cmaskvals->keycolor.g, GAP_VAL_GDOUBLE, 0, "# keycolor green channel (0.0 to 1.0)");
+ gap_val_set_keyword(keylist, "(keyColor.b ", &cmaskvals->keycolor.b, GAP_VAL_GDOUBLE, 0, "# keycolor blue channel (0.0 to 1.0)");
+
+} /* end p_set_master_keywords */
+
+
+/* --------------------------
+ * gap_colormask_file_load
+ * --------------------------
+ * load parameters from parameter file.
+ * return TRUE if ok, FALSE if not
+ */
+gboolean
+gap_colormask_file_load (const char *filename, GapColormaskValues *cmaskvals)
+{
+ GapValKeyList *keylist;
+ gint cnt_scanned_items;
+
+ /* TODO maybe init with defaults the case where no value available ?
+ */
+
+ keylist = gap_val_new_keylist();
+ p_set_master_keywords(keylist, cmaskvals);
+
+ cnt_scanned_items = gap_val_scann_filevalues(keylist, filename);
+ gap_val_free_keylist(keylist);
+
+ //if(gap_debug)
+ {
+ printf("gap_colormask_file_load: params loaded: cmaskvals:%d cnt_scanned_items:%d\n"
+ , (int)cmaskvals
+ , (int)cnt_scanned_items
+ );
+ }
+
+ if(cnt_scanned_items <= 0)
+ {
+ g_message(_("Could not read colormask parameters from file:\n%s"), filename);
+ return (FALSE);
+ }
+
+ return(TRUE);
+
+} /* end gap_colormask_file_load */
+
+
+/* --------------------------
+ * gap_colormask_file_save
+ * --------------------------
+ * (re)write the specified ffmpeg videoencode parameter file
+ */
+gboolean
+gap_colormask_file_save (const char *filename, GapColormaskValues *cmaskvals)
+{
+ GapValKeyList *keylist;
+ int l_rc;
+
+ keylist = gap_val_new_keylist();
+
+ //if(gap_debug)
+ {
+ printf("gap_colormask_file_save: now saving parameters: to file:%s\n", filename);
+ }
+
+ p_set_master_keywords(keylist, cmaskvals);
+ l_rc = gap_val_rewrite_file(keylist
+ , filename
+ , "# GIMP / GAP COLORMASK parameter file" /* hdr_text */
+ , ")" /* terminate char */
+ );
+
+ gap_val_free_keylist(keylist);
+
+ if(l_rc != 0)
+ {
+ gint l_errno;
+
+ l_errno = errno;
+ g_message(_("Could not save colormask parameterfile:"
+ "'%s'"
+ "%s")
+ ,filename
+ ,g_strerror (l_errno) );
+ return (FALSE);
+ }
+
+ return(TRUE);
+} /* end gap_colormask_file_save */
+
diff --git a/gap/gap_colormask_file.h b/gap/gap_colormask_file.h
new file mode 100644
index 0000000..8fc3a5a
--- /dev/null
+++ b/gap/gap_colormask_file.h
@@ -0,0 +1,91 @@
+/* gap_colormask_file.h
+ * by hof (Wolfgang Hofer)
+ * colormask filter parameter file handling procedures (load/save)
+ * 2010/02/25
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#ifndef _GAP_COLORMASK_FILE_H
+#define _GAP_COLORMASK_FILE_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+#define GAP_COLORMASK_ALGO_SIMPLE 0
+#define GAP_COLORMASK_ALGO_AVG_CHANGE_1 1
+#define GAP_COLORMASK_ALGO_AVG_CHANGE_2 2
+#define GAP_COLORMASK_ALGO_AVG_SMART 3
+#define GAP_COLORMASK_ALGO_AVG_AREA 4
+
+#define GAP_COLORMASK_ALGO_EDGE 5
+#define GAP_COLORMASK_ALGO_MAX 5.0
+
+typedef struct {
+ gint32 colormask_id;
+ gdouble loColorThreshold; /* color difference threshold 0.0 to 1.0 */
+ gdouble hiColorThreshold; /* color difference threshold 0.0 to 1.0 */
+ gdouble colorSensitivity; /* 1.0 to 2.0 sensitivity for colr diff algorithm */
+ gdouble lowerOpacity; /* opacity for matching pixels 0.0 to 1.0 */
+ gdouble upperOpacity; /* opacity for non matching pixels 0.0 to 1.0 */
+ gdouble triggerAlpha; /* 0.0 to 1.0 mask opacity greater than this alpha value trigger color masking */
+ gdouble isleRadius; /* radius in pixels for remove isaolated pixels */
+ gdouble isleAreaPixelLimit; /* size of area in pixels for remove isaolated pixel(area)s */
+ gdouble featherRadius; /* radius in pixels for feathering edges */
+
+ gdouble edgeColorThreshold;
+ gdouble thresholdColorArea;
+ gdouble pixelDiagonal;
+ gdouble pixelAreaLimit;
+ gboolean connectByCorner;
+
+ gboolean keepLayerMask;
+ gboolean keepWorklayer; /* shall be FALSE for productive usage
+ * TRUE: debug feature for visible verification of the area algorithm processing
+ */
+ gboolean enableKeyColorThreshold;
+ GimpRGB keycolor;
+ gdouble loKeyColorThreshold; /* color difference lower threshold for keycolor 0.0 to 1.0 */
+ gdouble keyColorSensitivity; /* 0.1 upto 10.0 default 1.0 */
+
+ gdouble significantRadius; /* radius to limit search for significant brightness/colorchanges */
+ gdouble significantColordiff; /* threshold to define when colors are considered as significant different */
+ gdouble significantBrightnessDiff; /* threshold to define significant brightness changes */
+
+
+ gint algorithm; /* 0 = simple, 1 = edge, 2= area */
+
+} GapColormaskValues;
+
+
+gboolean gap_colormask_file_load (const char *filename, GapColormaskValues *cmaskvals);
+
+gboolean gap_colormask_file_save (const char *filename, GapColormaskValues *cmaskvals);
+
+
+#endif
diff --git a/gap/gap_colormask_main.c b/gap/gap_colormask_main.c
new file mode 100644
index 0000000..0794d0d
--- /dev/null
+++ b/gap/gap_colormask_main.c
@@ -0,0 +1,467 @@
+/* gap_colormask_main.c
+ * color mask filter main and dialog procedures
+ * to set alpha channel for a layer according to matching colors
+ * of a color mask (image) by Wolfgang Hofer
+ * 2010/02/21
+ */
+
+/* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Revision history
+ * (2010/02/21) 2.7.0 hof: created
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gap_lastvaldesc.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_dialog.h"
+#include "gap_colormask_exec.h"
+
+#include "gap-intl.h"
+
+/* Defines */
+#define PLUG_IN_NAME "plug-in-layer-set-alpha-by-colormask"
+#define PLUG_IN_NAME_2 "plug-in-layer-set-alpha-by-colormask-in-frame"
+#define PLUG_IN_NAME_3 "plug-in-layer-create-colormask-in-frame"
+#define PLUG_IN_PRINT_NAME "Name to Layer"
+#define PLUG_IN_IMAGE_TYPES "RGB*, INDEXED*, GRAY*"
+#define PLUG_IN_AUTHOR "Wolfgang Hofer (hof gimp org)"
+#define PLUG_IN_COPYRIGHT "Wolfgang Hofer"
+
+
+int gap_debug = 1; /* 1 == print debug infos , 0 dont print debug infos */
+
+
+
+static GapColormaskValues cmaskvals =
+{
+ -1 /* colormask_id drawable id */
+ , 0.03 /* loColorThreshold */
+ , 0.12 /* hiColorThreshold */
+ , 1.35 /* colorSensitivity */
+ , 0.0 /* lowerOpacity */
+ , 1.0 /* upperOpacity */
+ , 0.8 /* triggerAlpha */
+ , 1.0 /* isleRadius */
+ , 7.0 /* isleAreaPixelLimit */
+ , 2.0 /* featherRadius */
+ , 0.1 /* edgeColorThreshold */
+ , 0.0 /* thresholdColorArea */
+ , 6.0 /* pixelDiagonal */
+ , 10.0 /* pixelAreaLimit */
+ , 0 /* connectByCorner */
+
+ , 1 /* keepLayerMask */
+ , 0 /* keepWorklayer */
+
+
+ , 0 /* gboolean enableKeyColorThreshold */
+ , { 0,0,0,0 } /* GimpRGB keycolor */
+ , 0.06 /* gdouble loKeyColorThreshold */
+ , 1.0 /* gdouble keyColorSensitivity */
+
+
+ , GAP_COLORMASK_ALGO_AVG_SMART
+};
+
+
+
+
+static GimpDrawable *globalDrawable = NULL;
+
+static void query (void);
+static void run (const gchar *name, /* name of plugin */
+ gint nparams, /* number of in-paramters */
+ const GimpParam * param, /* in-parameters */
+ gint *nreturn_vals, /* number of out-parameters */
+ GimpParam ** return_vals); /* out-parameters */
+
+static gint p_colormask_apply_run (gint32 image_id, gint32 drawable_id, gboolean doProgress, const char *name);
+
+
+
+
+/* Global Variables */
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+static GimpParamDef in_args[] = {
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "layer with alpha channel to be processed"},
+ { GIMP_PDB_DRAWABLE, "colormaskDrawable", "rgb drawable (optional with with alpha channel) to be applied"},
+ { GIMP_PDB_FLOAT, "loColorThreshold", "0.0 to 1.0 color difference lower threshold value (diff below this is considered as matching color)"},
+ { GIMP_PDB_FLOAT, "hiColorThreshold", "0.0 to 1.0 color difference upper threshold value (diff above this is considered as not matching color)"},
+ { GIMP_PDB_FLOAT, "colorSensitivity", "1 .. 2 sensitivity for color dff algorithm"},
+ { GIMP_PDB_FLOAT, "lowerOpacity", "0.0 to 1.0 opacity value for matching pixels"},
+ { GIMP_PDB_FLOAT, "upperOpacity", "0.0 to 1.0 opacity value for non matching pixels"},
+ { GIMP_PDB_FLOAT, "triggerAlpha", "0.0 to 1.0 in case the colormask has alpha, greater values than the specifiedone trigger colormasking "},
+ { GIMP_PDB_FLOAT, "isleRadius", "radius in pixels for remove isolated pixels"},
+ { GIMP_PDB_FLOAT, "isleAreaPixelLimit", "area size in pixels for removal of isolated pixel areas )"},
+ { GIMP_PDB_FLOAT, "featherRadius", "radius in pixels for smoothing edges "},
+ { GIMP_PDB_FLOAT, "edgeColorThreshold", "color difference for edge detection (relevant for algorthm 1, range 0.0 to 1.0) "},
+ { GIMP_PDB_FLOAT, "thresholdColorArea", "threshold for color difference colors belonging to same area. (relevant for algorithm 2 range .0 to 1.0) "},
+ { GIMP_PDB_FLOAT, "pixelDiagonal", "areas with smaller diagonale are considered small isles (relevant for algorithm 2)"},
+ { GIMP_PDB_FLOAT, "pixelAreaLimit", "areas with less pixels are considered as small isles (relevant for algorithm 2)"},
+ { GIMP_PDB_INT32, "connectByCorner", "pixels toching only at corners ar pat/not part of the area valid range is (relevant for algorithm 2) "},
+ { GIMP_PDB_INT32, "keepLayerMask", "0 apply the generated layerMask, 1 keep the generated layermask"},
+ { GIMP_PDB_INT32, "keepWorklayer", "0 delete internal worklayer, 1 keep the generated layermask (relevant for algorithm 2 for DEBUG purpose)"},
+ { GIMP_PDB_INT32, "enableKeyColorThreshold", "0 disable, 1 enable individual threshold for keycolor"},
+ { GIMP_PDB_COLOR, "keycolor", "KeyColor" },
+ { GIMP_PDB_FLOAT, "loKeyColorThreshold", "0.0 to 1.0 color difference lower individual threshold value for the key color"},
+ { GIMP_PDB_FLOAT, "keyColorSensitivity", "0.0 to 10.0 "},
+ { GIMP_PDB_INT32, "algorithm", "0 simple, 1 edge, 2 area"}
+ };
+
+static GimpParamDef return_vals[] = {
+ { GIMP_PDB_DRAWABLE, "the_drawable", "the handled drawable" }
+};
+
+static gint global_number_in_args = G_N_ELEMENTS (in_args);
+static gint global_number_out_args = G_N_ELEMENTS (return_vals);
+
+
+/* Functions */
+
+MAIN ()
+
+static void query (void)
+{
+ static GimpLastvalDef lastvals[] =
+ {
+ GIMP_LASTVALDEF_DRAWABLE (GIMP_ITER_FALSE, cmaskvals.colormask_id, "colormaskDrawable"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_FALSE, cmaskvals.loColorThreshold, "loColorThreshold"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_FALSE, cmaskvals.hiColorThreshold, "hiColorThreshold"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_FALSE, cmaskvals.colorSensitivity, "colorSensitivity"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.lowerOpacity, "lowerOpacity"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.upperOpacity, "upperOpacity"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.triggerAlpha, "triggerAlpha"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.isleRadius, "isleRadius"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.isleAreaPixelLimit, "isleAreaPixelLimit"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.featherRadius, "featherRadius"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.edgeColorThreshold, "edgeColorThreshold"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.thresholdColorArea, "thresholdColorArea"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.pixelDiagonal, "pixelDiagonal"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.pixelAreaLimit, "pixelAreaLimit"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, cmaskvals.connectByCorner, "connectByCorner"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, cmaskvals.keepLayerMask, "keepLayerMask"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, cmaskvals.keepWorklayer, "keepWorklayer"),
+ GIMP_LASTVALDEF_GINT32 (GIMP_ITER_FALSE, cmaskvals.enableKeyColorThreshold, "enableKeyColorThreshold"),
+ GIMP_LASTVALDEF_GIMPRGB (GIMP_ITER_TRUE, cmaskvals.keycolor, "keycolor"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.loKeyColorThreshold, "loKeyColorThreshold"),
+ GIMP_LASTVALDEF_GDOUBLE (GIMP_ITER_TRUE, cmaskvals.keyColorSensitivity, "keyColorSensitivity"),
+ GIMP_LASTVALDEF_GINT (GIMP_ITER_FALSE, cmaskvals.algorithm, "algorithm")
+ };
+
+
+
+ gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+ /* registration for last values buffer structure (useful for animated filter apply) */
+ gimp_lastval_desc_register(PLUG_IN_NAME,
+ &cmaskvals,
+ sizeof(cmaskvals),
+ G_N_ELEMENTS (lastvals),
+ lastvals);
+// gimp_lastval_desc_register(PLUG_IN_NAME_2,
+// &cmaskvals,
+// sizeof(cmaskvals),
+// G_N_ELEMENTS (lastvals),
+// lastvals);
+// gimp_lastval_desc_register(PLUG_IN_NAME_3,
+// &cmaskvals,
+// sizeof(cmaskvals),
+// G_N_ELEMENTS (lastvals),
+// lastvals);
+
+ /* the actual installation of the plugin */
+ gimp_install_procedure (PLUG_IN_NAME,
+ "Set layer opacity by applying another color drawable as colormask",
+ "This plug-in sets the opacity for the specified Input drawable (a layer), "
+ "according to matching colors of the specified colormask drawable. "
+ "The resulting opacity is created as layermask. "
+ "This newly created layermask is applied optionally (in case parameter keepLayerMask is FALSE)."
+ "Note that the colormask drawable may have an alpha channel."
+ "in this case the transparent parts of the colormask are protected areas, "
+ "that are not affected by this filter. "
+ "(e.g the corresponding pixels in processed layer keep their original opacity) "
+ " ",
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ GAP_VERSION_WITH_DATE,
+ N_("Apply Colormask..."),
+ PLUG_IN_IMAGE_TYPES,
+ GIMP_PLUGIN,
+ global_number_in_args,
+ global_number_out_args,
+ in_args,
+ return_vals);
+// gimp_install_procedure (PLUG_IN_NAME_2,
+// "Set Layer Opacity by applying the colormask layer colormask",
+// "This plug-in sets the alpha channel for the specified Input drawable (a layer), "
+// "according to matching colors of the colormask. The colormask must be a layer in the same image "
+// "with the layer name colormask"
+// "this procedure is intended for processing with individual per frame colormask "
+// " ",
+// PLUG_IN_AUTHOR,
+// PLUG_IN_COPYRIGHT,
+// GAP_VERSION_WITH_DATE,
+// N_("Apply Colormask in Frame..."),
+// PLUG_IN_IMAGE_TYPES,
+// GIMP_PLUGIN,
+// global_number_in_args,
+// global_number_out_args,
+// in_args,
+// return_vals);
+// gimp_install_procedure (PLUG_IN_NAME_3,
+// "Create or replace a individual per frame colormask as top layer",
+// "The colormask is created as mix of the processed original layer and, "
+// "an initial colormask and /or the colormask from the previous frame. "
+// "(the processed drawable shall be a layer of a frame image)"
+// "xxxxxxx "
+// " ",
+// PLUG_IN_AUTHOR,
+// PLUG_IN_COPYRIGHT,
+// GAP_VERSION_WITH_DATE,
+// N_("Create Colormask..."),
+// PLUG_IN_IMAGE_TYPES,
+// GIMP_PLUGIN,
+// global_number_in_args,
+// global_number_out_args,
+// in_args,
+// return_vals);
+
+ {
+ /* Menu names */
+ const char *menupath_image_video_layer_attr = N_("<Image>/Video/Layer/Attributes/");
+
+ //gimp_plugin_menu_branch_register("<Image>", "Video");
+ //gimp_plugin_menu_branch_register("<Image>/Video", "Layer");
+
+ gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_video_layer_attr);
+// gimp_plugin_menu_register (PLUG_IN_NAME_2, menupath_image_video_layer_attr);
+// gimp_plugin_menu_register (PLUG_IN_NAME_3, menupath_image_video_layer_attr);
+ }
+
+} /* end query */
+
+static void
+run (const gchar *name, /* name of plugin */
+ gint nparams, /* number of in-paramters */
+ const GimpParam * param, /* in-parameters */
+ gint *nreturn_vals, /* number of out-parameters */
+ GimpParam ** return_vals) /* out-parameters */
+{
+ const gchar *l_env;
+ gint32 image_id = -1;
+ gboolean doProgress;
+
+
+
+ /* Get the runmode from the in-parameters */
+ GimpRunMode run_mode = param[0].data.d_int32;
+
+ /* 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[2];
+
+ INIT_I18N();
+
+ l_env = g_getenv("GAP_DEBUG");
+ if(l_env != NULL)
+ {
+ if((*l_env != 'n') && (*l_env != 'N')) gap_debug = 1;
+ }
+
+ if(gap_debug) printf("\n\nDEBUG: run %s\n", name);
+
+
+ doProgress = FALSE;
+
+ /* 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_drawable = -1;
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+
+ /* get image and drawable */
+ image_id = param[1].data.d_int32;
+
+ /* Get drawable information */
+ globalDrawable = gimp_drawable_get (param[2].data.d_drawable);
+
+
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (name, &cmaskvals);
+
+ /* Get information from the dialog */
+ if (!gap_colormask_dialog(&cmaskvals, globalDrawable))
+ {
+ return;
+ }
+ doProgress = TRUE;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* check to see if invoked with the correct number of parameters */
+ if (nparams == global_number_in_args)
+ {
+ cmaskvals.colormask_id = (gint32) param[3].data.d_drawable;
+ cmaskvals.loColorThreshold = (gdouble) param[4].data.d_float;
+ cmaskvals.hiColorThreshold = (gdouble) param[5].data.d_float;
+ cmaskvals.colorSensitivity = (gdouble) param[6].data.d_float;
+ cmaskvals.lowerOpacity = (gdouble) param[7].data.d_float;
+ cmaskvals.upperOpacity = (gdouble) param[8].data.d_float;
+ cmaskvals.triggerAlpha = (gdouble) param[9].data.d_float;
+ cmaskvals.isleRadius = (gdouble) param[10].data.d_float;
+ cmaskvals.isleAreaPixelLimit = (gdouble) param[11].data.d_float;
+ cmaskvals.featherRadius = (gdouble) param[12].data.d_float;
+
+ cmaskvals.thresholdColorArea = (gdouble) param[13].data.d_float;
+ cmaskvals.pixelDiagonal = (gdouble) param[14].data.d_float;
+ cmaskvals.pixelAreaLimit = (gdouble) param[15].data.d_float;
+ cmaskvals.connectByCorner = (param[16].data.d_int32 == 0) ? FALSE : TRUE;
+ cmaskvals.keepLayerMask = (param[17].data.d_int32 == 0) ? FALSE : TRUE;
+ cmaskvals.keepWorklayer = (param[18].data.d_int32 == 0) ? FALSE : TRUE;
+
+ cmaskvals.enableKeyColorThreshold = (param[19].data.d_int32 == 0) ? FALSE : TRUE;
+ cmaskvals.keycolor = param[20].data.d_color;
+ cmaskvals.loKeyColorThreshold = (gdouble) param[21].data.d_float;
+ cmaskvals.keyColorSensitivity = (gdouble) param[22].data.d_float;
+
+ cmaskvals.algorithm = param[23].data.d_int32;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (name, &cmaskvals);
+
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Run the main function */
+ values[1].data.d_drawable = p_colormask_apply_run(image_id, param[2].data.d_drawable, doProgress, name);
+ if (values[1].data.d_drawable < 0)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ /* 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 ();
+
+ /* Store variable states for next run */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (name, &cmaskvals, sizeof (GapColormaskValues));
+
+ if(run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_drawable_flush (globalDrawable);
+ }
+ values[0].data.d_status = status;
+
+
+ gimp_drawable_detach (globalDrawable);
+} /* end run */
+
+
+
+/* ----------------------
+ * p_colormask_apply_run
+ * ----------------------
+ * The processing functions
+ *
+ */
+static gint32
+p_colormask_apply_run (gint32 image_id, gint32 drawable_id, gboolean doProgress, const char *name)
+{
+ gint32 retLayerId;
+
+ if (!gimp_drawable_is_layer(drawable_id))
+ {
+ return (-1); /* dont operate on other drawables than layers */
+ }
+ if (!gimp_drawable_has_alpha(drawable_id))
+ {
+ return (-1); /* dont operate on layers without alpha channel */
+ }
+
+ if (strcmp(name, PLUG_IN_NAME_3) == 0)
+ {
+ retLayerId = gap_create_or_replace_colormask_layer (drawable_id
+ , &cmaskvals
+ , doProgress
+ );
+ }
+ else if (strcmp(name, PLUG_IN_NAME_2) == 0)
+ {
+ retLayerId = gap_colormask_apply_by_name (drawable_id
+ , &cmaskvals
+ , doProgress
+ );
+ }
+ else
+ {
+ retLayerId = gap_colormask_apply_to_layer_of_same_size (drawable_id
+ , &cmaskvals
+ , doProgress
+ );
+ }
+
+ return (retLayerId);
+} /* end p_colormask_apply_run */
+
+
+
+
diff --git a/gap/gap_decode_mplayer.c b/gap/gap_decode_mplayer.c
index 7376a7f..5243241 100644
--- a/gap/gap_decode_mplayer.c
+++ b/gap/gap_decode_mplayer.c
@@ -296,7 +296,7 @@ p_mplayer_info(char *errlist)
/* -----------------------
* p_scann_start_time
* -----------------------
- * scann hour, minute, second from a string
+ * scan hour, minute, second from a string
* that has the format "HH:MM:SS"
* where any other non-digit characters are treated same as ":"
* strings with format "HHMMSS" are also accepted.
diff --git a/gap/gap_edge_detection.c b/gap/gap_edge_detection.c
new file mode 100644
index 0000000..6d198e8
--- /dev/null
+++ b/gap/gap_edge_detection.c
@@ -0,0 +1,421 @@
+/* gap_edge_detection.c
+ * by hof (Wolfgang Hofer)
+ * 2010/08/10
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_edge_detection.h"
+#include "gap_colordiff.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+#define OPACITY_LEVEL_UCHAR 50
+
+
+extern int gap_debug; /* ==0 ... dont print debug infos */
+
+typedef struct GapEdgeContext { /* nickname: ectx */
+ GimpDrawable *refDrawable;
+ GimpDrawable *edgeDrawable;
+ gdouble edgeColordiffThreshold;
+ gint32 edgeOpacityThreshold255;
+ gint32 edgeImageId;
+ gint32 edgeDrawableId;
+ gint32 countEdgePixels;
+
+ GimpPixelFetcher *pftRef;
+
+ } GapEdgeContext;
+
+
+
+/* ---------------------------------
+ * p_edgeProcessingForOneRegion
+ * ---------------------------------
+ * render edge drawable for the current processed pixel region.
+ * (use a pixelfetcher on region boundaries)
+ */
+static void
+p_edgeProcessingForOneRegion (const GimpPixelRgn *refPR
+ ,const GimpPixelRgn *edgePR
+ ,GapEdgeContext *ectx)
+{
+ guint row;
+ guchar* ref = refPR->data;
+ guchar* edge = edgePR->data;
+ guchar rightPixel[4];
+ guchar botPixel[4];
+ guchar rbPixel[4];
+ guchar* rightPixelPtr;
+ guchar* botPixelPtr;
+ guchar* rbPixelPtr;
+ gint32 rx;
+ gint32 ry;
+ gboolean debugPrint;
+
+
+
+ if(gap_debug)
+ {
+ printf("p_edgeProcessingForOneRegion START\n");
+ }
+ debugPrint = FALSE;
+
+ for (row = 0; row < edgePR->h; row++)
+ {
+ guint col;
+ guint idxref;
+ guint idxedge;
+
+ ry = refPR->y + row;
+
+ idxref = 0;
+ idxedge = 0;
+ for(col = 0; col < edgePR->w; col++)
+ {
+ gdouble colordiff1;
+ gdouble colordiff2;
+ gdouble colordiff3;
+ gdouble maxColordiff;
+ gboolean isColorCompareRequired;
+
+ rbPixelPtr = &ref[idxref];
+
+ rx = refPR->x + col;
+
+ isColorCompareRequired = TRUE;
+
+/*
+ * if(gap_debug)
+ * {
+ * debugPrint = FALSE;
+ * if((rx == 596) || (rx == 597))
+ * {
+ * if((ry==818) ||(ry==818))
+ * {
+ * debugPrint = TRUE;
+ * }
+ * }
+ * }
+ */
+
+ if(col < edgePR->w -1)
+ {
+ rightPixelPtr = &ref[idxref + refPR->bpp];
+
+ if(row < edgePR->h -1)
+ {
+ rbPixelPtr = &ref[idxref + refPR->bpp + refPR->rowstride];
+ }
+ }
+ else if(rx >= ectx->refDrawable->width -1)
+ {
+ /* drawable border is not considered as edge */
+ rightPixelPtr = &ref[idxref];
+ }
+ else
+ {
+ rightPixelPtr = &rightPixel[0];
+ gimp_pixel_fetcher_get_pixel (ectx->pftRef, rx +1, ry, rightPixelPtr);
+
+ if(ry >= ectx->refDrawable->height -1)
+ {
+ rbPixelPtr = &rbPixel[0];
+ gimp_pixel_fetcher_get_pixel (ectx->pftRef, rx +1, ry +1, rbPixelPtr);
+ }
+ }
+
+ if(row < edgePR->h -1)
+ {
+ botPixelPtr = &ref[idxref + refPR->rowstride];
+ }
+ else if(ry >= ectx->refDrawable->height -1)
+ {
+ /* drawable border is not considered as edge */
+ botPixelPtr = &ref[idxref];
+ }
+ else
+ {
+ botPixelPtr = &botPixel[0];
+ gimp_pixel_fetcher_get_pixel (ectx->pftRef, rx, ry +1, botPixelPtr);
+ }
+
+ if(refPR->bpp > 3)
+ {
+ /* reference drawable has alpha channel
+ * in this case significant changes of opacity shall detect edge
+ */
+ gint32 maxAlphaDiff;
+
+ maxAlphaDiff = MAX(abs(ref[idxref +3] - rightPixelPtr[3])
+ ,abs(ref[idxref +3] - botPixelPtr[3]));
+ maxAlphaDiff = MAX(maxAlphaDiff
+ ,abs(ref[idxref +3] - rbPixelPtr[3]));
+ if(debugPrint)
+ {
+ printf("rx:%d ry:%d idxref:%d idxedge:%d (maxAlphaDiff):%d Thres:%d Alpha ref:%d right:%d bot:%d rb:%d\n"
+ , (int)rx
+ , (int)ry
+ , (int)idxref
+ , (int)idxedge
+ , (int)maxAlphaDiff
+ , (int)ectx->edgeOpacityThreshold255
+ , (int)ref[idxref +3]
+ , (int)rightPixelPtr[3]
+ , (int)botPixelPtr[3]
+ , (int)rbPixelPtr[3]
+ );
+ }
+
+ if(maxAlphaDiff > ectx->edgeOpacityThreshold255)
+ {
+ ectx->countEdgePixels++;
+ edge[idxedge] = maxAlphaDiff;
+ isColorCompareRequired = FALSE;
+ }
+ else if(ref[idxref +3] < OPACITY_LEVEL_UCHAR)
+ {
+ /* transparent pixel is not considered as edge */
+ edge[idxedge] = 0;
+ isColorCompareRequired = FALSE;
+ }
+
+ }
+
+
+ if(isColorCompareRequired == TRUE)
+ {
+
+ colordiff1 = gap_colordiff_simple_guchar(&ref[idxref]
+ , &rightPixelPtr[0]
+ , debugPrint /* debugPrint */
+ );
+
+ colordiff2 = gap_colordiff_simple_guchar(&ref[idxref]
+ , &botPixelPtr[0]
+ , debugPrint /* debugPrint */
+ );
+
+ colordiff3 = gap_colordiff_simple_guchar(&ref[idxref]
+ , &rbPixelPtr[0]
+ , debugPrint /* debugPrint */
+ );
+ maxColordiff = MAX(colordiff1, colordiff2);
+ maxColordiff = MAX(maxColordiff, colordiff3);
+
+ if(debugPrint)
+ {
+ printf("rx:%d ry:%d colordiff(1): %.5f (2):%.5f (3):%.5f (max):%.5f Thres:%.5f\n"
+ , (int)rx
+ , (int)ry
+ , (float)colordiff1
+ , (float)colordiff2
+ , (float)colordiff3
+ , (float)maxColordiff
+ , (float)ectx->edgeColordiffThreshold
+ );
+ }
+
+ if (maxColordiff < ectx->edgeColordiffThreshold)
+ {
+ edge[idxedge] = 0;
+ }
+ else
+ {
+ gdouble value;
+
+ value = maxColordiff * 255.0;
+ edge[idxedge] = CLAMP(value, 1, 255);
+ ectx->countEdgePixels++;
+ }
+ }
+
+
+
+ idxref += refPR->bpp;
+ idxedge += edgePR->bpp;
+ }
+
+ ref += refPR->rowstride;
+ edge += edgePR->rowstride;
+
+ }
+
+} /* end p_edgeProcessingForOneRegion */
+
+
+
+/* ----------------------------------------
+ * p_createEmptyEdgeDrawable
+ * ----------------------------------------
+ * create the (empty) edge drawable as layer of a new image
+ *
+ */
+static void
+p_createEmptyEdgeDrawable(GapEdgeContext *ectx)
+{
+ ectx->edgeImageId = gimp_image_new(ectx->refDrawable->width
+ , ectx->refDrawable->height
+ , GIMP_GRAY
+ );
+ ectx->edgeDrawableId = gimp_layer_new(ectx->edgeImageId
+ , "edge"
+ , ectx->refDrawable->width
+ , ectx->refDrawable->height
+ , GIMP_GRAY_IMAGE
+ , 100.0 /* full opacity */
+ , 0 /* normal mode */
+ );
+
+ gimp_image_add_layer (ectx->edgeImageId, ectx->edgeDrawableId, 0 /* stackposition */ );
+
+ ectx->edgeDrawable = gimp_drawable_get(ectx->edgeDrawableId);
+
+} /* end p_createEmptyEdgeDrawable */
+
+
+/* ----------------------------------------
+ * p_edgeDetection
+ * ----------------------------------------
+ * setup pixel regions and perform edge detection processing per region.
+ * as result of this processing the edgeDrawable is created and rendered.
+ */
+static void
+p_edgeDetection(GapEdgeContext *ectx)
+{
+ GimpPixelRgn refPR;
+ GimpPixelRgn edgePR;
+ gpointer pr;
+
+ p_createEmptyEdgeDrawable(ectx);
+ if(ectx->edgeDrawable == NULL)
+ {
+ return;
+ }
+
+
+ gimp_pixel_rgn_init (&refPR, ectx->refDrawable, 0, 0
+ , ectx->refDrawable->width, ectx->refDrawable->height
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+
+ gimp_pixel_rgn_init (&edgePR, ectx->edgeDrawable, 0, 0
+ , ectx->edgeDrawable->width, ectx->edgeDrawable->height
+ , TRUE /* dirty */
+ , FALSE /* shadow */
+ );
+
+ /* compare pixel areas in tiled portions via pixel region processing loops.
+ */
+ for (pr = gimp_pixel_rgns_register (2, &refPR, &edgePR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_edgeProcessingForOneRegion (&refPR, &edgePR, ectx);
+ }
+ gimp_drawable_flush (ectx->edgeDrawable);
+}
+
+
+
+/* ----------------------------------------
+ * gap_edgeDetection
+ * ----------------------------------------
+ *
+ * returns the drawable id of a newly created channel
+ * representing edges of the specified image.
+ *
+ * black pixels indicate areas of same or similar colors,
+ * white indicates sharp edges.
+ *
+ */
+gint32 gap_edgeDetection(gint32 refDrawableId
+ , gdouble edgeColordiffThreshold
+ , gint32 *countEdgePixels
+ )
+{
+ GapEdgeContext edgeContext;
+ GapEdgeContext *ectx;
+ gdouble edgeOpacityThreshold;
+
+ /* init context */
+ ectx = &edgeContext;
+ ectx->refDrawable = gimp_drawable_get(refDrawableId);
+ ectx->edgeDrawable = NULL;
+ ectx->edgeColordiffThreshold = CLAMP(edgeColordiffThreshold, 0.0, 1.0);
+ edgeOpacityThreshold = CLAMP((edgeColordiffThreshold * 255), 0, 255);
+ ectx->edgeOpacityThreshold255 = edgeOpacityThreshold;
+ ectx->edgeDrawableId = -1;
+ ectx->countEdgePixels = 0;
+
+ if(gap_debug)
+ {
+ printf("gap_edgeDetection START edgeColordiffThreshold:%.5f refDrawableId:%d\n"
+ , (float)ectx->edgeColordiffThreshold
+ , (int)refDrawableId
+ );
+ }
+
+ if(ectx->refDrawable != NULL)
+ {
+ ectx->pftRef = gimp_pixel_fetcher_new (ectx->refDrawable, FALSE /* shadow */);
+ gimp_pixel_fetcher_set_edge_mode (ectx->pftRef, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+ p_edgeDetection(ectx);
+
+ gimp_pixel_fetcher_destroy (ectx->pftRef);
+ }
+
+ if(ectx->refDrawable != NULL)
+ {
+ gimp_drawable_detach(ectx->refDrawable);
+ ectx->refDrawable = NULL;
+ }
+ if(ectx->edgeDrawable != NULL)
+ {
+ gimp_drawable_detach(ectx->edgeDrawable);
+ ectx->edgeDrawable = NULL;
+ }
+
+ *countEdgePixels = ectx->countEdgePixels;
+
+ if(gap_debug)
+ {
+ printf("gap_edgeDetection END resulting edgeDrawableId:%d countEdgePixels:%d\n"
+ , (int)ectx->edgeDrawableId
+ , (int)*countEdgePixels
+ );
+ }
+
+ return (ectx->edgeDrawableId);
+
+} /* end gap_edgeDetection */
diff --git a/gap/gap_edge_detection.h b/gap/gap_edge_detection.h
new file mode 100644
index 0000000..3379f13
--- /dev/null
+++ b/gap/gap_edge_detection.h
@@ -0,0 +1,63 @@
+// TODOS: levels of non-black edge pixels are too dark (stretch range...)
+// check colordiff (that seems not work properly on draft locate test image coords x:25 y:23 and x:25 y:24 !!!
+
+/* gap_edge_detection.h
+ * by hof (Wolfgang Hofer)
+ * 2010/08/08
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#ifndef _GAP_EDGE_DETECTION_H
+#define _GAP_EDGE_DETECTION_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+
+
+/* ----------------------------------------
+ * gap_edgeDetection
+ * ----------------------------------------
+ *
+ * returns the drawable id of a newly created channel
+ * representing edges of the specified image.
+ *
+ * black pixels indicate areas of same or similar colors,
+ * white indicates sharp edges.
+ *
+ */
+gint32 gap_edgeDetection(gint32 refDrawableId
+ , gdouble edgeColordiffThreshold
+ , gint32 *countEdgePixels
+ );
+
+
+
+
+#endif
diff --git a/gap/gap_fmac_base.c b/gap/gap_fmac_base.c
index 09ed38a..d839cc1 100644
--- a/gap/gap_fmac_base.c
+++ b/gap/gap_fmac_base.c
@@ -277,7 +277,7 @@ p_build_fmac_list(const char *filtermacro_file, GimpRunMode run_mode)
return NULL;
}
- /* process filtermacro file (scann line by line and add filtername
+ /* process filtermacro file (scan line by line and add filtername
* and params to the fmac_root list)
*/
txf_ptr_root = gap_val_load_textfile(filtermacro_file);
@@ -380,7 +380,7 @@ p_merge_fmac_list(FMacElem *fmac_root, const char *filtermacro_file, GimpRunMode
}
- /* process filtermacro file (scann line by line and overwrite
+ /* process filtermacro file (scan line by line and overwrite
* the 2nd parameter set where filtername matches
*/
txf_ptr_root = gap_val_load_textfile(filtermacro_file);
diff --git a/gap/gap_frame_fetcher.c b/gap/gap_frame_fetcher.c
index 3d66a66..d43bc09 100644
--- a/gap/gap_frame_fetcher.c
+++ b/gap/gap_frame_fetcher.c
@@ -89,6 +89,11 @@
#define GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS 6
#define GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED 36
+#define GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS_GIMPRC_KEY "gap_ffetch_max_img_cache_elements"
+#define GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS_GIMPRC_KEY "gap_ffetch_max_gvc_cache_elements"
+#define GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED_GIMPRC_KEY "gap_ffetch_gva_frames_to_keep_cached"
+
+
/* the lists of cached images and duplicates are implemented via GIMP image parasites,
* where images are simply loaded by GIMP without adding a display and marked with a non persistent parasite.
* the GAP_IMAGE_CACHE_PARASITE holds the modification timestamp (mtime) and full filename (inclusive terminating 0)
@@ -137,6 +142,7 @@ static GapFFetchGvahandCache *global_gvcache = NULL;
static GapFFetchResourceUserElem *global_rsource_users = NULL;
+
/*************************************************************
* FRAME FETCHER procedures *
*************************************************************
@@ -153,9 +159,114 @@ static t_GVA_Handle* p_ffetch_get_open_gvahand(const char* filename, gint32 sel
static void p_add_image_to_list_of_duplicated_images(gint32 image_id, gint32 ffetch_user_id);
+static gint32 p_get_ffetch_max_img_cache_elements();
+static gint32 p_get_ffetch_max_gvc_cache_elements();
+static gint32 ffetch_gva_frames_to_keep_cached();
/* ----------------------------------------------------
+ * p_get_ffetch_max_img_cache_elements
+ * ----------------------------------------------------
+ */
+static gint32
+p_get_ffetch_max_img_cache_elements()
+{
+ static gint32 value = -1;
+ static char *gimprc_key = GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS_GIMPRC_KEY;
+
+ if(value == -1)
+ {
+ if(gap_debug)
+ {
+ printf("get gimprc value for %s\n"
+ , gimprc_key
+ );
+ }
+ value = gap_base_get_gimprc_int_value (gimprc_key
+ , GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS /* default_value*/
+ , 1 /* min_value */
+ , 2000 /* max_value */
+ );
+ }
+ if(gap_debug)
+ {
+ printf("value for %s is:%d\n"
+ , gimprc_key
+ ,(int)value
+ );
+ }
+ return (value);
+} /* end p_get_ffetch_max_img_cache_elements */
+
+/* ----------------------------------------------------
+ * p_get_ffetch_max_gvc_cache_elements
+ * ----------------------------------------------------
+ */
+static gint32
+p_get_ffetch_max_gvc_cache_elements()
+{
+ static gint32 value = -1;
+ static char *gimprc_key = GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS_GIMPRC_KEY;
+
+ if(value == -1)
+ {
+ if(gap_debug)
+ {
+ printf("get gimprc value for %s\n"
+ , gimprc_key
+ );
+ }
+ value = gap_base_get_gimprc_int_value (gimprc_key
+ , GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS /* default_value*/
+ , 1 /* min_value */
+ , 80 /* max_value */
+ );
+ }
+ if(gap_debug)
+ {
+ printf("value for %s is:%d\n"
+ , gimprc_key
+ ,(int)value
+ );
+ }
+ return (value);
+} /* end p_get_ffetch_max_gvc_cache_elements */
+
+/* ----------------------------------------------------
+ * p_get_ffetch_gva_frames_to_keep_cached
+ * ----------------------------------------------------
+ */
+static gint32
+p_get_ffetch_gva_frames_to_keep_cached()
+{
+ static gint32 value = -1;
+ static char *gimprc_key = GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED_GIMPRC_KEY;
+
+ if(value == -1)
+ {
+ if(gap_debug)
+ {
+ printf("get gimprc value for %s\n"
+ , gimprc_key
+ );
+ }
+ value = gap_base_get_gimprc_int_value (gimprc_key
+ , GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED /* default_value*/
+ , 1 /* min_value */
+ , 1000 /* max_value */
+ );
+ }
+ if(gap_debug)
+ {
+ printf("value for %s is:%d\n"
+ , gimprc_key
+ ,(int)value
+ );
+ }
+ return (value);
+} /* end p_get_ffetch_gva_frames_to_keep_cached */
+
+/* ----------------------------------------------------
* p_load_cache_image
* ----------------------------------------------------
*/
@@ -264,7 +375,7 @@ p_load_cache_image(const char* filename, gboolean addToCache)
gchar *parasite_filename_ptr;
gint32 len_filename0; /* filename length including the terminating 0 */
- if (l_number_of_cached_images > GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS)
+ if (l_number_of_cached_images > p_get_ffetch_max_img_cache_elements())
{
/* the image cache already has more elements than desired,
* drop the 1st cached image
@@ -273,7 +384,7 @@ p_load_cache_image(const char* filename, gboolean addToCache)
{
printf("FrameFetcher: DELETE because cache is full: (image_id:%d) name:%s number_of_cached_images:%d pid:%d\n"
, (int)l_first_chached_image_id
- , gimp_image_get_filename(images[l_idi])
+ , gimp_image_get_filename(l_first_chached_image_id)
, (int)l_number_of_cached_images
, (int)gap_base_getpid()
);
@@ -473,7 +584,7 @@ p_ffetch_get_open_gvahand(const char* filename, gint32 seltrack, const char *pre
/* init the global_image cache */
global_gvcache = g_malloc0(sizeof(GapFFetchGvahandCache));
global_gvcache->gvc_list = NULL;
- global_gvcache->max_vid_cache = GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS;
+ global_gvcache->max_vid_cache = p_get_ffetch_max_gvc_cache_elements();
}
gvcache = global_gvcache;
@@ -510,7 +621,7 @@ p_ffetch_get_open_gvahand(const char* filename, gint32 seltrack, const char *pre
if(l_gvahand)
{
- GVA_set_fcache_size(l_gvahand, GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED);
+ GVA_set_fcache_size(l_gvahand, p_get_ffetch_gva_frames_to_keep_cached());
gvc_new = g_malloc0(sizeof(GapFFetchGvahandCacheElem));
gvc_new->filename = g_strdup(filename);
@@ -676,6 +787,8 @@ gap_frame_fetch_dup_image(gint32 ffetch_user_id
if (stackpos < 0)
{
dup_image_id = gimp_image_duplicate(image_id);
+
+ gap_frame_fetch_remove_parasite(dup_image_id);
resulting_layer = gap_image_merge_visible_layers(dup_image_id, GIMP_CLIP_TO_IMAGE);
}
else
@@ -779,7 +892,7 @@ gap_frame_fetch_dup_video(gint32 ffetch_user_id
/* if no success, we try explicite read that frame */
if(gvahand->current_seek_nr != framenr)
{
- if(((gvahand->current_seek_nr + GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED) > framenr)
+ if(((gvahand->current_seek_nr + p_get_ffetch_gva_frames_to_keep_cached()) > framenr)
&& (gvahand->current_seek_nr < framenr ) )
{
/* near forward seek is performed by dummyreads to fill up the
@@ -949,3 +1062,30 @@ gap_frame_fetch_unregister_user(gint32 ffetch_user_id)
}
} /* end gap_frame_fetch_unregister_user */
+
+
+/* -------------------------------
+ * gap_frame_fetch_remove_parasite
+ * -------------------------------
+ * removes the image parasite that marks the image as member
+ * of the gap frame fetcher cache.
+ */
+void
+gap_frame_fetch_remove_parasite(gint32 image_id)
+{
+ GimpParasite *l_parasite;
+
+ l_parasite = gimp_image_parasite_find(image_id, GAP_IMAGE_CACHE_PARASITE);
+
+ if(l_parasite)
+ {
+ gimp_image_parasite_detach(image_id, GAP_IMAGE_CACHE_PARASITE);
+ if(gap_debug)
+ {
+ printf("FrameFetcher: removed parasite from (image_id:%d) pid:%d\n"
+ , (int)image_id, (int)gap_base_getpid());
+ }
+ gimp_parasite_free(l_parasite);
+ }
+
+} /* end gap_frame_fetch_remove_parasite */
diff --git a/gap/gap_frame_fetcher.h b/gap/gap_frame_fetcher.h
index 8fce9b7..2c7c553 100644
--- a/gap/gap_frame_fetcher.h
+++ b/gap/gap_frame_fetcher.h
@@ -92,6 +92,9 @@ gap_frame_fetch_delete_list_of_duplicated_images(gint32 ffetch_user_id);
* ----------------------------
* returns image_id of the original cached image.
* RESTRICTION: the Caller must NOT not modify that image and shall not open a display for it!
+ * In case this image is duplicated, the parasite that marks an image as member of the gap frame fetcher cache
+ * must be removed (by calling procedure gap_frame_fetch_remove_parasite on the duplicate)
+ * otherwise the duplicate might be unexpectedly deleted when the frame fetcher cache is full.
*/
gint32
gap_frame_fetch_orig_image(gint32 ffetch_user_id
@@ -133,5 +136,13 @@ gap_frame_fetch_dup_video(gint32 ffetch_user_id
);
+/* -------------------------------
+ * gap_frame_fetch_remove_parasite
+ * -------------------------------
+ * removes the image parasite that marks the image as member
+ * of the gap frame fetcher cache.
+ */
+void
+gap_frame_fetch_remove_parasite(gint32 image_id);
#endif
diff --git a/gap/gap_layer_copy.c b/gap/gap_layer_copy.c
index 7567449..f8a2fe4 100644
--- a/gap/gap_layer_copy.c
+++ b/gap/gap_layer_copy.c
@@ -847,3 +847,41 @@ gap_layer_create_layer_from_alpha(gint32 src_layer_id, gint32 image_id
} /* end gap_layer_create_layer_from_alpha */
+/* ---------------------------------
+ * gap_layer_find_by_name
+ * ---------------------------------
+ * return -1 if the specified image has no layer with the specified name
+ */
+gint32
+gap_layer_find_by_name(gint32 image_id, const char *name)
+{
+ gint l_nlayers;
+ gint32 *l_layers_list;
+ gint32 l_layer_id;
+
+ l_layer_id = -1;
+ l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+ if(l_layers_list != NULL)
+ {
+ gint ii;
+ for(ii=0; ii < l_nlayers; ii++)
+ {
+ char *layername;
+ gboolean isEqual;
+
+ layername = gimp_drawable_get_name(l_layers_list[ii]);
+ isEqual = (strcmp(layername, name) == 0);
+ g_free(layername);
+
+ if (isEqual)
+ {
+ l_layer_id = l_layers_list[ii];
+ break;
+ }
+ }
+ g_free (l_layers_list);
+ }
+
+ return (l_layer_id);
+
+} /* end gap_layer_find_by_name */
diff --git a/gap/gap_layer_copy.h b/gap/gap_layer_copy.h
index de2318d..3e04bf2 100644
--- a/gap/gap_layer_copy.h
+++ b/gap/gap_layer_copy.h
@@ -93,4 +93,6 @@ gint32 gap_layer_create_layer_from_alpha(gint32 src_layer_id, gint32 image_id
, const char *name_prefix, const char *name_suffix
, gboolean applyExistingLayermask, gboolean useTransferAlpha);
+gint32 gap_layer_find_by_name(gint32 image_id, const char *name);
+
#endif
diff --git a/gap/gap_lib.c b/gap/gap_lib.c
index 76fe9df..04fcf9d 100644
--- a/gap/gap_lib.c
+++ b/gap/gap_lib.c
@@ -528,7 +528,7 @@ p_do_active_layer_tracking(gint32 image_id
/* ============================================================================
* gap_lib_file_exists
*
- * return 0 ... file does not exist, or is not accessable by user,
+ * return 0 ... file does not exist, or is not accessible by user,
* or is empty,
* or is not a regular file
* 1 ... file exists
@@ -883,6 +883,39 @@ gap_lib_alloc_extension(const char *imagename)
return(l_ext);
}
+/* -------------------------------------
+ * gap_lib_build_basename_without_ext
+ * -------------------------------------
+ * return a duplicate of the basename part of the specified filename without extension.
+ * leading directory path and drive letter (for WinOS) is cut off
+ * the caller is responsible to g_free the returned string.
+ */
+char *
+gap_lib_build_basename_without_ext(const char *filename)
+{
+ char *basename;
+ gint idx;
+
+ basename = g_filename_display_basename(filename);
+ idx = strlen(basename) -1;
+ while(idx > 0)
+ {
+ if(basename[idx] == '.')
+ {
+ basename[idx] = '\0';
+ break;
+ }
+ if((basename[idx] == G_DIR_SEPARATOR) || (basename[idx] == DIR_ROOT))
+ {
+ break; /* dont run into dir part */
+ }
+
+ idx--;
+ }
+ return(basename);
+} /* end gap_lib_build_basename_without_ext */
+
+
/* ----------------------------------
* gap_lib_alloc_fname_fixed_digits
@@ -2611,14 +2644,14 @@ gap_lib_load_named_frame (gint32 old_image_id, char *lod_name)
if (gap_pdb_gimp_displays_reconnect(old_image_id, l_new_image_id))
{
- /* deleteing the old image if it is still alive
+ /* deleting the old image if it is still alive
* (maybe still required gimp-2.2.x prior to gimp-2.2.11
* gimp-2.2.11 still accepts the delete, but the old image becomes invalid after
* reconnecting the display.
* gimp-2.3.8 even complains and breaks gimp-gap if we attempt to delete
* the old image. (see #339840)
- * GAP has no more chance for explicite delete the old image
- * (hope that this is already done implicite by gimp_reconnect_displays ?)
+ * GAP has no more chance of explicitly deleting the old image
+ * (hope that this is already done implicitly by gimp_reconnect_displays ?)
*/
if(gap_image_is_alive(old_image_id))
diff --git a/gap/gap_lib.h b/gap/gap_lib.h
index d92d093..8666384 100644
--- a/gap/gap_lib.h
+++ b/gap/gap_lib.h
@@ -66,6 +66,7 @@ GapAnimInfo* gap_lib_alloc_ainfo(gint32 image_id, GimpRunMode run_mode);
int gap_lib_dir_ainfo(GapAnimInfo *ainfo_ptr);
int gap_lib_chk_framerange(GapAnimInfo *ainfo_ptr);
int gap_lib_chk_framechange(GapAnimInfo *ainfo_ptr);
+char* gap_lib_build_basename_without_ext(const char *filename);
int gap_lib_save_named_frame (gint32 image_id, char *sav_name);
int gap_lib_load_named_frame (gint32 image_id, char *lod_name);
diff --git a/gap/gap_libgimpgap.h b/gap/gap_libgimpgap.h
index 74c2121..acf7882 100644
--- a/gap/gap_libgimpgap.h
+++ b/gap/gap_libgimpgap.h
@@ -36,6 +36,11 @@
#include "gap-intl.h"
#include "gap_arr_dialog.h"
+#include "gap_colordiff.h"
+#include "gap_locate.h"
+#include "gap_edge_detection.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_exec.h"
#include "gap_image.h"
#include "gap_layer_copy.h"
#include "gap_lib.h"
diff --git a/gap/gap_locate.c b/gap/gap_locate.c
new file mode 100644
index 0000000..7694dfa
--- /dev/null
+++ b/gap/gap_locate.c
@@ -0,0 +1,843 @@
+/* gap_locate.c
+ * by hof (Wolfgang Hofer)
+ * 2010/08/06
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_locate.h"
+#include "gap_colordiff.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+#define MAX_SHAPE_TABLE_SIZE (1 + (GAP_LOCATE_MAX_REF_SHAPE_RADIUS * 2))
+#define OPACITY_LEVEL_UCHAR 50
+#define COLORDIFF_BREAK_THRESHOLD_FACTOR 1.1
+
+extern int gap_debug; /* ==0 ... dont print debug infos */
+
+typedef struct GapLocateColor {
+ guchar rgba[4]; /* the color channels */
+ } GapLocateColor;
+
+
+typedef struct GapLocateContext { /* nickname: lctx */
+ GimpDrawable *refDrawable;
+ GimpDrawable *targetDrawable;
+ gint32 refX;
+ gint32 refY;
+ gint32 refShapeRadius;
+ gint32 targetMoveRadius;
+ gint32 targetX;
+ gint32 targetY;
+ gdouble minColordiff;
+ gdouble breakAtColordiff;
+ gdouble refPixelColordiffThreshold;
+ gdouble edgeColordiffThreshold;
+ gdouble tmpColordiffBreakThreshold; /* cancel area compare if average colordiff goes above this threshold */
+
+
+ /* common clip rectangle offset in the tareget drawable */
+ gint32 targetOffX;
+ gint32 targetOffY;
+
+ /* transformation offsets to convert target coordinates (tx/ty) to indexes for refColorTab */
+ gint32 transformOffX;
+ gint32 transformOffY;
+
+ /* common clip rectangle sizes */
+ gint32 commonAreaWidth;
+ gint32 commonAreaHeight;
+
+ gdouble commonAreaSumColordiff;
+ gint32 commonAreaComparedPixelCount;
+
+ GimpPixelFetcher *pftRef;
+ GimpPixelFetcher *pftTarget;
+ GapLocateColor *refColorPtr;
+ GapLocateColor refColorTab[MAX_SHAPE_TABLE_SIZE][MAX_SHAPE_TABLE_SIZE];
+
+ gint32 pickedTryNumber;
+ gint32 pickedPixelCount;
+ gint32 countTries;
+ gint32 countAreaCompares;
+
+ gboolean cancelAttemptFlag;
+
+ } GapLocateContext;
+
+
+
+
+/* ---------------------------------
+ * p_debugPrintReferenceTable
+ * ---------------------------------
+ */
+static void
+p_debugPrintReferenceTable(GapLocateContext *lctx)
+{
+ gint32 rx;
+ gint32 ry;
+ gint32 tabSizeUsed;
+
+ tabSizeUsed = 1 +(2 * lctx->refShapeRadius);
+ printf("\nopacity of refColorTab (usedSize)\n+");
+ for(rx = 0; rx < tabSizeUsed; rx++)
+ {
+ if(rx == lctx->refShapeRadius)
+ {
+ printf("+");
+ }
+ else
+ {
+ printf("-");
+ }
+ }
+ printf("\n");
+
+
+ for(ry = 0; ry < tabSizeUsed; ry++)
+ {
+ if(ry == lctx->refShapeRadius)
+ {
+ printf("+");
+ }
+ else
+ {
+ printf("|");
+ }
+ for(rx = 0; rx < tabSizeUsed; rx++)
+ {
+ GapLocateColor *currColorPtr;
+ currColorPtr = &lctx->refColorTab[rx][ry];
+
+ if(currColorPtr->rgba[3] < OPACITY_LEVEL_UCHAR)
+ {
+ printf(".");
+ }
+ else
+ {
+ if((ry == lctx->refShapeRadius) && (rx == lctx->refShapeRadius))
+ {
+ printf("o");
+ }
+ else
+ {
+ printf("x");
+ }
+ }
+
+ }
+ printf("\n");
+ }
+} /* end p_debugPrintReferenceTable */
+
+/* ---------------------------------
+ * p_GapLocateColor_to_GimpHsv
+ * ---------------------------------
+ * check if the reference shape has more than one single color
+ * and has sufficient number of non transparent pixels
+ * (otherwise area compare loops have no chance to deliver
+ * acceptable locating results)
+ */
+static void
+p_GapLocateColor_to_GimpHsv(GapLocateColor *colorPtr, GimpHSV *hsvPtr)
+{
+ GimpRGB rgb;
+
+ gimp_rgba_set_uchar (&rgb, colorPtr->rgba[0], colorPtr->rgba[1], colorPtr->rgba[2], 255);
+ gimp_rgb_to_hsv(&rgb, hsvPtr);
+
+}
+
+
+/* ---------------------------------
+ * p_trimReferenceShape
+ * ---------------------------------
+ * check if the reference shape has more than one single color
+ * and has sufficient number of non transparent pixels
+ * (otherwise area compare loops have no chance to deliver
+ * acceptable locating results)
+ */
+static void
+p_trimReferenceShape(GapLocateContext *lctx)
+{
+ GimpHSV refHsv;
+ gint32 tabSizeUsed;
+ gint32 rx;
+ gint32 ry;
+ gboolean isRefPixelColor;
+
+ p_GapLocateColor_to_GimpHsv(lctx->refColorPtr, &refHsv);
+ isRefPixelColor = ((refHsv.s >= 0.25) && (refHsv.v >= 0.1));
+
+ tabSizeUsed = 1 +(2 * lctx->refShapeRadius);
+ for(ry = 0; ry < tabSizeUsed; ry++)
+ {
+ for(rx = 0; rx < tabSizeUsed; rx++)
+ {
+ GimpHSV currentHsv;
+ GapLocateColor *currColorPtr;
+ gdouble vDif;
+
+ currColorPtr = &lctx->refColorTab[rx][ry];
+ p_GapLocateColor_to_GimpHsv(currColorPtr, ¤tHsv);
+
+ vDif = fabs(refHsv.v - currentHsv.v);
+
+ if(isRefPixelColor)
+ {
+ gdouble hDif;
+ gdouble sDif;
+
+ hDif = fabs(refHsv.h - currentHsv.h);
+ /* normalize hue difference.
+ * hue values represents an angle
+ * where value 0.5 equals 180 degree
+ * and value 1.0 stands for 360 degree that is
+ * equal to 0.0
+ * Hue is maximal different at 180 degree.
+ *
+ * after normalizing, the difference
+ * hDiff value 1.0 represents angle difference of 180 degree
+ */
+ if(hDif > 0.5)
+ {
+ hDif = (1.0 - hDif) * 2.0;
+ }
+ else
+ {
+ hDif = hDif * 2.0;
+ }
+
+ if((hDif > 0.5) || (vDif > 0.6))
+ {
+ currColorPtr->rgba[3] = 0;
+ }
+
+ }
+ else
+ {
+ if(vDif > 0.5)
+ {
+ currColorPtr->rgba[3] = 0;
+ }
+ }
+
+
+ }
+ }
+} /* end p_trimReferenceShape */
+
+
+/* ---------------------------------
+ * p_initReferenceTable
+ * ---------------------------------
+ * init the reference table refColorTab that represents
+ * the reference shape as array of colors with alpha channel.
+ *
+ * the used size of the reference table depends on the refShapeRadius,
+ * but not on the reference coordinates lctx->refX/Y
+ * if the reference coordinate is near the edges some pixels that refere
+ * to coordinates outside the reference image boundaries will be initialized as transparent
+ */
+static void
+p_initReferenceTable(GapLocateContext *lctx)
+{
+ gint32 tabSizeUsed;
+ gint32 rx;
+ gint32 ry;
+ gint32 refOffX;
+ gint32 refOffY;
+
+ tabSizeUsed = 1 +(2 * lctx->refShapeRadius);
+ refOffX = lctx->refX - lctx->refShapeRadius;
+ refOffY = lctx->refY - lctx->refShapeRadius;
+
+ for(ry = 0; ry < tabSizeUsed; ry++)
+ {
+ for(rx = 0; rx < tabSizeUsed; rx++)
+ {
+ GapLocateColor *currColorPtr;
+ gint32 xx;
+ gint32 yy;
+
+ currColorPtr = &lctx->refColorTab[rx][ry];
+ xx = refOffX + rx;
+ yy = refOffY + ry;
+
+ if ((xx < 0) || (xx >= lctx->refDrawable->width)
+ || (yy < 0) || (yy >= lctx->refDrawable->height))
+ {
+ /* use full transparent black pixel for coordinates outside boundaries */
+ currColorPtr->rgba[0] = 0;
+ currColorPtr->rgba[1] = 0;
+ currColorPtr->rgba[2] = 0;
+ currColorPtr->rgba[3] = 0;
+ }
+ else
+ {
+ gimp_pixel_fetcher_get_pixel (lctx->pftRef
+ , refOffX + rx
+ , refOffY + ry
+ , &currColorPtr->rgba[0]
+ );
+ }
+
+ }
+ }
+
+ p_trimReferenceShape(lctx);
+
+} /* end p_initReferenceTable */
+
+
+
+
+
+/* ---------------------------------
+ * p_compareAreaRegion
+ * ---------------------------------
+ * calculate summaryColordiff for all opaque pixels
+ * in the compared area region.
+ */
+static void
+p_compareAreaRegion (const GimpPixelRgn *targetPR
+ ,GapLocateContext *lctx)
+{
+ guint row;
+ guchar* target = targetPR->data; /* the target drawable */
+
+ if(lctx->cancelAttemptFlag == TRUE)
+ {
+ return;
+ }
+ for (row = 0; row < targetPR->h; row++)
+ {
+ guint col;
+ guint idxtarget;
+
+ idxtarget = 0;
+ for(col = 0; col < targetPR->w; col++)
+ {
+ GapLocateColor *referenceColorPtr;
+ gint32 rx;
+ gint32 ry;
+ gint32 tx;
+ gint32 ty;
+
+ if(targetPR->bpp > 3)
+ {
+ if(target[idxtarget +3] < OPACITY_LEVEL_UCHAR)
+ {
+ /* transparent target pixel is not compared */
+ idxtarget += targetPR->bpp;
+ continue;
+ }
+ }
+
+ tx = targetPR->x + col;
+ ty = targetPR->y + row;
+
+ rx = CLAMP(tx - lctx->transformOffX, 0, MAX_SHAPE_TABLE_SIZE -1);
+ ry = CLAMP(ty - lctx->transformOffY, 0, MAX_SHAPE_TABLE_SIZE -1);
+
+ referenceColorPtr = &lctx->refColorTab[rx][ry];
+
+ if(referenceColorPtr->rgba[3] >= OPACITY_LEVEL_UCHAR)
+ {
+ gdouble colordiff;
+
+ colordiff = gap_colordiff_simple_guchar(&referenceColorPtr->rgba[0]
+ , &target[idxtarget +0]
+ , FALSE /*gboolean debugPrint*/
+ );
+ lctx->commonAreaComparedPixelCount += 1;
+ lctx->commonAreaSumColordiff += colordiff;
+
+
+
+ if(gap_debug)
+ {
+ printf("try:(%d) pixel at ref: %d/%d target:%d/%d colordiff:%.5f\n"
+ , (int)lctx->countTries
+ , (int)rx
+ , (int)ry
+ , (int)tx
+ , (int)ty
+ , (float)colordiff
+ );
+ }
+
+ if(((lctx->commonAreaComparedPixelCount >> 3) & 7) == 7)
+ {
+ gdouble tmpAvgColordiff;
+
+ tmpAvgColordiff = lctx->commonAreaSumColordiff / (gdouble)lctx->commonAreaComparedPixelCount;
+ if(tmpAvgColordiff > lctx->tmpColordiffBreakThreshold)
+ {
+ lctx->cancelAttemptFlag = TRUE;
+ if(gap_debug)
+ {
+ printf("try:(%d) cancelAttemptFlag set TRUE\n"
+ , (int)lctx->countTries
+ );
+ }
+ return;
+ }
+ }
+
+
+ }
+
+ idxtarget += targetPR->bpp;
+ }
+
+ target += targetPR->rowstride;
+
+ }
+
+} /* end p_compareAreaRegion */
+
+
+
+
+/* ----------------------------------------
+ * p_calculateCommonAreaClipping
+ * ----------------------------------------
+ * check clipping and calculate commonAreaWidth and Height
+ * and offsets in target drawable
+ *
+ * <--- leftRadius --->o<------- rightRadius ------->
+ *
+ */
+static void
+p_calculateCommonAreaClipping(GapLocateContext *lctx, gint32 offX, gint32 offY
+ , gint32 targetX, gint32 targetY
+ )
+{
+ gint32 commonWidth;
+ gint32 commonHeight;
+ gint32 leftRefRadius;
+ gint32 topRefRadius;
+ gint32 rightRefRadius;
+ gint32 botRefRadius;
+ gint32 leftTargetX;
+ gint32 topTargetY;
+ gint32 rightTargetX;
+ gint32 botTargetY;
+ gint32 leftTargetRadius;
+ gint32 topTargetRadius;
+ gint32 rightTargetRadius;
+ gint32 botTargetRadius;
+
+ commonWidth = MIN(lctx->refDrawable->width, lctx->targetDrawable->width);
+ commonHeight = MIN(lctx->refDrawable->height, lctx->targetDrawable->height);
+
+ leftRefRadius = lctx->refShapeRadius;
+ topRefRadius = lctx->refShapeRadius;
+
+ rightRefRadius = lctx->refShapeRadius;
+ botRefRadius = lctx->refShapeRadius;
+
+
+
+ leftTargetX = CLAMP((targetX - leftRefRadius), 0, commonWidth -1);
+ topTargetY = CLAMP((targetY - topRefRadius), 0, commonHeight -1);
+
+ rightTargetX = CLAMP((targetX + rightRefRadius), 0, commonWidth -1);
+ botTargetY = CLAMP((targetY + botRefRadius), 0, commonHeight -1);
+
+ leftTargetRadius = targetX - leftTargetX;
+ topTargetRadius = targetY - topTargetY;
+
+ rightTargetRadius = rightTargetX - targetX;
+ botTargetRadius = botTargetY - targetY;
+
+
+ lctx->targetOffX = leftTargetX;
+ lctx->targetOffY = topTargetY;
+ lctx->commonAreaWidth = 1 + leftTargetRadius + rightTargetRadius;
+ lctx->commonAreaHeight = 1 + topTargetRadius + botTargetRadius;
+
+ lctx->transformOffX = targetX - lctx->refShapeRadius;
+ lctx->transformOffY = targetY - lctx->refShapeRadius;
+
+ if(gap_debug)
+ {
+ printf("refX/Y: %d / %d offX/Y: %d / %d targetX/Y %d / %d leftTargetX:%d topTargetY:%d rightTargetX:%d botTargetY:%d"
+ " leftTargetRadius:%d topTargetRadius:%d rightTargetRadius:%d botTargetRadius %d\n"
+ , (int) lctx->refX
+ , (int) lctx->refY
+ , (int) offX
+ , (int) offY
+ , (int) targetX
+ , (int) targetY
+ , (int) leftTargetX
+ , (int) topTargetY
+ , (int) rightTargetX
+ , (int) botTargetY
+ , (int) leftTargetRadius
+ , (int) topTargetRadius
+ , (int) rightTargetRadius
+ , (int) botTargetRadius
+ );
+
+ printf("transformOffX:%d transformOffY:%d targetOffX/Y: %d / %d commonArea W/H: %d x %d\n"
+ , (int)lctx->transformOffX
+ , (int)lctx->transformOffY
+ , (int)lctx->targetOffX
+ , (int)lctx->targetOffY
+ , (int)lctx->commonAreaWidth
+ , (int)lctx->commonAreaHeight
+ );
+ }
+
+} /* end p_calculateCommonAreaClipping */
+
+
+/* ----------------------------------------
+ * p_compareArea
+ * ----------------------------------------
+ *
+ * +---+---+
+ * | | |
+ * | ..|...|
+ * | ..|...|
+ * +---o---+ ----------
+ * | ..|...| ^
+ * | ..|...| | refShapeRadius
+ * | ..|...| v
+ * +---+---+ -----------
+ *
+ *
+ */
+static void p_compareArea(GapLocateContext *lctx, gint32 offX, gint32 offY)
+{
+#define COMMON_AREA_MIN_EDGE_LENGTH 4
+#define COMMON_AREA_MIN_PIXELS_COMPARED 12
+
+ GimpPixelRgn targetPR;
+ gpointer pr;
+ gint32 targetX;
+ gint32 targetY;
+ guchar pixel[4];
+ gdouble colordiff;
+
+ lctx->countTries++;
+
+ targetX = lctx->refX + offX;
+ if(targetX < 0)
+ {
+ return;
+ }
+ if(targetX > lctx->targetDrawable->width -1)
+ {
+ return;
+ }
+ targetY = lctx->refY + offY;
+ if(targetY < 0)
+ {
+ return;
+ }
+ if(targetY > lctx->targetDrawable->height -1)
+ {
+ return;
+ }
+ gimp_pixel_fetcher_get_pixel (lctx->pftTarget, targetX, targetY, &pixel[0]);
+ colordiff = gap_colordiff_simple_guchar(&lctx->refColorPtr->rgba[0]
+ , &pixel[0]
+ , FALSE /*gboolean debugPrint*/
+ );
+ if(colordiff > lctx->refPixelColordiffThreshold)
+ {
+ /* the coresponding pixel in the target drawable at
+ * current offsets differs more than tolerated threshold.
+ * in this case skip full area comare for performance reasons
+ */
+ if(gap_debug)
+ {
+ printf("p_compareArea ref pixel too much Different, SKIP AREA compare at offsets offX:%d offY:%d refPixelColordiffThreshold:%.5f colordiff:%.5f\n"
+ ,(int)offX
+ ,(int)offY
+ ,(float)lctx->refPixelColordiffThreshold
+ ,(float)colordiff
+ );
+ }
+ return;
+ }
+
+
+ p_calculateCommonAreaClipping(lctx, offX, offY, targetX, targetY);
+
+ if((lctx->commonAreaWidth < COMMON_AREA_MIN_EDGE_LENGTH)
+ || (lctx->commonAreaHeight < COMMON_AREA_MIN_EDGE_LENGTH))
+ {
+ /* common area is too small to locate image details
+ * (comparing very few or single pixels does not make sense
+ * it would indicate matching area at unexpected coordinates)
+ *
+ */
+ if(gap_debug)
+ {
+ printf("p_compareArea is too small at offsets offX:%d offY:%d\n"
+ ,(int)offX
+ ,(int)offY
+ );
+ }
+ return;
+ }
+
+
+ lctx->commonAreaSumColordiff = 0.0;
+ lctx->commonAreaComparedPixelCount = 0;
+ lctx->cancelAttemptFlag = FALSE;
+
+ gimp_pixel_rgn_init (&targetPR, lctx->targetDrawable, lctx->targetOffX, lctx->targetOffY
+ , lctx->commonAreaWidth, lctx->commonAreaHeight
+ , FALSE /* dirty */
+ , FALSE /* shadow */
+ );
+
+ /* compare pixel areas in tiled portions via pixel region processing loops.
+ */
+ for (pr = gimp_pixel_rgns_register (1, &targetPR);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ p_compareAreaRegion (&targetPR, lctx);
+ }
+
+ lctx->countAreaCompares++;
+
+ if ((lctx->commonAreaComparedPixelCount >= COMMON_AREA_MIN_PIXELS_COMPARED)
+ && (lctx->cancelAttemptFlag != TRUE))
+ {
+ gdouble commonAreaAvgColordiff;
+
+ commonAreaAvgColordiff = lctx->commonAreaSumColordiff / (gdouble)lctx->commonAreaComparedPixelCount;
+
+ if(gap_debug)
+ {
+ printf("p_compareArea try:(%d) done at offsets offX:%d offY:%d sum:%.5f, count:%.0f commonAreaAvgColordiff:%.5f\n"
+ ,(int)lctx->countTries
+ ,(int)offX
+ ,(int)offY
+ ,(float)lctx->commonAreaSumColordiff
+ ,(float)lctx->commonAreaComparedPixelCount
+ ,(float)commonAreaAvgColordiff
+ );
+ }
+
+
+ if ((commonAreaAvgColordiff < lctx->minColordiff)
+ || ((commonAreaAvgColordiff == lctx->minColordiff) && (lctx->commonAreaComparedPixelCount > lctx->pickedPixelCount)))
+ {
+ /* found best matching common area (so far) at current offests
+ * set minColordiff and result targertX/Y coordinates.
+ * (processing may continue to find better matching result
+ * at other offsets later on..)
+ */
+ lctx->minColordiff = commonAreaAvgColordiff;
+ lctx->tmpColordiffBreakThreshold = lctx->minColordiff * COLORDIFF_BREAK_THRESHOLD_FACTOR;
+ lctx->targetX = targetX;
+ lctx->targetY = targetY;
+ lctx->pickedTryNumber = lctx->countTries;
+ lctx->pickedPixelCount = lctx->commonAreaComparedPixelCount;
+
+ }
+
+ }
+
+} /* end p_compareArea */
+
+
+
+/* ----------------------------------------
+ * p_locateDetailLoop
+ * ----------------------------------------
+ * calls area comarison by varying offsets within targetMoveRadius
+ * starting at offsets 0/0 to find the offsets where reference matches best with target.
+ * break the loop at first perfect match (where minColordiff <= breakAtColordiff level)
+ */
+static void p_locateDetailLoop(GapLocateContext *lctx)
+{
+ gint32 offX;
+ gint32 offY;
+
+ for(offX = 0; offX < lctx->targetMoveRadius; offX++)
+ {
+ for(offY = 0; offY < lctx->targetMoveRadius; offY++)
+ {
+ p_compareArea(lctx, offX, offY);
+ if(lctx->minColordiff <= lctx->breakAtColordiff)
+ {
+ return;
+ }
+
+ if(offX != 0)
+ {
+ p_compareArea(lctx, 0 - offX, offY);
+ if(lctx->minColordiff <= lctx->breakAtColordiff)
+ {
+ return;
+ }
+ if(offY != 0)
+ {
+ p_compareArea(lctx, 0 - offX, 0 - offY);
+ if(lctx->minColordiff <= lctx->breakAtColordiff)
+ {
+ return;
+ }
+ }
+ }
+
+ if(offY != 0)
+ {
+ p_compareArea(lctx, offX, 0 - offY);
+ if(lctx->minColordiff <= lctx->breakAtColordiff)
+ {
+ return;
+ }
+ }
+
+ }
+ }
+} /* end p_locateDetailLoop */
+
+
+
+/* ----------------------------------------
+ * gap_locateDetailWithinRadius
+ * ----------------------------------------
+ *
+ * the locateDetailWithinRadius procedure takes a referenced detail
+ * specified via refX/Y coordinate, refShapeRadius within a
+ * reference drawable
+ * and tries to locate the same (or similar) detail coordinates
+ * in the target Drawable.
+ *
+ * This is done by comparing pixel areas at refShapeRadius
+ * in the corresponding target Drawable in a loop while
+ * varying offsets within targetMoveRadius.
+ * the targetX/Y koords are picked at those offsets where the compared areas
+ * are best matching (e.g with minimun color difference)
+ * the return value is the minimum colordifference value
+ * (in range 0.0 to 1.0 where 0.0 indicates that the compared area is exactly equal)
+ */
+gdouble gap_locateDetailWithinRadius(gint32 refDrawableId
+ , gint32 refX
+ , gint32 refY
+ , gint32 refShapeRadius
+ , gint32 targetDrawableId
+ , gint32 targetMoveRadius
+ , gdouble requiredColordiffThreshold
+ , gint32 *targetX
+ , gint32 *targetY
+ )
+{
+ GapLocateContext locateContext;
+ GapLocateContext *lctx;
+
+ /* init context */
+ lctx = &locateContext;
+ lctx->refDrawable = NULL;
+ lctx->targetDrawable = NULL;
+ lctx->refX = refX;
+ lctx->refY = refY;
+ lctx->refShapeRadius = CLAMP(refShapeRadius, 1, GAP_LOCATE_MAX_REF_SHAPE_RADIUS);
+ lctx->targetMoveRadius = targetMoveRadius;
+ lctx->targetX = 0;
+ lctx->targetY = 0;
+ lctx->minColordiff = 1.0;
+ lctx->tmpColordiffBreakThreshold = CLAMP(requiredColordiffThreshold, 0.0, 1.0) * COLORDIFF_BREAK_THRESHOLD_FACTOR;
+ lctx->breakAtColordiff = 0.0;
+ lctx->refPixelColordiffThreshold = 0.11;
+ lctx->edgeColordiffThreshold = 0.14;
+ lctx->countTries = 0;
+ lctx->countAreaCompares = 0;
+ lctx->pickedPixelCount = 0;
+
+ lctx->refDrawable = gimp_drawable_get(refDrawableId);
+ lctx->targetDrawable = gimp_drawable_get(targetDrawableId);
+
+ if((lctx->refDrawable != NULL)
+ && (lctx->targetDrawable != NULL))
+ {
+ /* create and init pixelfetchers (deliver black transparent pixel outside boundaries) */
+ lctx->pftRef = gimp_pixel_fetcher_new (lctx->refDrawable, FALSE /* shadow */);
+ lctx->pftTarget = gimp_pixel_fetcher_new (lctx->targetDrawable, FALSE /* shadow */);
+ gimp_pixel_fetcher_set_edge_mode (lctx->pftRef, GIMP_PIXEL_FETCHER_EDGE_NONE);
+ gimp_pixel_fetcher_set_edge_mode (lctx->pftTarget, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+ /* init reference pixel color (in center of used area in refColorTab) */
+ lctx->refColorPtr = &lctx->refColorTab[lctx->refShapeRadius][lctx->refShapeRadius];
+ gimp_pixel_fetcher_get_pixel (lctx->pftRef, refX, refY, &lctx->refColorPtr->rgba[0]);
+
+ p_initReferenceTable(lctx);
+ p_locateDetailLoop(lctx);
+
+ gimp_pixel_fetcher_destroy (lctx->pftRef);
+ gimp_pixel_fetcher_destroy (lctx->pftTarget);
+ }
+
+
+ if(lctx->refDrawable != NULL)
+ {
+ gimp_drawable_detach(lctx->refDrawable);
+ }
+
+ if(lctx->targetDrawable != NULL)
+ {
+ gimp_drawable_detach(lctx->targetDrawable);
+ }
+
+
+ *targetX = lctx->targetX;
+ *targetY = lctx->targetY;
+
+ if(gap_debug)
+ {
+ p_debugPrintReferenceTable(lctx);
+ printf("gap_locateDetailWithinRadius: tries:%d areaCompares:%d pickedTryNumber:%d pickedPixelCount:%d resulting colordiff:%.5f\n"
+ ,(int)lctx->countTries
+ ,(int)lctx->countAreaCompares
+ ,(int)lctx->pickedTryNumber
+ ,(int)lctx->pickedPixelCount
+ ,(float)lctx->minColordiff
+ );
+ }
+
+ return (lctx->minColordiff);
+
+} /* end gap_locateDetailWithinRadius */
diff --git a/gap/gap_locate.h b/gap/gap_locate.h
new file mode 100644
index 0000000..e602958
--- /dev/null
+++ b/gap/gap_locate.h
@@ -0,0 +1,83 @@
+/* gap_locate.h
+ * by hof (Wolfgang Hofer)
+ * 2010/08/06
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#ifndef _GAP_LOCATE_H
+#define _GAP_LOCATE_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+#define GAP_LOCATE_MIN_REF_SHAPE_RADIUS 3
+#define GAP_LOCATE_MAX_REF_SHAPE_RADIUS 16
+
+
+/* ----------------------------------------
+ * gap_locateDetailWithinRadius
+ * ----------------------------------------
+ *
+ * the locateDetailWithinRadius procedure takes a referenced detail
+ * specified via refX/Y coordinate, refShapeRadius within a
+ * reference drawable
+ * and tries to locate the same (or similar) detail coordinates
+ * in the target Drawable.
+ *
+ * This is done by comparing pixel areas at refShapeRadius
+ * in the corresponding target Drawable in a loop while
+ * varying offsets within targetMoveRadius.
+ * the targetX/Y koords are picked at those offsets where the compared areas
+ * are best matching (e.g with minimun color difference)
+ * the return value is the minimum colrdifference value
+ * (in range 0.0 to 1.0 where 0.0 indicates that the compared area is exactly equal)
+ *
+ * requiredColordiffThreshold has valid range 0.0 to 1.0
+ * values < 1.0 can speed up processing because area compare operations
+ * are cancelled on offesets where colordiff is above this threshold
+ * (e.g. when further analyse has no chance to deliver result
+ * below the required matching quality)
+ * use value 1.0 if you are interested in weak matching results too.
+ *
+ */
+gdouble gap_locateDetailWithinRadius(gint32 refDrawableId
+ , gint32 refX
+ , gint32 refY
+ , gint32 refShapeRadius
+ , gint32 targetDrawableId
+ , gint32 targetMoveRadius
+ , gdouble requiredColordiffThreshold
+ , gint32 *targetX
+ , gint32 *targetY
+ );
+
+
+
+
+#endif
diff --git a/gap/gap_main.c b/gap/gap_main.c
index 985b5a8..bb224a6 100644
--- a/gap/gap_main.c
+++ b/gap/gap_main.c
@@ -831,23 +831,23 @@ query ()
" With the Controlpoint Parameters you can control position (coordinates),\n"
" size, rotation, perspective and opacity values of the inserted layer\n"
" If you want to move an Object from position AX/AY to BX/BY in a straight line within the range of 24 frames\n"
- " you need 2 Contolpoints, if you want the object to move folowing a path\n"
+ " you need 2 Contolpoints, if you want the object to move following a path\n"
" you need some more Controlpoints to do that.\n"
" With the rotation_follow Parameter you can force automatic calculation of the rotation for the inserted\n"
" layer according to the path vectors it is moving along.\n"
" A controlpoint can be fixed to a special framenumber using the keyframe_abs controlpoint-parameter.\n"
- " Restictions:\n"
+ " Restrictions:\n"
" - keyframe_abs numbers must be 0 (== not fixed) or a frame_number within the affected frame range\n"
" - keyframes_abs must be in sequence (ascending or descending)\n"
- " - the first and last controlpoint are always implicite keyframes, and should be passed with keyframe_abs = 0\n"
- " - the number of controlpoints is limitied to a maximum of %d.\n"
+ " - the first and last controlpoint are always implicit keyframes, and should be passed with keyframe_abs = 0\n"
+ " - the number of controlpoints is limited to a maximum of %d.\n"
" the number of controlpoints must be passed in all argc_* parameters\n"
"If the TraceLayer feature is turned on, an additional layer\n"
" is inserted below the moving object. This Tracelayer shows all steps\n"
" of the moving object since the 1st Frame.\n"
"With TweenSteps you can calculate virtual Frames between 2 destination frames\n"
" all these Steps are collected in another additional Layer.\n"
- " this Tweenlayer is added below the moving Obect in all handled destination Frames\n"
+ " this Tweenlayer is added below the moving Object in all handled destination Frames\n"
"See also (plug_in_gap_move_path, plug_in_gap_move)",
(int)GAP_MOV_MAX_POINT);
diff --git a/gap/gap_match.c b/gap/gap_match.c
index 9b79203..83c5bb0 100644
--- a/gap/gap_match.c
+++ b/gap/gap_match.c
@@ -228,7 +228,7 @@ gap_match_number(gint32 layer_idx, const char *pattern)
{
/* now we are one character past a number */
l_digit_buff[l_idx] = '\0';
- l_num = atol(l_digit_buff); /* scann the number */
+ l_num = atol(l_digit_buff); /* scan the number */
if(l_num == layer_idx)
{
diff --git a/gap/gap_mod_layer.c b/gap/gap_mod_layer.c
index ccb7c7d..9fb4bbc 100644
--- a/gap/gap_mod_layer.c
+++ b/gap/gap_mod_layer.c
@@ -283,7 +283,7 @@ p_raise_layer (gint32 image_id, gint32 layer_id, GapModLayliElem * layli_ptr, gi
if(! gimp_drawable_has_alpha (layer_id))
{
- /* implicite add an alpha channel before we try to raise */
+ /* implicitly add an alpha channel before we try to raise */
gimp_layer_add_alpha(layer_id);
}
gimp_image_raise_layer(image_id, layer_id);
@@ -296,7 +296,7 @@ p_lower_layer (gint32 image_id, gint32 layer_id, GapModLayliElem * layli_ptr, gi
if(! gimp_drawable_has_alpha (layer_id))
{
- /* implicite add an alpha channel before we try to lower */
+ /* implicitly add an alpha channel before we try to lower */
gimp_layer_add_alpha(layer_id);
}
@@ -306,7 +306,7 @@ p_lower_layer (gint32 image_id, gint32 layer_id, GapModLayliElem * layli_ptr, gi
&& (! gimp_drawable_has_alpha (layli_ptr[nlayers-1].layer_id)))
{
/* the layer is one step above a "bottom-layer without alpha" */
- /* implicite add an alpha channel before we try to lower */
+ /* implicitly add an alpha channel before we try to lower */
gimp_layer_add_alpha(layli_ptr[nlayers-1].layer_id);
}
}
@@ -1218,7 +1218,7 @@ cleanup:
/* ============================================================================
- * p_frames_modify
+ * gap_mod_frames_modify
*
* foreach frame of the range (given by range_from and range_to)
* perform function defined by action_mode
@@ -1228,12 +1228,14 @@ cleanup:
* (or -1 on error or cancel)
* ============================================================================
*/
-static gint32
-p_frames_modify(GapAnimInfo *ainfo_ptr,
+gint32
+gap_mod_frames_modify(GapAnimInfo *ainfo_ptr,
long range_from, long range_to,
gint32 action_mode, gint32 sel_mode,
gint32 sel_case, gint32 sel_invert,
- char *sel_pattern, char *new_layername)
+ char *sel_pattern, char *new_layername,
+ GtkWidget *progress_bar,
+ gboolean *run_flag)
{
long l_cur_frame_nr;
long l_step, l_begin, l_end;
@@ -1264,7 +1266,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
if(gap_debug)
{
- printf("gap: p_frames_modify START, action_mode=%d sel_mode=%d case=%d, invert=%d patt:%s:\n",
+ printf("gap: gap_mod_frames_modify START, action_mode=%d sel_mode=%d case=%d, invert=%d patt:%s:\n",
(int)action_mode, (int)sel_mode, (int)sel_case, (int)sel_invert, sel_pattern);
}
@@ -1324,7 +1326,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
{
if(gap_debug)
{
- printf("p_frames_modify While l_cur_frame_nr = %d\n", (int)l_cur_frame_nr);
+ printf("gap_mod_frames_modify While l_cur_frame_nr = %d\n", (int)l_cur_frame_nr);
}
/* build the frame name */
@@ -1356,7 +1358,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
if(l_layli_ptr == NULL)
{
- printf("gap: p_frames_modify: cant alloc layer info list\n");
+ printf("gap: gap_mod_frames_modify: cant alloc layer info list\n");
goto error;
}
@@ -1460,7 +1462,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
);
if(l_rc != 0)
{
- if(gap_debug) printf("gap: p_frames_modify p_apply-action failed. rc=%d\n", (int)l_rc);
+ if(gap_debug) printf("gap: gap_mod_frames_modify p_apply-action failed. rc=%d\n", (int)l_rc);
goto error;
}
@@ -1478,7 +1480,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
l_rc = gap_lib_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename);
if(l_rc < 0)
{
- printf("gap: p_frames_modify save frame %d failed.\n", (int)l_cur_frame_nr);
+ printf("gap: gap_mod_frames_modify save frame %d failed.\n", (int)l_cur_frame_nr);
goto error;
}
else l_rc = 0;
@@ -1558,7 +1560,29 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
{
l_percentage += l_percentage_step;
- gimp_progress_update (l_percentage);
+
+ if(progress_bar != NULL)
+ {
+ guchar *progressText;
+ progressText = g_strdup_printf("frame:%d (%d)", (int)l_cur_frame_nr, (int)l_end);
+
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), progressText);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), l_percentage);
+ g_free(progressText);
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+ if(*run_flag != TRUE)
+ {
+ /* cancel button was pressed in interactive mode */
+ break;
+ }
+ }
+ else
+ {
+ gimp_progress_update (l_percentage);
+ }
}
/* advance to next frame */
@@ -1597,12 +1621,12 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
}
}
- if(gap_debug) printf("p_frames_modify End OK\n");
+ if(gap_debug) printf("gap_mod_frames_modify End OK\n");
return 0;
error:
- if(gap_debug) printf("gap: p_frames_modify exit with Error\n");
+ if(gap_debug) printf("gap: gap_mod_frames_modify exit with Error\n");
if((l_tmp_image_id >= 0) && (l_operating_on_current_image == FALSE))
{
@@ -1617,7 +1641,7 @@ error:
if(l_plugin_iterator != NULL) g_free(l_plugin_iterator);
return -1;
-} /* end p_frames_modify */
+} /* end gap_mod_frames_modify */
/* ============================================================================
@@ -1639,12 +1663,15 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
gint32 l_sel_mode;
gint32 l_sel_case;
gint32 l_sel_invert;
+ GtkWidget *progress_bar;
+ GtkWidget *dlg;
char l_sel_pattern[MAX_LAYERNAME];
char l_new_layername[MAX_LAYERNAME];
l_rc = 0;
-
+ progress_bar = NULL;
+ dlg = NULL;
ainfo_ptr = gap_lib_alloc_ainfo(image_id, run_mode);
if(ainfo_ptr != NULL)
@@ -1654,10 +1681,15 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
{
if(run_mode == GIMP_RUN_INTERACTIVE)
{
+ /* note: for interactive call the processing is already done
+ * as callback of the dialog
+ */
l_rc = gap_mod_frames_dialog (ainfo_ptr, &l_from, &l_to,
&l_action_mode,
&l_sel_mode, &sel_case, &sel_invert,
&l_sel_pattern[0], &l_new_layername[0]);
+ gap_lib_free_ainfo(&ainfo_ptr);
+ return (l_rc);
}
else
{
@@ -1676,16 +1708,20 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
if(l_rc >= 0)
{
+ gboolean run_flag;
+
+ run_flag = TRUE;
/* no need to save the current image before processing
- * because the p_frames_modify procedure operates directly on the current frame
+ * because the gap_mod_frames_modify procedure operates directly on the current frame
* and loads all other frames from disc.
* futher all successful processed frames are saved back to disk
* (including the current one)
*/
- l_rc = p_frames_modify(ainfo_ptr, l_from, l_to,
+ l_rc = gap_mod_frames_modify(ainfo_ptr, l_from, l_to,
l_action_mode,
l_sel_mode, sel_case, sel_invert,
- &l_sel_pattern[0], &l_new_layername[0]
+ &l_sel_pattern[0], &l_new_layername[0],
+ progress_bar, &run_flag
);
}
diff --git a/gap/gap_mod_layer.h b/gap/gap_mod_layer.h
index 0aa787b..fd7439e 100644
--- a/gap/gap_mod_layer.h
+++ b/gap/gap_mod_layer.h
@@ -36,6 +36,10 @@
#ifndef _GAP_MOD_LAYER_H
#define _GAP_MOD_LAYER_H
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+#include "gap_libgapbase.h"
+
#define MAX_LAYERNAME 128
/* action_mode values */
@@ -130,4 +134,12 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
gint32 sel_case, gint32 sel_invert,
char *sel_pattern, char *new_layername);
+gint32 gap_mod_frames_modify(GapAnimInfo *ainfo_ptr,
+ long range_from, long range_to,
+ gint32 action_mode, gint32 sel_mode,
+ gint32 sel_case, gint32 sel_invert,
+ char *sel_pattern, char *new_layername,
+ GtkWidget *progress_bar,
+ gboolean *run_flag);
+
#endif
diff --git a/gap/gap_mod_layer_dialog.c b/gap/gap_mod_layer_dialog.c
index fdaf946..d09d355 100644
--- a/gap/gap_mod_layer_dialog.c
+++ b/gap/gap_mod_layer_dialog.c
@@ -111,24 +111,47 @@ p_mod_frames_response (GtkWidget *widget,
gint response_id,
GapModFramesGlobalParams *gmop)
{
- switch (response_id)
+ if (response_id == GTK_RESPONSE_CANCEL)
{
- case GTK_RESPONSE_OK:
- gmop->run_flag = TRUE;
-
- default:
- if(gmop->shell)
- {
- GtkWidget *l_shell;
+ gmop->retcode = -1;
+ if(gmop->run_flag == TRUE)
+ {
+ gmop->run_flag = FALSE;
+ return;
+ }
+ }
+ else if (response_id == GTK_RESPONSE_OK)
+ {
+ if(gmop->run_flag == TRUE)
+ {
+ return;
+ }
+ gtk_widget_set_sensitive(gmop->main_vbox, FALSE);
+
+ gmop->run_flag = TRUE;
+ gmop->retcode = gap_mod_frames_modify(gmop->ainfo_ptr
+ , gmop->range_from
+ , gmop->range_to
+ , gmop->action_mode
+ , gmop->sel_mode
+ , gmop->sel_case
+ , gmop->sel_invert
+ , gmop->sel_pattern
+ , gmop->new_layername
+ , gmop->progress_bar
+ , &gmop->run_flag
+ );
+ }
- l_shell = gmop->shell;
- gmop->shell = NULL;
+ if(gmop->shell)
+ {
+ GtkWidget *l_shell;
- gtk_widget_destroy(l_shell);
- gtk_main_quit();
- }
+ l_shell = gmop->shell;
+ gmop->shell = NULL;
- break;
+ gtk_widget_destroy(l_shell);
+ gtk_main_quit();
}
} /* end p_mod_frames_response */
@@ -960,6 +983,7 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
{
GtkWidget *dlg;
GtkWidget *main_vbox;
+ GtkWidget *active_vbox;
GtkWidget *hbox;
GtkWidget *frame;
GtkWidget *entry;
@@ -972,6 +996,7 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
GtkWidget *menu_bar;
GtkWidget *menu_item;
GtkWidget *master_menu;
+ GtkWidget *progress_bar;
gint row;
GtkObject *adj;
@@ -997,6 +1022,7 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
main_vbox = gtk_vbox_new (FALSE, 4);
+ gmop->main_vbox = main_vbox;
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), main_vbox);
@@ -1389,6 +1415,14 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
G_CALLBACK (gimp_int_adjustment_update),
&gmop->range_to);
+ row++;
+
+ progress_bar = gtk_progress_bar_new ();
+ gmop->progress_bar = progress_bar;
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), " ");
+ gtk_widget_show (progress_bar);
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), progress_bar);
gtk_widget_show(main_vbox);
@@ -1417,6 +1451,7 @@ gap_mod_frames_dialog(GapAnimInfo *ainfo_ptr,
gap_stock_init();
gmop->run_flag = FALSE;
+ gmop->retcode = -1;
gmop->ainfo_ptr = ainfo_ptr;
gmop->range_from = gmop->ainfo_ptr->first_frame_nr;
gmop->range_to = gmop->ainfo_ptr->last_frame_nr;
diff --git a/gap/gap_mod_layer_dialog.h b/gap/gap_mod_layer_dialog.h
index 6f40400..e744897 100644
--- a/gap/gap_mod_layer_dialog.h
+++ b/gap/gap_mod_layer_dialog.h
@@ -69,9 +69,13 @@ typedef struct GapModFramesGlobalParams { /* nick: gmop */
GtkWidget *case_sensitive_check_button;
GtkWidget *invert_check_button;
GtkWidget *layer_selection_frame;
+ GtkWidget *progress_bar;
+ GtkWidget *main_vbox;
GtkObject *frame_from_adj;
GtkObject *frame_to_adj;
+
+ gint32 retcode;
} GapModFramesGlobalParams;
diff --git a/gap/gap_morph_dialog.c b/gap/gap_morph_dialog.c
index 2e6f379..3b97bda 100644
--- a/gap/gap_morph_dialog.c
+++ b/gap/gap_morph_dialog.c
@@ -44,8 +44,11 @@
#include "gap_base.h"
+#include "gap_colordiff.h"
+#include "gap_locate.h"
#include "gap_morph_main.h"
#include "gap_morph_dialog.h"
+#include "gap_morph_shape.h"
#include "gap_morph_exec.h"
#include "gap_pdb_calls.h"
#include "gap_pview_da.h"
@@ -112,24 +115,11 @@ static gint32 global_morph_pv_height = GAP_MORPH_PV_HEIGHT_DEFAULT;
static void p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup);
static void p_upd_widget_values(GapMorphGUIParams *mgup);
static void p_upd_warp_info_label(GapMorphGUIParams *mgup);
-static gboolean p_pixel_check_opaque(GimpPixelFetcher *pft
- , gint bpp
- , gdouble needx
- , gdouble needy
- );
-static void p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
- ,gint bpp
- ,gdouble alpha_rad
- ,gint32 width
- ,gint32 height
- ,gdouble *px
- ,gdouble *py
- );
static void p_generate_outline_shape_workpoints(GapMorphGUIParams *mgup);
static void p_add_4corner_workpoints(GapMorphGUIParams *mgup);
-static void p_zoom_in(GapMorphSubWin *swp, gdouble l_x, gdouble l_y); //XXX unfinished
-static void p_zoom_out(GapMorphSubWin *swp); //XXX unfinished
+static void p_zoom_in(GapMorphSubWin *swp, gdouble l_x, gdouble l_y);
+static void p_zoom_out(GapMorphSubWin *swp);
static void p_fit_zoom_into_pview_size(GapMorphSubWin *swp);
static void on_swap_button_pressed_callback(GtkWidget *wgt, GapMorphGUIParams *mgup);
static void on_fit_zoom_pressed_callback(GtkWidget *wgt, GapMorphSubWin *swp);
@@ -146,8 +136,13 @@ static void p_set_nearest_current_workpoint(GapMorphSubWin *swp
);
static void p_set_current_workpoint_no_refresh(GapMorphSubWin *swp, GapMorphWorkPoint *wp);
static void p_set_current_workpoint(GapMorphSubWin *swp, GapMorphWorkPoint *wp);
-static GapMorphWorkPoint * p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y);
-static void p_add_new_point_refresh(GapMorphSubWin *swp, gdouble in_x, gdouble in_y);
+
+static void p_locate_point(gint32 refDrawableId, gint32 targetDrawableId
+ , GapMorphGUIParams *mgup
+ , gdouble rx, gdouble ry
+ , gdouble *tx, gdouble *ty);
+static GapMorphWorkPoint * p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y, gboolean locate_detail);
+static void p_add_new_point_refresh(GapMorphSubWin *swp, gdouble in_x, gdouble in_y, gboolean locate_detail);
static void p_refresh_total_points_label(GapMorphGUIParams *mgup);
static GapMorphWorkPoint * p_find_nearest_point(GapMorphSubWin *swp
, gdouble in_x
@@ -213,6 +208,8 @@ static GtkWidget * p_create_subwin(GapMorphSubWin *swp
static void gap_morph_create_dialog(GapMorphGUIParams *mgup);
+
+
/* -----------------------------
* p_morph_response
* -----------------------------
@@ -225,6 +222,7 @@ p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup)
switch (response_id)
{
case GAP_MORPH_RESPONSE_RESET:
+ mgup->cancelWorkpointGeneration = TRUE;
gap_morph_exec_free_workpoint_list(&mgup->mgpp->master_wp_list);
p_add_4corner_workpoints(mgup);
p_set_upd_timer_request(mgup
@@ -254,6 +252,7 @@ p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup)
case GTK_RESPONSE_OK:
if(mgup)
{
+ mgup->cancelWorkpointGeneration = TRUE;
if (GTK_WIDGET_VISIBLE (mgup->shell))
{
gtk_widget_hide (mgup->shell);
@@ -265,6 +264,7 @@ p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup)
dialog = NULL;
if(mgup)
{
+ mgup->cancelWorkpointGeneration = TRUE;
dialog = mgup->shell;
if(dialog)
{
@@ -297,6 +297,15 @@ p_upd_widget_values(GapMorphGUIParams *mgup)
GTK_ADJUSTMENT(mgup->affect_radius_spinbutton_adj)
,mgup->mgpp->affect_radius);
gtk_adjustment_set_value(
+ GTK_ADJUSTMENT(mgup->locate_edge_threshold_spinbutton_adj)
+ ,mgup->mgpp->edgeColordiffThreshold);
+ gtk_adjustment_set_value(
+ GTK_ADJUSTMENT(mgup->locate_radius_spinbutton_adj)
+ ,mgup->mgpp->locateDetailMoveRadius);
+ gtk_adjustment_set_value(
+ GTK_ADJUSTMENT(mgup->locate_shape_radius_spinbutton_adj)
+ ,mgup->mgpp->locateDetailShapeRadius);
+ gtk_adjustment_set_value(
GTK_ADJUSTMENT(mgup->gravity_intensity_spinbutton_adj)
,mgup->mgpp->gravity_intensity);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mgup->use_quality_wp_selection_checkbutton)
@@ -345,127 +354,6 @@ p_upd_warp_info_label(GapMorphGUIParams *mgup)
-/* -----------------------------
- * p_pixel_check_opaque
- * -----------------------------
- * check average opacity in an area
- * of 2x2 pixels
- * return TRUE if average alpha is 50% or more opaque
- */
-static gboolean
-p_pixel_check_opaque(GimpPixelFetcher *pft, gint bpp, gdouble needx, gdouble needy)
-{
- guchar pixel[4][4];
- gdouble alpha_val;
-
- gint xi, yi;
- gint alpha_idx;
-
-
- if (needx >= 0.0)
- xi = (int) needx;
- else
- xi = -((int) -needx + 1);
-
- if (needy >= 0.0)
- yi = (int) needy;
- else
- yi = -((int) -needy + 1);
-
- gimp_pixel_fetcher_get_pixel (pft, xi, yi, pixel[0]);
- gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi, pixel[1]);
- gimp_pixel_fetcher_get_pixel (pft, xi, yi + 1, pixel[2]);
- gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi + 1, pixel[3]);
-
- alpha_idx = bpp -1;
- /* average aplha channel normalized to range 0.0 upto 1.0 */
- alpha_val = (
- (gdouble)pixel[0][alpha_idx] / 255.0
- + (gdouble)pixel[1][alpha_idx] / 255.0
- + (gdouble)pixel[2][alpha_idx] / 255.0
- + (gdouble)pixel[3][alpha_idx] / 255.0
- ) / 4.0;
-
- if (alpha_val > 0.5) /* fix THRESHOLD half or more opaque */
- {
- return (TRUE);
- }
- return (FALSE);
-} /* end p_pixel_check_opaque */
-
-
-/* -----------------------------
- * p_find_outmost_opaque_pixel
- * -----------------------------
- * returns koords in paramters px, py
- */
-static void
-p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
- ,gint bpp
- ,gdouble alpha_rad
- ,gint32 width
- ,gint32 height
- ,gdouble *px
- ,gdouble *py
- )
-{
- gdouble center_x;
- gdouble center_y;
- gdouble cos_alpha;
- gdouble sin_alpha;
- gdouble l_x, l_y, l_r;
- gdouble half_width;
- gdouble half_height;
-
- l_x = 0;
- l_y = 0;
- cos_alpha = cos(alpha_rad);
- sin_alpha = sin(alpha_rad);
-
- /* printf("sin: %.5f cos:%.5f\n", sin_alpha, cos_alpha); */
-
- half_width = (gdouble)(width /2.0);
- half_height = (gdouble)(height /2.0);
- center_x = half_width;
- center_y = half_height;
- l_r = MAX(half_width, half_height);
- l_r *= l_r;
-
- /* start at the out-most point
- * (may be out of the layer in most cases)
- * and search towards the center along
- * the line with angle alpha
- */
- while(l_r > 0)
- {
- l_y = l_r * sin_alpha;
- l_x = l_r * cos_alpha;
- if((l_x <= half_width)
- && (l_y <= half_height))
- {
- if (((center_x + l_x) >= 0)
- && ((center_y + l_y) >= 0))
- {
- /* now we are inside the layer area */
- if (p_pixel_check_opaque(pft
- ,bpp
- ,center_x + l_x
- ,center_y + l_y
- ))
- {
- break;
- }
- }
- }
- l_r--;
- }
-
- *px = center_x + l_x;
- *py = center_y + l_y;
-
-} /* end p_find_outmost_opaque_pixel */
-
-
/* -----------------------------------
* p_generate_outline_shape_workpoints
* -----------------------------------
@@ -473,59 +361,8 @@ p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
static void
p_generate_outline_shape_workpoints(GapMorphGUIParams *mgup)
{
- GapMorphWorkPoint *wp;
- GimpPixelFetcher *src_pixfet;
- GimpPixelFetcher *dst_pixfet;
- GimpDrawable *dst_drawable;
- GimpDrawable *src_drawable;
- gdouble alpha_rad;
- gdouble step_rad;
- gint ii;
-
- src_drawable = gimp_drawable_get (mgup->mgpp->osrc_layer_id);
- dst_drawable = gimp_drawable_get (mgup->mgpp->fdst_layer_id);
-
- src_pixfet = gimp_pixel_fetcher_new (src_drawable, FALSE /*shadow*/);
- dst_pixfet = gimp_pixel_fetcher_new (dst_drawable, FALSE /*shadow*/);
-
- step_rad = (2.0 * G_PI) / MAX(1, mgup->num_shapepoints);
- alpha_rad = 0.0;
-
- /* loop from 0 to 360 degree */
- for(ii=0; ii < mgup->num_shapepoints; ii++)
- {
- gdouble sx, sy, dx, dy;
-
- p_find_outmost_opaque_pixel(src_pixfet
- ,src_drawable->bpp
- ,alpha_rad
- ,src_drawable->width
- ,src_drawable->height
- ,&sx
- ,&sy
- );
- p_find_outmost_opaque_pixel(dst_pixfet
- ,dst_drawable->bpp
- ,alpha_rad
- ,dst_drawable->width
- ,dst_drawable->height
- ,&dx
- ,&dy
- );
-
- /* create a new workpoint with sx,sy, dx, dy coords */
- wp = gap_morph_dlg_new_workpont(sx ,sy ,dx ,dy);
- wp->next = mgup->mgpp->master_wp_list;
- mgup->mgpp->master_wp_list = wp;
-
- alpha_rad += step_rad;
- }
- gimp_pixel_fetcher_destroy (src_pixfet);
- gimp_pixel_fetcher_destroy (dst_pixfet);
-
- gimp_drawable_detach(src_drawable);
- gimp_drawable_detach(dst_drawable);
-
+ mgup->mgpp->numOutlinePoints = mgup->num_shapepoints;
+ gap_morph_shape_generate_outline_workpoints(mgup->mgpp);
} /* end p_generate_outline_shape_workpoints */
@@ -741,6 +578,10 @@ on_swap_button_pressed_callback(GtkWidget *wgt, GapMorphGUIParams *mgup)
{
return;
}
+ if(mgup->workpointGenerationBusy == TRUE)
+ {
+ return;
+ }
src_layer_width = gimp_drawable_width(mgup->mgpp->osrc_layer_id);
src_layer_height = gimp_drawable_height(mgup->mgpp->osrc_layer_id);
@@ -1004,7 +845,6 @@ on_koord_spinbutton_changed (GtkObject *obj,
if(value != *val_ptr)
{
gimp_double_adjustment_update(GTK_ADJUSTMENT(obj), val_ptr);
- // *val_ptr = value;
if(swp)
{
/* setup a timer request for pview redraw after 16 millisec
@@ -1082,19 +922,9 @@ gap_morph_dlg_new_workpont(gdouble srcx, gdouble srcy, gdouble dstx, gdouble dst
{
GapMorphWorkPoint *wp;
- wp = g_new(GapMorphWorkPoint, 1);
- wp->next = NULL;
- wp->fdst_x = dstx;
- wp->fdst_y = dsty;
- wp->osrc_x = srcx;
- wp->osrc_y = srcy;
-
- wp->dst_x = wp->fdst_x;
- wp->dst_y = wp->fdst_y;
- wp->src_x = wp->osrc_x;
- wp->src_y = wp->osrc_y;
-
+ wp = gap_morph_shape_new_workpont(srcx, srcy, dstx, dsty);
return(wp);
+
} /* end gap_morph_dlg_new_workpont */
/* -----------------------------
@@ -1290,15 +1120,81 @@ p_set_current_workpoint(GapMorphSubWin *swp, GapMorphWorkPoint *wp_cur)
} /* end p_set_current_workpoint */
/* -----------------------------
+ * p_locate_point
+ * -----------------------------
+ * locate point coordinate in the other drawable
+ * (by searching for best matching shape within move radius)
+ */
+static void
+p_locate_point(gint32 refDrawableId, gint32 targetDrawableId
+ , GapMorphGUIParams *mgup
+ , gdouble rx, gdouble ry
+ , gdouble *tx, gdouble *ty)
+{
+ gint32 refX;
+ gint32 refY;
+ gint32 targetX;
+ gint32 targetY;
+ gdouble colordiff;
+
+ refX = rx;
+ refY = ry;
+
+
+ colordiff = gap_locateDetailWithinRadius(refDrawableId
+ , refX
+ , refY
+ , mgup->mgpp->locateDetailShapeRadius
+ , targetDrawableId
+ , mgup->mgpp->locateDetailMoveRadius
+ , mgup->mgpp->locateColordiffThreshold
+ , &targetX
+ , &targetY
+ );
+ if(gap_debug)
+ {
+ printf("p_locate_point: x/y: %d / %d MoveRadius:%d ShapeRadius:%d Threshold:%.5f colordiff:%.5f\n"
+ , (int)refX
+ , (int)refY
+ , (int)mgup->mgpp->locateDetailMoveRadius
+ , (int)mgup->mgpp->locateDetailShapeRadius
+ , (float)mgup->mgpp->locateColordiffThreshold
+ , (float)colordiff
+ );
+
+ }
+
+ if(colordiff < mgup->mgpp->locateColordiffThreshold)
+ {
+ /* found a sufficient matching shape in the target drawable
+ * at targetX/Y coordinate. Overwrite tx and ty with this matching point coordinate
+ */
+ *tx = targetX;
+ *ty = targetY;
+
+
+ if(gap_debug)
+ {
+ printf("p_locate_point: SUCCESS, found matching shape! tx:%d ty:%d\n"
+ ,(int)targetX
+ ,(int)targetY
+ );
+ }
+ }
+
+} /* end p_locate_point */
+
+
+/* -----------------------------
* p_add_new_point
* -----------------------------
* add a new point at in_x/in_y
- * and calculate the other koordianates
- * (using the offests of the nearest existing point
+ * and calculate the other coordinates
+ * (using the offsets of the nearest existing point
* as guess)
*/
static GapMorphWorkPoint *
-p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y)
+p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y, gboolean locate_detail)
{
GapMorphGUIParams *mgup;
GapMorphSubWin *swp_other;
@@ -1333,6 +1229,8 @@ p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y)
srcx = in_x;
srcy = in_y;
+
+
if(mgup->op_mode == GAP_MORPH_OP_MODE_MOVE)
{
dstx = (srcx) * w / MAX(wo,1);
@@ -1343,6 +1241,18 @@ p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y)
dstx = (srcx + (wp_near->fdst_x - wp_near->osrc_x)) * w / MAX(wo,1);
dsty = (srcy + (wp_near->fdst_y - wp_near->osrc_y)) * h / MAX(ho,1);
}
+
+ if(locate_detail)
+ {
+ p_locate_point(*swp->layer_id_ptr /* reference DrawableId */
+ , *swp_other->layer_id_ptr /* target DrawableId */
+ , mgup
+ , srcx, srcy /* reference coordinates */
+ , &dstx, &dsty /* OUT: target coordinates */
+ );
+ }
+
+
}
else
{
@@ -1366,6 +1276,16 @@ p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y)
srcx = (dstx + (wp_near->osrc_x - wp_near->fdst_x)) * w / MAX(wo,1);
srcy = (dsty + (wp_near->osrc_y - wp_near->fdst_y)) * h / MAX(ho,1);
}
+
+ if(locate_detail)
+ {
+ p_locate_point(*swp->layer_id_ptr /* reference DrawableId */
+ , *swp_other->layer_id_ptr /* target DrawableId */
+ , mgup
+ , dstx, dsty /* reference coordinates */
+ , &srcx, &srcy /* OUT: target coordinates */
+ );
+ }
}
wp = gap_morph_dlg_new_workpont(srcx, srcy, dstx, dsty);
@@ -1386,12 +1306,12 @@ p_add_new_point(GapMorphSubWin *swp, gdouble in_x, gdouble in_y)
* (this also sets refresh request)
*/
static void
-p_add_new_point_refresh(GapMorphSubWin *swp, gdouble in_x, gdouble in_y)
+p_add_new_point_refresh(GapMorphSubWin *swp, gdouble in_x, gdouble in_y, gboolean locate_detail)
{
GapMorphGUIParams *mgup;
GapMorphWorkPoint *wp;
- wp = p_add_new_point(swp, in_x, in_y);
+ wp = p_add_new_point(swp, in_x, in_y, locate_detail);
p_set_current_workpoint(swp, wp);
mgup = (GapMorphGUIParams *)swp->mgup;
@@ -1432,7 +1352,7 @@ p_refresh_total_points_label(GapMorphGUIParams *mgup)
* p_find_nearest_point
* ---------------------------------
* saerch the workpoint list for the point that is the nearest
- * to position in_x/in_y in the osrc or fdst koord system.
+ * to position in_x/in_y in the osrc or fdst coord system.
* (depending on the swp->src_flag)
*/
static GapMorphWorkPoint *
@@ -1940,10 +1860,10 @@ on_pview_events (GtkWidget *widget
GdkEventButton *bevent;
GdkEventMotion *mevent;
gint mouse_button;
- gdouble curx; /* current mouse position koordinate */
+ gdouble curx; /* current mouse position coordinate */
gdouble cury;
- static gdouble prevx; /* prev mouse position koordinate */
+ static gdouble prevx; /* prev mouse position coordinate */
static gdouble prevy;
static gboolean drag_disabled = FALSE; /* ALT or CTRL pressed */
@@ -1991,6 +1911,9 @@ on_pview_events (GtkWidget *widget
gdouble l_x;
gdouble l_y;
gboolean make_new_point;
+ gboolean locate_detail;
+
+ locate_detail = FALSE;
/* Picking of pathpoints is done when
* the left mousebutton goes down (mouse_button == 1)
@@ -2062,6 +1985,15 @@ on_pview_events (GtkWidget *widget
return (FALSE);
}
+ if(bevent->state & GDK_CONTROL_MASK)
+ {
+ if(mgup->op_mode == GAP_MORPH_OP_MODE_MOVE)
+ {
+ make_new_point = TRUE;
+ }
+ locate_detail = TRUE;
+ }
+
if(bevent->state & GDK_SHIFT_MASK)
{
/* SHIFT-Click: force creation of new point
@@ -2114,7 +2046,7 @@ on_pview_events (GtkWidget *widget
if(make_new_point)
{
/* add the new point and handle refresh for both src and dst view */
- p_add_new_point_refresh(swp, l_x, l_y);
+ p_add_new_point_refresh(swp, l_x, l_y, locate_detail);
return(FALSE);
}
}
@@ -2179,6 +2111,7 @@ on_pview_events (GtkWidget *widget
gtk_adjustment_set_value (GTK_ADJUSTMENT(swp->x_spinbutton_adj), *swp->wpx_ptr);
gtk_adjustment_set_value (GTK_ADJUSTMENT(swp->y_spinbutton_adj), *swp->wpy_ptr);
+
}
}
break;
@@ -2448,7 +2381,6 @@ on_pointcolor_button_changed(GimpColorButton *widget,
,GAP_MORPH_DLG_UPD_REQUEST_REDRAW
);
-
}
} /* end on_pointcolor_button_changed */
@@ -2673,6 +2605,7 @@ on_wp_save_button_clicked (GtkButton *button
,GdkEventButton *bevent
,GapMorphGUIParams *mgup)
{
+ mgup->cancelWorkpointGeneration = TRUE;
p_create_wp_filesel(mgup
,bevent
,TRUE /* save_mode */
@@ -2688,12 +2621,15 @@ on_wp_load_button_clicked (GtkButton *button
,GdkEventButton *bevent
,GapMorphGUIParams *mgup)
{
+ mgup->cancelWorkpointGeneration = TRUE;
p_create_wp_filesel(mgup
,bevent
,FALSE /* save_mode */
);
} /* end on_wp_load_button_clicked */
+
+
/* --------------------------------
* on_wp_shape_button_clicked
* --------------------------------
@@ -2707,22 +2643,47 @@ on_wp_shape_button_clicked (GtkButton *button
if(mgup)
{
gboolean clear_old_workpoint_set;
+ gboolean autoEdgeShape;
+
+ if(mgup->workpointGenerationBusy == TRUE)
+ {
+ return;
+ }
clear_old_workpoint_set = TRUE;
+ autoEdgeShape = FALSE;
if(bevent)
{
if(bevent->state & GDK_SHIFT_MASK)
{
clear_old_workpoint_set = FALSE;
}
+ if(bevent->state & GDK_CONTROL_MASK)
+ {
+ autoEdgeShape = TRUE;
+ }
}
if(clear_old_workpoint_set)
{
gap_morph_exec_free_workpoint_list(&mgup->mgpp->master_wp_list);
}
- mgup->num_shapepoints = (gint32)GTK_ADJUSTMENT(mgup->num_shapepoints_adj)->value;;
- p_generate_outline_shape_workpoints(mgup);
+ mgup->num_shapepoints = (gint32)GTK_ADJUSTMENT(mgup->num_shapepoints_adj)->value;
+
+ if(autoEdgeShape == TRUE)
+ {
+ gtk_widget_show (mgup->progressBar);
+ mgup->cancelWorkpointGeneration = FALSE;
+ mgup->mgpp->numWorkpoints = mgup->num_shapepoints;
+ mgup->mgpp->numOutlinePoints = 0;
+ gap_moprhShapeDetectionEdgeBased(mgup, &mgup->cancelWorkpointGeneration);
+ gtk_widget_hide (mgup->progressBar);
+ }
+ else
+ {
+ p_generate_outline_shape_workpoints(mgup);
+ }
+
p_set_upd_timer_request(mgup
,GAP_MORPH_DLG_UPD_REQUEST_FULL_REFRESH
,GAP_MORPH_DLG_UPD_REQUEST_FULL_REFRESH
@@ -3195,7 +3156,7 @@ p_create_subwin(GapMorphSubWin *swp
row = 0;
- /* the layer seletion combobox */
+ /* the layer selection combobox */
// label = gtk_label_new( _("Layer:"));
//
// gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
@@ -3229,7 +3190,7 @@ p_create_subwin(GapMorphSubWin *swp
row++;
- /* the x koordinate label */
+ /* the x coordinate label */
label = gtk_label_new (_("X:"));
gtk_widget_show (label);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
@@ -3237,7 +3198,7 @@ p_create_subwin(GapMorphSubWin *swp
(GtkAttachOptions) (0), 4, 0);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
- /* the x koordinate spinbutton */
+ /* the x coordinate spinbutton */
adj = gtk_adjustment_new ( 0
, 0
, 10000 /* constrain to image width is done later */
@@ -3260,7 +3221,7 @@ p_create_subwin(GapMorphSubWin *swp
- /* the y koordinate label */
+ /* the y coordinate label */
label = gtk_label_new (_("Y:"));
gtk_widget_show (label);
gtk_table_attach (GTK_TABLE (table), label, 2, 3, row, row+1,
@@ -3269,7 +3230,7 @@ p_create_subwin(GapMorphSubWin *swp
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
- /* the y koordinate spinbutton */
+ /* the y coordinate spinbutton */
adj = gtk_adjustment_new ( 0
, 0
, 10000 /* constrain to image height is done later */
@@ -3652,7 +3613,9 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
GTK_FILL, 0, 0, 0 );
gimp_help_set_help_data(button,
_("Create N workpoints following the outline shape of the layer."
- "(the shape detection is looking for non-transparent pixels)."
+ "the simple shape detection is looking for non-transparent pixels."
+ "CTRL-click uses an edge detection based shape detection algorithm "
+ "that is capable to operate on opaque images."
"SHIFT-click: adds the new points and keeps the old points")
, NULL);
gtk_widget_show (button);
@@ -3712,7 +3675,6 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
, G_CALLBACK (gimp_double_adjustment_update)
, &mgup->mgpp->affect_radius);
-
/* the deform intensity label */
label = gtk_label_new (_("Intensity:"));
gtk_widget_show (label);
@@ -3731,12 +3693,12 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
mgup->gravity_intensity_spinbutton = spinbutton;
gtk_widget_show (spinbutton);
+ gtk_widget_set_size_request (spinbutton, 80, -1);
gtk_table_attach (GTK_TABLE (table), spinbutton, 5, 7, row, row+1,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0),
0, 0);
- gtk_widget_set_size_request (spinbutton, 80, -1);
gimp_help_set_help_data (spinbutton, _("Deform intensity.")
, NULL);
g_signal_connect (G_OBJECT (adj), "value_changed"
@@ -3890,6 +3852,8 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
row++;
+
+
/* the number_of_points label */
label = gtk_label_new ("---");
mgup->warp_info_label = label;
@@ -3939,12 +3903,96 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
row++;
+ /* the LOCATE label */
+ label = gtk_label_new (_("Locate:"));
+ gtk_widget_show (label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ /* locate move radius spinbutton */
+ adj = gtk_adjustment_new ( mgup->mgpp->locateDetailMoveRadius
+ , 0
+ , 200
+ , 1, 10, 0);
+ mgup->locate_radius_spinbutton_adj = adj;
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1, 0);
+ gtk_widget_show (spinbutton);
+
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 1, 2, row, row+1,
+ (GtkAttachOptions) (0),
+ (GtkAttachOptions) (0),
+ 0, 0);
+
+ gtk_widget_set_size_request (spinbutton, 80, -1);
+ gimp_help_set_help_data (spinbutton, _("Locate radius in pixels."
+ " radius for automatically point locate feature "
+ " triggered by CTRL when setting workpoints."
+ "(Not relevant for rendering)")
+ , NULL);
+ g_signal_connect (G_OBJECT (adj), "value_changed"
+ , G_CALLBACK (gimp_int_adjustment_update)
+ , &mgup->mgpp->locateDetailMoveRadius);
+
+ /* locate shape radius spinbutton */
+ adj = gtk_adjustment_new ( mgup->mgpp->locateDetailShapeRadius
+ , GAP_LOCATE_MIN_REF_SHAPE_RADIUS
+ , GAP_LOCATE_MAX_REF_SHAPE_RADIUS
+ , 1, 10, 0);
+ mgup->locate_shape_radius_spinbutton_adj = adj;
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1, 0);
+ gtk_widget_show (spinbutton);
+
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, row, row+1,
+ (GtkAttachOptions) (0),
+ (GtkAttachOptions) (0),
+ 0, 0);
+
+ gtk_widget_set_size_request (spinbutton, 80, -1);
+ gimp_help_set_help_data (spinbutton, _("Locate Shaperadius in pixels."
+ " Defines shape size as area around workpoint to be compared "
+ " when loacting corresponding coordinate"
+ "(Not relevant for rendering)")
+ , NULL);
+ g_signal_connect (G_OBJECT (adj), "value_changed"
+ , G_CALLBACK (gimp_int_adjustment_update)
+ , &mgup->mgpp->locateDetailShapeRadius);
+
+
+
+ /* locate edge detection threshold spinbutton */
+ adj = gtk_adjustment_new ( mgup->mgpp->edgeColordiffThreshold
+ , 0.01
+ , 0.35
+ , 0.01, 0.1, 0.0);
+ mgup->locate_edge_threshold_spinbutton_adj = adj;
+ spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0.01, 2);
+ gtk_widget_show (spinbutton);
+
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 3, 4, row, row+1,
+ (GtkAttachOptions) (0),
+ (GtkAttachOptions) (0),
+ 0, 0);
+
+ gtk_widget_set_size_request (spinbutton, 80, -1);
+ gimp_help_set_help_data (spinbutton, _("Edge detection threshold"
+ " for automatically point locate feature "
+ " triggered by CTRL when setting workpoints."
+ "(Not relevant for rendering)")
+ , NULL);
+ g_signal_connect (G_OBJECT (adj), "value_changed"
+ , G_CALLBACK (gimp_double_adjustment_update)
+ , &mgup->mgpp->edgeColordiffThreshold);
+
+
+
/* the create tween checkbutton */
checkbutton = gtk_check_button_new_with_label ( _("Create Layers"));
mgup->create_tween_layers_checkbutton = checkbutton;
gtk_widget_show (checkbutton);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgup->mgpp->create_tween_layers);
- gtk_table_attach( GTK_TABLE(table), checkbutton, 0, 2, row, row+1,
+ gtk_table_attach( GTK_TABLE(table), checkbutton, 5, 6, row, row+1,
GTK_FILL, 0, 0, 0 );
g_signal_connect (checkbutton, "toggled",
G_CALLBACK (on_create_tween_layers_toggled_callback),
@@ -3958,7 +4006,7 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
checkbutton = gtk_check_button_new_with_label ( _("Quality"));
mgup->use_quality_wp_selection_checkbutton = checkbutton;
gtk_widget_show (checkbutton);
- gtk_table_attach( GTK_TABLE(table), checkbutton, 4, 5, row, row+1,
+ gtk_table_attach( GTK_TABLE(table), checkbutton, 6, 7, row, row+1,
GTK_FILL, 0, 0, 0 );
g_signal_connect (checkbutton, "toggled",
G_CALLBACK (on_use_quality_wp_selection_toggled_callback),
@@ -3973,7 +4021,7 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
checkbutton = gtk_check_button_new_with_label ( _("Lines"));
mgup->show_lines_checkbutton = checkbutton;
gtk_widget_show (checkbutton);
- gtk_table_attach( GTK_TABLE(table), checkbutton, 6, 7, row, row+1,
+ gtk_table_attach( GTK_TABLE(table), checkbutton, 7, 8, row, row+1,
GTK_FILL, 0, 0, 0 );
g_signal_connect (checkbutton, "toggled",
G_CALLBACK (on_show_lines_toggled_callback),
@@ -3983,6 +4031,12 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
, NULL);
+ /* the progress bar */
+ mgup->progressBar = gtk_progress_bar_new ();
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mgup->progressBar), " ");
+ gtk_widget_hide (mgup->progressBar);
+ gtk_table_attach( GTK_TABLE(table), mgup->progressBar, 8, 10, row, row+1,
+ GTK_FILL, 0, 0, 0 );
@@ -4019,6 +4073,8 @@ gap_morph_dialog(GapMorphGlobalParams *mgpp)
mgup = &morph_gui_params;
mgup->mgpp = mgpp;
+ mgup->workpointGenerationBusy = FALSE;
+ mgup->cancelWorkpointGeneration = FALSE;
if(gap_debug) printf("gap_morph_dialog: START mgpp->master_wp_list: %d\n", (int)mgpp->master_wp_list);
@@ -4033,7 +4089,7 @@ gap_morph_dialog(GapMorphGlobalParams *mgpp)
gtk_widget_show (mgup->shell);
/* fit both src and dst into preview size
- * (this implicite forces full refresh)
+ * (this implicitly forces full refresh)
*/
p_scale_wp_list(mgup);
p_fit_zoom_into_pview_size(&mgup->src_win);
diff --git a/gap/gap_morph_dialog.h b/gap/gap_morph_dialog.h
index 75dbce8..20f3670 100644
--- a/gap/gap_morph_dialog.h
+++ b/gap/gap_morph_dialog.h
@@ -78,6 +78,9 @@ typedef struct GapMorphGUIParams { /* nickname: mgup */
GtkObject *tween_steps_spinbutton_adj;
GtkObject *affect_radius_spinbutton_adj;
+ GtkObject *locate_edge_threshold_spinbutton_adj;
+ GtkObject *locate_radius_spinbutton_adj;
+ GtkObject *locate_shape_radius_spinbutton_adj;
GtkObject *gravity_intensity_spinbutton_adj;
GtkWidget *gravity_intensity_spinbutton;
GtkWidget *use_gravity_checkbutton;
@@ -121,6 +124,10 @@ typedef struct GapMorphGUIParams { /* nickname: mgup */
GtkWidget *workpoint_file_upper_label;
GtkWidget *workpoint_lower_label;
GtkWidget *workpoint_upper_label;
+
+ GtkWidget *progressBar;
+ gboolean workpointGenerationBusy;
+ gboolean cancelWorkpointGeneration;
} GapMorphGUIParams;
diff --git a/gap/gap_morph_exec.c b/gap/gap_morph_exec.c
index cc4969a..45d37ca 100644
--- a/gap/gap_morph_exec.c
+++ b/gap/gap_morph_exec.c
@@ -64,7 +64,6 @@
* (gives bad quality results, you should NOT enable this option)
*/
#undef GAP_MORPH_USE_SIMPLE_PIXEL_WARP
-//#define GAP_MORPH_USE_SIMPLE_PIXEL_WARP
#define GAP_MORPH_NEAR_SQR_RADIUS 64
#define GAP_MORPH_NEAR_RADIUS 4
@@ -1732,7 +1731,6 @@ p_calculate_work_point_movement(gdouble total_steps
gdouble scaley;
- if(gap_debug) printf ("p_calculate_work_point_movement: START\n");
if(forward_move)
{
@@ -1744,10 +1742,31 @@ p_calculate_work_point_movement(gdouble total_steps
scalex = (gdouble)curr_width / (gdouble)gimp_drawable_width(dst_layer_id);
scaley = (gdouble)curr_height / (gdouble)gimp_drawable_height(dst_layer_id);
}
+
+ if(gap_debug)
+ {
+ gint32 countPoints;
+
+ countPoints = 0;
+ for(wp_elem_orig = master_list; wp_elem_orig != NULL; wp_elem_orig = (GapMorphWorkPoint *)wp_elem_orig->next)
+ {
+ countPoints++;
+ }
+ printf ("p_calculate_work_point_movement: START total_steps:%.2f current_step:%.2f forward_move:%d scalex:%.3f scaley:%.3f master_list:%d countPoints:%d\n"
+ ,(float)total_steps
+ ,(float)current_step
+ ,(int)forward_move
+ ,(float)scalex
+ ,(float)scaley
+ ,(int)master_list
+ ,(int)countPoints
+ );
+ }
wp_list = NULL;
wp_elem_prev = NULL;
+
for(wp_elem_orig = master_list; wp_elem_orig != NULL; wp_elem_orig = (GapMorphWorkPoint *)wp_elem_orig->next)
{
wp_elem_dup = g_new(GapMorphWorkPoint, 1);
@@ -1964,8 +1983,8 @@ p_layer_warp_move (GapMorphWorkPoint *wp_list_1
if(mgpp->have_workpointsets)
{
/* pick based on 2 sets of workpoints */
- p_pixel_warp_multipick(wcap_1 /// XXXXX list1
- , wcap_2 /// XXXXX list2
+ p_pixel_warp_multipick(wcap_1 /* list1 */
+ , wcap_2 /* list2 */
, wp_mix_factor
, l_col
, l_row
@@ -2014,7 +2033,14 @@ p_layer_warp_move (GapMorphWorkPoint *wp_list_1
* }
*/
- gimp_progress_update(l_total_progress);
+ if(mgpp->progress_callback_fptr == NULL)
+ {
+ gimp_progress_update(l_total_progress);
+ }
+ else
+ {
+ (*mgpp->progress_callback_fptr)(l_total_progress, mgpp->callback_data_ptr);
+ }
}
}
@@ -2405,6 +2431,16 @@ p_create_morph_tween_frame(gint32 total_steps
}
else
{
+ if(gap_debug)
+ {
+ printf("simple workpoint setting for BG LAYER total_steps:%.2f current_step:%.2f forward_move:%d wp_mix_factor:%.3f\n"
+ , (float)total_steps
+ , (float)current_step
+ , (int)forward_move
+ , (float)wp_mix_factor
+ );
+ }
+
/* simple workpoint settings for src_layer */
curr_wp_list_1 = p_calculate_work_point_movement((gdouble)total_steps
,(gdouble)current_step
@@ -2472,6 +2508,16 @@ p_create_morph_tween_frame(gint32 total_steps
}
else
{
+ if(gap_debug)
+ {
+ printf("simple workpoint setting for TOP LAYER total_steps:%.2f current_step:%.2f forward_move:%d wp_mix_factor:%.3f\n"
+ , (float)total_steps
+ , (float)current_step
+ , (int)forward_move
+ , (float)wp_mix_factor
+ );
+ }
+
/* simple workpoint settings for sc_layer */
curr_wp_list_1 = p_calculate_work_point_movement((gdouble)total_steps
,(gdouble)current_step
@@ -2506,11 +2552,7 @@ p_create_morph_tween_frame(gint32 total_steps
}
/* merge BG and TOP Layer (does mix opacity according to current step) */
-// merged_layer_id =
-// gap_image_merge_visible_layers(curr_image_id, GIMP_EXPAND_AS_NECESSARY);
-
- merged_layer_id =
- p_mix_layers(curr_image_id
+ merged_layer_id = p_mix_layers(curr_image_id
,bg_layer_id
,top_layer_id
,curr_width
@@ -2601,7 +2643,10 @@ p_get_tween_steps_and_layerstacks(GapMorphGlobalParams *mgpp, GapMorphExeLayerst
for(ii=0; ii < mlayers->dst_nlayers; ii++)
{
- if(gap_debug) printf("dst: id[%d]: %d\n", (int)ii, (int)mlayers->dst_layers[ii] );
+ if(gap_debug)
+ {
+ printf("dst: id[%d]: %d\n", (int)ii, (int)mlayers->dst_layers[ii] );
+ }
if(mlayers->dst_layers[ii] == mgpp->fdst_layer_id)
{
@@ -2716,7 +2761,7 @@ gap_morph_execute(GapMorphGlobalParams *mgpp)
mgpp->render_mode = GAP_MORPH_RENDER_MODE_WARP;
}
- if(mgpp->do_progress)
+ if((mgpp->do_progress) && (mgpp->progress_callback_fptr == NULL))
{
if(mgpp->render_mode == GAP_MORPH_RENDER_MODE_MORPH)
{
@@ -2825,7 +2870,14 @@ gap_morph_execute(GapMorphGlobalParams *mgpp)
if(mgpp->do_progress)
{
mgpp->master_progress = (gdouble)current_step / (gdouble)(MAX(1, last_step));
- gimp_progress_update(mgpp->master_progress);
+ if(mgpp->progress_callback_fptr == NULL)
+ {
+ gimp_progress_update(mgpp->master_progress);
+ }
+ else
+ {
+ (*mgpp->progress_callback_fptr)(mgpp->master_progress, mgpp->callback_data_ptr);
+ }
}
@@ -2912,6 +2964,13 @@ p_create_simple_fade_tween_frame(gint32 total_steps
,(gdouble)0.0
,(gdouble)100.0
);
+ if(mgpp->do_progress)
+ {
+ if(mgpp->progress_callback_fptr != NULL)
+ {
+ (*mgpp->progress_callback_fptr)(0.05, mgpp->callback_data_ptr);
+ }
+ }
merged_layer_id =
p_mix_layers(curr_image_id
@@ -2922,8 +2981,16 @@ p_create_simple_fade_tween_frame(gint32 total_steps
,(gdouble)(curr_opacity / 100.0)
);
+ if(mgpp->do_progress)
+ {
+ if(mgpp->progress_callback_fptr != NULL)
+ {
+ (*mgpp->progress_callback_fptr)(1.0, mgpp->callback_data_ptr);
+ }
+ }
mgpp->master_progress += mgpp->layer_progress_step;
+
/* DEBUG code: show duplicate of the temporary tween image */
if(FALSE)
{
@@ -2956,6 +3023,8 @@ gap_morph_render_one_of_n_tweens(GapMorphGlobalParams *mgpp, gdouble total_steps
gint32 new_layer_id;
GapMorphExeLayerstack mlayers_struct;
GapMorphExeLayerstack *mlayers;
+ gint32 bck_tween_steps;
+ gint32 ret_layer_id;
mlayers = &mlayers_struct;
mlayers->tab_wp_sets = NULL;
@@ -2971,21 +3040,29 @@ gap_morph_render_one_of_n_tweens(GapMorphGlobalParams *mgpp, gdouble total_steps
{
if(*mgpp->workpoint_file_lower != '\0')
{
- /* load a single workpointfile
- * (note: we use general procedure p_build_wp_set_table and handle the single workpointfile
- * as mix of upper and lower set that both refere to the same single workpointfile)
- */
- p_build_wp_set_table(mgpp ,mlayers);
+ /* load a single workpointfile */
+ mlayers->available_wp_sets = 1;
+ mlayers->tab_wp_sets = g_new(GapMorphWarpCoreAPI*, mlayers->available_wp_sets);
+
+ mlayers->tab_wp_sets[0] = p_load_workpoint_set(mgpp->workpoint_file_lower, mgpp, mlayers);
+ mgpp->master_wp_list = mlayers->tab_wp_sets[0]->wp_list;
+
mgpp->have_workpointsets = FALSE;
mlayers->available_wp_sets = 0;
}
}
/* overrule the number of steps with the explicite parameter value */
+ bck_tween_steps = mgpp->tween_steps;
mgpp->tween_steps = total_steps;
if(mgpp->do_simple_fade)
{
+ if(gap_debug)
+ {
+ printf("do_simple_fade == TRUE, skip morph and do fade operation instead\n");
+ }
+
/* we have no workpoints available
* in this case make an attempt with a fast fade operation
* (but restricted to frames of same size and type)
@@ -3012,7 +3089,13 @@ gap_morph_render_one_of_n_tweens(GapMorphGlobalParams *mgpp, gdouble total_steps
,mlayers
);
}
- return (gap_image_merge_to_specified_layer(new_layer_id, GIMP_CLIP_TO_IMAGE));
+ ret_layer_id = gap_image_merge_to_specified_layer(new_layer_id, GIMP_CLIP_TO_IMAGE);
+
+ /* restore tween_steps value */
+ mgpp->tween_steps = bck_tween_steps;
+
+ return(ret_layer_id);
+
} /* end gap_morph_render_one_of_n_tweens */
@@ -3045,6 +3128,505 @@ gap_morph_render_one_tween(GapMorphGlobalParams *mgpp)
+
+/* -----------------------------------------------
+ * p_buildTargetTweenFilename
+ * -----------------------------------------------
+ * build filename for the target Tween frame with the specified targetFrameNr number part
+ * with full directory path (specified by tweenDirectory)
+ *
+ * the caller is rsponsible to g_free the returned filename.
+ */
+static char*
+p_buildTargetTweenFilename(GapAnimInfo *ainfo_ptr, const char *tweenDirectory, gint32 targetFrameNr)
+{
+ char *fileBasenameWithoutDir;
+ char *fileNameTarget;
+ char *fileNameTargetWithDir;
+ char *checkName;
+
+
+ fileBasenameWithoutDir = g_path_get_basename(ainfo_ptr->basename);
+ fileNameTarget = g_strdup_printf("%s%06d%s"
+ , fileBasenameWithoutDir
+ , (int)targetFrameNr
+ , ainfo_ptr->extension
+ );
+
+ checkName = g_build_filename(tweenDirectory, fileBasenameWithoutDir, NULL);
+
+ if((strcmp(checkName, ainfo_ptr->basename) == 0)
+ || (*tweenDirectory == '.'))
+ {
+ if(gap_debug)
+ {
+ printf("WARNING: create tween in directory: %s\n"
+ " with additional prefix tween_"
+ " to prevent overwrite of source frames in the same directory\n"
+ , tweenDirectory
+ );
+ }
+ fileNameTarget = g_strdup_printf("tween_%s%06d%s"
+ , fileBasenameWithoutDir
+ , (int)targetFrameNr
+ , ainfo_ptr->extension
+ );
+ }
+ else
+ {
+ fileNameTarget = g_strdup_printf("%s%06d%s"
+ , fileBasenameWithoutDir
+ , (int)targetFrameNr
+ , ainfo_ptr->extension
+ );
+ }
+
+ fileNameTargetWithDir = g_build_filename(tweenDirectory, fileNameTarget, NULL);
+
+
+ if(gap_debug)
+ {
+ printf("checkName: %s\n", checkName);
+ printf("fileBasenameWithoutDir: %s\n", fileBasenameWithoutDir);
+ printf("fileNameTarget: %s\n", fileNameTarget);
+ printf("fileNameTargetWithDir: %s\n", fileNameTargetWithDir);
+ }
+
+ if(fileBasenameWithoutDir) { g_free(fileBasenameWithoutDir); }
+ if(fileNameTarget) { g_free(fileNameTarget); }
+ if(checkName) { g_free(checkName); }
+
+ return(fileNameTargetWithDir);
+
+} /* end p_buildTargetTweenFilename */
+
+
+/* -----------------------------------------------
+ * p_copyAndGetMergedFrameImageLayer
+ * -----------------------------------------------
+ * load the specified frame as temporary image, merge to one layer and return this layer's id.
+ * further copy the original image to the directory specified via tweenDirectory
+ * the filename of the copy is built with original filename and extension but the number part is replaced
+ * by the specified targetFrameNr.
+ *
+ * return -1 if FAILED, or layerId (positive integer) on success
+ */
+gint32
+p_copyAndGetMergedFrameImageLayer(GapAnimInfo *ainfo_ptr, gint32 frameNr
+ , const char *tweenDirectory, gint32 targetFrameNr, gboolean overwrite_flag)
+{
+ gint32 imageId;
+ gint32 layerId;
+
+ layerId = -1;
+ if(ainfo_ptr->new_filename != NULL)
+ {
+ g_free(ainfo_ptr->new_filename);
+ }
+ ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
+ frameNr,
+ ainfo_ptr->extension);
+ if(ainfo_ptr->new_filename == NULL)
+ {
+ printf("could not create frame filename for frameNr:%d\n", (int)frameNr);
+ return -1;
+ }
+
+ if(!g_file_test(ainfo_ptr->new_filename, G_FILE_TEST_EXISTS))
+ {
+ printf("file not found: %s\n", ainfo_ptr->new_filename);
+ return -1;
+ }
+
+ /* load current frame */
+ imageId = gap_lib_load_image(ainfo_ptr->new_filename);
+ if(imageId >= 0)
+ {
+ char *targetTweenFilename;
+
+ layerId = gap_image_merge_visible_layers(imageId, GIMP_CLIP_TO_IMAGE);
+
+ targetTweenFilename = p_buildTargetTweenFilename(ainfo_ptr, tweenDirectory, targetFrameNr);
+
+ if(gap_debug)
+ {
+ printf("COPY: %s ==> %s\n", ainfo_ptr->new_filename, targetTweenFilename);
+ }
+
+
+ if((!g_file_test(targetTweenFilename, G_FILE_TEST_EXISTS))
+ || (overwrite_flag == TRUE))
+ {
+ gap_lib_file_copy(ainfo_ptr->new_filename /* the original file */
+ ,targetTweenFilename /* the copy */
+ );
+ }
+
+
+ }
+
+ return(layerId);
+
+} /* end p_copyAndGetMergedFrameImageLayer */
+
+
+
+
+/* -------------------------------------
+ * p_morph_render_frame_tweens_in_subdir
+ * -------------------------------------
+ *
+ * return one of the newly created morphed tween frame layers
+ * (the one that was created last is picked)
+ *
+ */
+gint32
+p_morph_render_frame_tweens_in_subdir(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp, gboolean *cancelFlagPtr)
+{
+ char *tweenDirectory;
+ char *targetTweenFrameFilename;
+ gint32 l_tween_layer_id;
+ gint32 l_rc;
+ gint l_errno;
+ gint32 frameCount;
+ gint32 currFrameNr;
+ gint32 nextFrameNr;
+ gint32 nextTargetFrameNr;
+ gint32 baseTargetFrameNr;
+ gint32 currTargetFrameNr;
+ gint32 currLayerId;
+ gint32 nextLayerId;
+ gint32 firstFrameNr;
+ gint32 lastFrameNr;
+ gdouble framesToProcess;
+ gdouble nTweenFramesTotal; /* for outer progress (frames that are just copied are not included in progress) */
+ gdouble nTweenFramesDone; /* rendered tween frames so far (frames that are just copied are not included) */
+
+
+
+ l_tween_layer_id = -1;
+ l_errno = 0;
+ targetTweenFrameFilename = NULL;
+
+ if(gap_debug)
+ {
+ printf("p_morph_render_frame_tweens_in_subdir START\n");
+ }
+
+ if((mgpp->tween_subdir[0] == '\0') || (mgpp->tween_subdir[0] == '.'))
+ {
+ /* use same directory as source frames
+ * (in this case tween frame filenames are automatically prefixed with "tween_"
+ * to prevent overwrite of the source frames while processing)
+ */
+ tweenDirectory = g_path_get_dirname(ainfo_ptr->basename);
+ }
+ else
+ {
+ tweenDirectory = gap_file_make_abspath_filename(&mgpp->tween_subdir[0]
+ , ainfo_ptr->basename
+ );
+ }
+
+
+ if(gap_debug)
+ {
+ printf("p_morph_render_frame_tweens_in_subdir tweenDirectory:%s\n"
+ ,tweenDirectory
+ );
+ }
+
+
+
+ if (!g_file_test(tweenDirectory, G_FILE_TEST_IS_DIR))
+ {
+ gap_file_mkdir (tweenDirectory, GAP_FILE_MKDIR_MODE);
+ l_errno = errno;
+ }
+
+ if (!g_file_test(tweenDirectory, G_FILE_TEST_IS_DIR))
+ {
+ g_message (_("Failed to create tween subdirectory: '%s':\n%s")
+ , tweenDirectory
+ , g_strerror (l_errno)
+ );
+ return (-1);
+ }
+
+
+ frameCount = 0;
+ nextLayerId = -1;
+
+ if(gap_lib_chk_framerange(ainfo_ptr) != 0)
+ {
+ return(0);
+ }
+
+
+ firstFrameNr = CLAMP(mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr);
+ currFrameNr = firstFrameNr;
+ currTargetFrameNr = firstFrameNr;
+ nextTargetFrameNr = firstFrameNr;
+ baseTargetFrameNr = firstFrameNr;
+
+ currLayerId = p_copyAndGetMergedFrameImageLayer(ainfo_ptr
+ , currTargetFrameNr
+ , tweenDirectory
+ , currTargetFrameNr
+ , mgpp->overwrite_flag
+ );
+ nextFrameNr = currFrameNr + 1;
+
+
+ lastFrameNr = MIN(mgpp->range_to, ainfo_ptr->last_frame_nr);
+ nTweenFramesTotal = MAX(1.0, (lastFrameNr - firstFrameNr) * mgpp->master_tween_steps);
+ nTweenFramesDone = 0;
+
+ /* loop to process all frames within the specified frame range */
+ while(TRUE)
+ {
+ gdouble nFrames;
+ gint32 tweenFramesToBeCreated;
+ gdouble l_current_step;
+
+ /* recalculate (in case range_to is changed on GUI while processing is already running) */
+ lastFrameNr = MIN(mgpp->range_to, ainfo_ptr->last_frame_nr);
+ framesToProcess = MAX(1, (lastFrameNr - currFrameNr) -1);
+ nTweenFramesTotal = MAX(1.0, (lastFrameNr - firstFrameNr) * mgpp->master_tween_steps);
+
+ if (nextFrameNr > lastFrameNr)
+ {
+ if(gap_debug)
+ {
+ printf("BREAK nextFrameNr:%d lastFrameNr:%d\n"
+ ,(int)nextFrameNr
+ ,(int)lastFrameNr
+ );
+ }
+
+ break;
+ }
+
+ nFrames = nextFrameNr - currFrameNr;
+ tweenFramesToBeCreated = (nFrames * mgpp->master_tween_steps) + (nFrames -1);
+
+ nextTargetFrameNr = baseTargetFrameNr + (1 + tweenFramesToBeCreated);
+
+ if(gap_debug)
+ {
+ printf("ATTR currFrameNr:%d nextFrameNr:%d master_tween_steps:%d tweenFramesToBeCreated:%d baseTargetFrameNr:%d nextTargetFrameNr:%d\n"
+ ,(int)currFrameNr
+ ,(int)nextFrameNr
+ ,(int)mgpp->master_tween_steps
+ ,(int)tweenFramesToBeCreated
+ ,(int)baseTargetFrameNr
+ ,(int)nextTargetFrameNr
+ );
+ }
+ nextLayerId = p_copyAndGetMergedFrameImageLayer(ainfo_ptr
+ , nextFrameNr
+ , tweenDirectory
+ , nextTargetFrameNr
+ , mgpp->overwrite_flag
+ );
+
+ if((nextLayerId >= 0) && (currLayerId >= 0))
+ {
+ char *workpointFileName;
+ gboolean success;
+
+
+ success = TRUE;
+ baseTargetFrameNr = nextTargetFrameNr;
+
+ workpointFileName = gap_lib_alloc_fname(ainfo_ptr->basename,
+ currFrameNr,
+ "." GAP_MORPH_WORKPOINT_EXTENSION);
+ if(g_file_test(workpointFileName, G_FILE_TEST_EXISTS))
+ {
+ if(gap_debug)
+ {
+ printf("USING workpointfile: %s\n"
+ , workpointFileName
+ );
+ }
+ g_snprintf(&mgpp->workpoint_file_lower[0]
+ , sizeof(mgpp->workpoint_file_lower)
+ , "%s"
+ , workpointFileName
+ );
+ g_snprintf(&mgpp->workpoint_file_upper[0]
+ , sizeof(mgpp->workpoint_file_upper)
+ , "%s"
+ , workpointFileName
+ );
+
+ mgpp->do_simple_fade = FALSE;
+ }
+ else
+ {
+ if(gap_debug)
+ {
+ printf("have no valid frame specific workpointfile: %s\n"
+ " (reset names to force simple fade operaton\n"
+ , workpointFileName
+ );
+ }
+ mgpp->workpoint_file_lower[0] = '\0';
+ mgpp->workpoint_file_upper[0] = '\0';
+ mgpp->do_simple_fade = TRUE;
+ }
+
+
+ mgpp->osrc_layer_id = currLayerId;
+ mgpp->fdst_layer_id = nextLayerId;
+
+ /* loop to generate tween frames between currentFrameNr and nextFrameNr */
+ for (l_current_step = 1; l_current_step <= tweenFramesToBeCreated; l_current_step++)
+ {
+ gint32 l_tween_tmp_image_id;
+
+ currTargetFrameNr++;
+
+ mgpp->master_progress = 0.0;
+ mgpp->layer_progress_step = 0.5;
+
+ if(l_tween_layer_id >= 0)
+ {
+ /* delete the previous handled tween frame image in memory.
+ * (that is already saved to disk)
+ * we keep only the last one opened in gimp.
+ */
+ gap_image_delete_immediate(gimp_drawable_get_image(l_tween_layer_id));
+ }
+
+ if(targetTweenFrameFilename != NULL)
+ {
+ g_free(targetTweenFrameFilename);
+ }
+ targetTweenFrameFilename = p_buildTargetTweenFilename(ainfo_ptr
+ , tweenDirectory
+ , currTargetFrameNr);
+ if(targetTweenFrameFilename == NULL)
+ {
+ success = FALSE;
+ break;
+ }
+
+ if(gap_debug)
+ {
+ printf("p_morph_render_frame_tweens_in_subdir creating tween:%s\n"
+ , targetTweenFrameFilename
+ );
+ }
+
+ /* progress and cancel handling */
+ if(mgpp->do_progress)
+ {
+ gdouble percentage;
+ percentage = nTweenFramesDone / nTweenFramesTotal;
+ if(gap_debug)
+ {
+ printf("\nTWEEN_PROGRESS: nTweenFramesDone:%.0f nTweenFramesTotal:%.0f percentage:%.5f\n\n"
+ , (float)nTweenFramesDone
+ , (float)nTweenFramesTotal
+ , (float)percentage
+ );
+ }
+ if(mgpp->master_progress_callback_fptr != NULL)
+ {
+ (*mgpp->master_progress_callback_fptr)( nTweenFramesDone
+ , nTweenFramesTotal
+ , targetTweenFrameFilename
+ , mgpp->callback_data_ptr
+ );
+ }
+ if(cancelFlagPtr != NULL)
+ {
+ if (*cancelFlagPtr == TRUE)
+ {
+ success = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (mgpp->overwrite_flag != TRUE)
+ {
+ if(g_file_test(targetTweenFrameFilename, G_FILE_TEST_EXISTS))
+ {
+ l_tween_layer_id = -1;
+ if(gap_debug)
+ {
+ printf("SKIP tween generation because file: %s already exists"
+ , targetTweenFrameFilename
+ );
+ }
+ continue;
+ }
+ }
+
+
+ /* CALL of the morphing processor */
+
+ l_tween_layer_id = gap_morph_render_one_of_n_tweens(mgpp
+ , tweenFramesToBeCreated +1
+ , l_current_step
+ );
+ l_tween_tmp_image_id = gimp_drawable_get_image(l_tween_layer_id);
+ if(gap_debug)
+ {
+ printf("p_morph_render_frame_tweens_in_subdir saving tween:%s :%d\n"
+ ,targetTweenFrameFilename
+ ,(int)l_tween_tmp_image_id
+ );
+ }
+ l_rc = gap_lib_save_named_frame(l_tween_tmp_image_id, targetTweenFrameFilename);
+ if(targetTweenFrameFilename != NULL)
+ {
+ g_free(targetTweenFrameFilename);
+ targetTweenFrameFilename = NULL;
+ }
+ if (l_rc < 0)
+ {
+ l_tween_layer_id = -1;
+ p_error_message_with_filename(mgpp->run_mode, _("file: %s save failed"), targetTweenFrameFilename);
+ success = FALSE;
+ break;
+ }
+
+ nTweenFramesDone++;
+
+ }
+
+ g_free(workpointFileName);
+ gap_image_delete_immediate(gimp_drawable_get_image(currLayerId));
+ if(!success)
+ {
+ break;
+ }
+
+ currFrameNr = nextFrameNr;
+ currLayerId = nextLayerId;
+ currTargetFrameNr++;
+
+ }
+ nextFrameNr++;
+
+ }
+
+ if(nextLayerId >= 0)
+ {
+ gap_image_delete_immediate(gimp_drawable_get_image(nextLayerId));
+ }
+
+ return(l_tween_layer_id);
+
+} /* end p_morph_render_frame_tweens_in_subdir */
+
+
+
+
/* ----------------------------------
* gap_morph_render_frame_tweens
* ----------------------------------
@@ -3053,24 +3635,27 @@ gap_morph_render_one_tween(GapMorphGlobalParams *mgpp)
*
*/
gint32
-gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp)
+gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp, gboolean *cancelFlagPtr)
{
gint32 l_tween_layer_id;
gint32 l_tween_frame_nr;
gint32 l_rc;
gdouble total_steps;
+
+ if(mgpp->create_tweens_in_subdir == TRUE)
+ {
+ return(p_morph_render_frame_tweens_in_subdir(ainfo_ptr, mgpp, cancelFlagPtr));
+ }
+
+
l_tween_layer_id = -1;
total_steps = (mgpp->range_to - mgpp->range_from);
- mgpp->tween_steps = (mgpp->range_to - mgpp->range_from); /// ??? - 1;
+ mgpp->tween_steps = (mgpp->range_to - mgpp->range_from);
mgpp->master_progress = 0.0;
- mgpp->layer_progress_step = 0.5 / (gdouble)(MAX(1.0, (total_steps -1.0)));
- if(mgpp->do_progress)
- {
- gimp_progress_init(_("creating morph tween frames..."));
- }
+ mgpp->layer_progress_step = 0.5;
if (mgpp->tween_steps > 0)
{
@@ -3098,6 +3683,8 @@ gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
return -1;
}
+
+
/* load current frame */
l_tmp_image_id = gap_lib_load_image(ainfo_ptr->new_filename);
@@ -3126,7 +3713,9 @@ gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
gint32 l_tween_tmp_image_id;
gdouble l_current_step;
- mgpp->master_progress = 2.0 * mgpp->layer_progress_step * (l_tween_frame_nr - (mgpp->range_from +1));
+ mgpp->master_progress = 0.0;
+ mgpp->layer_progress_step = 0.5;
+
if(l_tween_layer_id >= 0)
{
@@ -3144,13 +3733,44 @@ gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
l_tween_frame_nr,
ainfo_ptr->extension);
+ /* progress and cancel handling */
if(mgpp->do_progress)
{
- char *l_msg;
- l_msg = g_strdup_printf(_("creating morph tween frame: %d"), (int)l_tween_frame_nr);
- gimp_progress_init(l_msg);
- g_free(l_msg);
+ gdouble percentage;
+ gdouble nTweenFramesDone;
+ gdouble nTweenFramesTotal;
+
+ nTweenFramesDone = l_tween_frame_nr - mgpp->range_from;
+ nTweenFramesTotal = mgpp->range_to - mgpp->range_from;
+
+ percentage = nTweenFramesDone / nTweenFramesTotal;
+ if(gap_debug)
+ {
+ printf("\nTWEEN_PROGRESS: nTweenFramesDone:%.0f nTweenFramesTotal:%.0f percentage:%.5f\n\n"
+ , (float)nTweenFramesDone
+ , (float)nTweenFramesTotal
+ , (float)percentage
+ );
+ }
+ if(mgpp->master_progress_callback_fptr != NULL)
+ {
+ (*mgpp->master_progress_callback_fptr)( nTweenFramesDone
+ , nTweenFramesTotal
+ , ainfo_ptr->new_filename
+ , mgpp->callback_data_ptr
+ );
+ }
+ if(cancelFlagPtr != NULL)
+ {
+ if (*cancelFlagPtr == TRUE)
+ {
+ break;
+ }
+ }
}
+
+
+
if(gap_debug)
{
printf("gap_morph_render_frame_tweens creating tween:%s\n"
diff --git a/gap/gap_morph_exec.h b/gap/gap_morph_exec.h
index b0ca8b8..e973010 100644
--- a/gap/gap_morph_exec.h
+++ b/gap/gap_morph_exec.h
@@ -56,7 +56,7 @@ void gap_morph_exec_get_warp_pick_koords(GapMorphWorkPoint *wp_list
);
gint32 gap_morph_execute(GapMorphGlobalParams *mgpp);
gint32 gap_morph_render_one_tween(GapMorphGlobalParams *mgpp);
-gint32 gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp);
+gint32 gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp, gboolean *cancelFlagPtr);
#endif
diff --git a/gap/gap_morph_main.c b/gap/gap_morph_main.c
index fb2f7cd..e22b8eb 100644
--- a/gap/gap_morph_main.c
+++ b/gap/gap_morph_main.c
@@ -36,6 +36,7 @@
#include "gap_morph_main.h"
#include "gap_morph_exec.h"
+#include "gap_morph_shape.h"
#include "gap_morph_dialog.h"
#include "gap_morph_tween_dialog.h"
#include "gap_pview_da.h"
@@ -52,11 +53,13 @@
#define PLUG_IN_NAME "plug_in_gap_morph_layers"
#define PLUG_IN_NAME_TWEEN "plug_in_gap_morph_tween" /* render missing tween(s) between frames */
#define PLUG_IN_NAME_ONE_TWEEN "plug_in_gap_morph_one_tween" /* single tween rendering */
+#define PLUG_IN_NAME_WORKPOINTS "plug_in_gap_morph_workpoints" /* workpoint generation */
#define PLUG_IN_PRINT_NAME "Morph Layers"
#define PLUG_IN_IMAGE_TYPES "RGBA, GRAYA"
#define PLUG_IN_AUTHOR "Wolfgang Hofer (hof gimp org)"
#define PLUG_IN_COPYRIGHT "Wolfgang Hofer"
+#define LOCATE_COLORDIFF_THRESHOLD 0.05;
int gap_debug = 0; /* 1 == print debug infos , 0 dont print debug infos */
@@ -86,7 +89,20 @@ static GapMorphGlobalParams global_params =
, -1 /* long range_from */
, -1 /* long range_to */
, FALSE /* gboolean overwrite_flag */
+, FALSE /* gboolean append_flag */
, FALSE /* gboolean do_simple_fade */
+, 0.08 /* gdouble edgeColordiffThreshold */
+, 0.05 /* gdouble locateColordiffThreshold */
+, 5 /* gint32 locateDetailShapeRadius */
+, 70 /* gint32 locateDetailMoveRadius */
+, 150 /* gint32 numWorkpoints */
+, 32 /* gint32 numOutlinePoints */
+, 1 /* gint32 master_tween_steps */
+, TRUE /* gboolean create_tweens_in_subdir */
+, "tween_frames\0" /* char tween_subdir[1024] */
+, NULL /* master_progress_callback_fptr */
+, NULL /* progress_callback_fptr */
+, NULL /* gpointer callback_data_ptr */
};
@@ -97,7 +113,7 @@ static void run (const gchar *name,
gint *nreturn_vals, /* number of parameters returned */
GimpParam ** return_vals); /* parameters to be returned */
-static gint32 p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp);
+static gint32 p_handle_tween_processing_pug_ins(GapMorphGlobalParams *mgpp, const char *name);
/* Global Variables */
GimpPlugInInfo PLUG_IN_INFO =
@@ -158,6 +174,22 @@ static GimpParamDef out_one_tween_args[] = {
{ GIMP_PDB_DRAWABLE, "tween_drawable", "newly created tween (a layer in a newly created image)"},
};
+static GimpParamDef in_workpoint_args[] = {
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive."},
+ { GIMP_PDB_IMAGE, "start_image", "from frame image (must be a frame with GIMP-GAP typical number part in the imagefilename)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "ignored"},
+ { GIMP_PDB_INT32, "to_frame_nr", "frame number of the next available frame"},
+ { GIMP_PDB_INT32, "numWorkpoints", "number of workpoints to be generated (per workpoint file)"},
+ { GIMP_PDB_INT32, "numOutlinePoints", "number of additional workpoint on outline (per workpoint file)"},
+ { GIMP_PDB_INT32, "locateDetailMoveRadius", "radius for locating corresponding coordinate"},
+ { GIMP_PDB_INT32, "locateDetailShapeRadius", "shape radius in pixels (3 to 16) for locating corresponding coordinate"},
+ { GIMP_PDB_FLOAT, "locateColordiffThreshold", "0.05 (0.01 - 0.1) "},
+ { GIMP_PDB_FLOAT, "edgeColordiffThreshold", "0.05 (0.0 - 1.0) edge detection threshold (the generated workpoints are picked at detected edges) "},
+ };
+static GimpParamDef out_workpoint_args[] = {
+ { GIMP_PDB_INT32, "num_workpointfiles", "number of successfully generated workpoint files"},
+ };
+
MAIN ()
static void query (void)
@@ -175,12 +207,12 @@ static void query (void)
"(or how much layers to modify depending on the create_tween_layers parameter) "
"source and destination may differ in size and can be in different images. "
"for WARP render_mode (1) there will be just Move Deformation. "
- "Deformation is controled by workpoints. Workpoints are created and edited with the help "
+ "Deformation is controlled by workpoints. Workpoints are created and edited with the help "
"of the INTERACTIVE GUI and saved to file(s). "
"For the NON-INTERACTIVE runmode you must provide the filename of such a file. "
"Provide 2 filenames if you want to operate with multiple workpoint sets. "
"In that case your workpoint files can have a 2 digit numberpart. "
- "This will implicite select all filenames with numbers inbetween as well."
+ "This will implicitly select all filenames with numbers in between as well."
,
PLUG_IN_AUTHOR,
PLUG_IN_COPYRIGHT,
@@ -196,7 +228,7 @@ static void query (void)
/* the actual installation of the plugin */
gimp_install_procedure (PLUG_IN_NAME_TWEEN,
- "Render tween frames via morhing",
+ "Render tween frames via morphing",
"This plug-in creates and saves image frames that are a mix of the specified image frame and the frame with to_frame_nr, "
"The typical usage is to create the frame images of missing frame numbers in a series of anim frame images. "
"the overwrite flag allows overwriting of already existing frames between the start frame image "
@@ -220,12 +252,12 @@ static void query (void)
/* the actual installation of the plugin */
gimp_install_procedure (PLUG_IN_NAME_ONE_TWEEN,
- "Render one tween via morhing",
+ "Render one tween via morphing",
"This plug-in creates a new image that is a mix of the specified src_drawable and dst_drawable, "
"the mixing is done based on a morphing transformation where the tween_mix_factor "
"determines how much the result looks like source or destination. "
"source and destination may differ in size and can be in different images. "
- "Morphing is controled by workpoints. A Workpoint file can be created and edited with the help "
+ "Morphing is controlled by workpoints. A Workpoint file can be created and edited with the help "
"of the Morph feature in the Video menu. "
"Note: without workpoints the resulting tween is calculated as simple fade operation. "
,
@@ -242,54 +274,81 @@ static void query (void)
);
+ /* the actual installation of the plugin */
+ gimp_install_procedure (PLUG_IN_NAME_WORKPOINTS,
+ "Generate workpoint files for tween frame morphing",
+ "This plug-in generates workpoint files for the specifed frame range."
+ "The generated files are saved with the same name as the frame name(s) "
+ "but with extension .morphpoints "
+ ,
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ GAP_VERSION_WITH_DATE,
+ N_("Morph Workpoint Generator..."),
+ PLUG_IN_IMAGE_TYPES,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (in_workpoint_args),
+ G_N_ELEMENTS (out_workpoint_args),
+ in_workpoint_args,
+ out_workpoint_args
+ );
+
{
/* Menu names */
const char *menupath_image_video_morph = N_("<Image>/Video/Morph/");
- //gimp_plugin_menu_branch_register("<Image>", "Video/Morph");
-
gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_video_morph);
gimp_plugin_menu_register (PLUG_IN_NAME_TWEEN, menupath_image_video_morph);
gimp_plugin_menu_register (PLUG_IN_NAME_ONE_TWEEN, menupath_image_video_morph);
+ gimp_plugin_menu_register (PLUG_IN_NAME_WORKPOINTS, menupath_image_video_morph);
}
}
/* ----------------------------------
- * p_handle_PLUG_IN_NAME_TWEEN
+ * p_handle_tween_processing_pug_ins
* ----------------------------------
*
* return the newly created tween morphed layer
*
*/
static gint32
-p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
+p_handle_tween_processing_pug_ins(GapMorphGlobalParams *mgpp, const char *name)
{
gint32 l_tween_layer_id;
GapAnimInfo *ainfo_ptr;
- gboolean l_rc;
gboolean l_run_flag;
l_tween_layer_id = -1;
- l_rc = FALSE;
l_run_flag = TRUE;
+ if(gap_debug)
+ {
+ printf("p_handle_tween_processing_pug_ins name:%s\n", name);
+ }
+
ainfo_ptr = gap_lib_alloc_ainfo(mgpp->image_id, mgpp->run_mode);
if(ainfo_ptr != NULL)
{
if (0 == gap_lib_dir_ainfo(ainfo_ptr))
{
mgpp->range_from = ainfo_ptr->curr_frame_nr;
- /* mgpp->range_to is already set at noninteractive call. for interacive cals this is set in the following dialog
+ /* mgpp->range_to is already set at non-interactive call. for interactive calls this is set in the following dialog
*/
if(mgpp->run_mode == GIMP_RUN_INTERACTIVE)
{
+ if(gap_lib_chk_framerange(ainfo_ptr) != 0)
+ {
+ return(-1);
+ }
+
if(0 != gap_lib_chk_framechange(ainfo_ptr)) { l_run_flag = FALSE; }
else
{
- if(*ainfo_ptr->extension == '\0' && ainfo_ptr->frame_cnt == 0)
+ if((*ainfo_ptr->extension == '\0')
+ || (ainfo_ptr->frame_cnt == 0))
{
- /* plugin was called on a frame without extension and without framenumer in its name
+ /* plugin was called on a frame without extension and without framenumber in its name
* (typical for new created images named like 'Untitled'
*/
g_message(_("Operation cancelled.\n"
@@ -298,11 +357,28 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
"==> Rename your image, then try again."));
return -1;
}
- l_rc = gap_morph_frame_tweens_dialog(ainfo_ptr, mgpp);
+ if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+ {
+ l_tween_layer_id = gap_morph_frame_tweens_dialog(ainfo_ptr, mgpp);
+ /* this dialog also perform processing while dialog window is open
+ * (no need to run non-interactive when dialog returns) */
+ l_run_flag = FALSE;
+ }
+ else if (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0)
+ {
+ l_tween_layer_id = gap_morph_generate_frame_tween_workpoints_dialog(ainfo_ptr, mgpp);
+ /* this dialog also perform processing while dialog window is open
+ * (no need to run non-interactive when dialog returns) */
+ l_run_flag = FALSE;
+ }
+ else
+ {
+ return -1;
+ }
mgpp->do_progress = TRUE;
}
- if((0 != gap_lib_chk_framechange(ainfo_ptr)) || (!l_rc))
+ if(0 != gap_lib_chk_framechange(ainfo_ptr))
{
l_run_flag = FALSE;
}
@@ -311,8 +387,39 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
if(l_run_flag == TRUE)
{
- /* render tween frames and write them as framefiles to disc () */
- l_tween_layer_id = gap_morph_render_frame_tweens(ainfo_ptr, mgpp);
+ gboolean cancelFlag;
+
+ cancelFlag = FALSE;
+ if(gap_debug)
+ {
+ printf("l_run_flag is TRUE, running...%s\n", name);
+ }
+
+ if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+ {
+ /* render tween frames and write them as framefiles to disc () */
+ l_tween_layer_id = gap_morph_render_frame_tweens(ainfo_ptr
+ , mgpp
+ , &cancelFlag
+ );
+ }
+ else if (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0)
+ {
+ /* non-interactive workpoint generation (progressBar is NULL) */
+ l_tween_layer_id = gap_morph_shape_generate_frame_tween_workpoints(ainfo_ptr
+ , mgpp
+ , NULL /* masterProgressBar */
+ , NULL /* progressBar */
+ , &cancelFlag
+ );
+ }
+ }
+ else
+ {
+ if(gap_debug)
+ {
+ printf("l_run_flag is FALSE, terminating...%s\n", name);
+ }
}
}
@@ -321,7 +428,7 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
return(l_tween_layer_id);
-} /* end p_handle_PLUG_IN_NAME_TWEEN */
+} /* end p_handle_tween_processing_pug_ins */
/* -------------------------------------
* run
@@ -329,7 +436,7 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
*/
static void
run (const gchar *name, /* name of plugin */
- gint nparams, /* number of in-paramters */
+ gint nparams, /* number of in-parameters */
const GimpParam * param, /* in-parameters */
gint *nreturn_vals, /* number of out-parameters */
GimpParam ** return_vals) /* out-parameters */
@@ -343,7 +450,7 @@ run (const gchar *name, /* name of plugin */
/* Get the runmode from the in-parameters */
GimpRunMode run_mode = param[0].data.d_int32;
- /* status variable, use it to check for errors in invocation usualy only
+ /* status variable, use it to check for errors in invocation usually only
* during non-interactive calling
*/
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
@@ -365,7 +472,8 @@ run (const gchar *name, /* name of plugin */
}
run_flag = FALSE;
- if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+ if ((strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+ || (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0))
{
run_flag = TRUE;
}
@@ -396,6 +504,8 @@ run (const gchar *name, /* name of plugin */
mgpp->fdst_layer_id = gap_morph_exec_find_dst_drawable(image_id, drawable_id);
run_flag = gap_morph_dialog(mgpp);
mgpp->do_progress = TRUE;
+ mgpp->master_progress_callback_fptr = NULL;
+ mgpp->progress_callback_fptr = NULL;
}
else if (strcmp(name, PLUG_IN_NAME_ONE_TWEEN) == 0)
{
@@ -406,7 +516,12 @@ run (const gchar *name, /* name of plugin */
mgpp->fdst_layer_id = gap_morph_exec_find_dst_drawable(image_id, drawable_id);
run_flag = gap_morph_one_tween_dialog(mgpp);
mgpp->do_progress = TRUE;
+ mgpp->master_progress_callback_fptr = NULL;
+ mgpp->progress_callback_fptr = NULL;
}
+ /* dialog for other frame tween processing plug ins see
+ * p_handle_tween_processing_pug_ins
+ */
break;
case GIMP_RUN_NONINTERACTIVE:
@@ -442,7 +557,7 @@ run (const gchar *name, /* name of plugin */
}
else
{
- printf("%s: noninteractive call requires a not-empty workpoint_file_1 parameter\n"
+ printf("%s: non-interactive call requires a not-empty workpoint_file_1 parameter\n"
, PLUG_IN_NAME
);
status = GIMP_PDB_CALLING_ERROR;
@@ -450,7 +565,7 @@ run (const gchar *name, /* name of plugin */
}
else
{
- printf("%s: noninteractive call requires a not-NULL workpoint_file_1 parameter\n"
+ printf("%s: non-interactive call requires a not-NULL workpoint_file_1 parameter\n"
, PLUG_IN_NAME
);
status = GIMP_PDB_CALLING_ERROR;
@@ -506,7 +621,7 @@ run (const gchar *name, /* name of plugin */
}
else
{
- printf("%s: noninteractive call requires a not-NULL workpoint_file parameter\n"
+ printf("%s: non-interactive call requires a not-NULL workpoint_file parameter\n"
, PLUG_IN_NAME_ONE_TWEEN
);
status = GIMP_PDB_CALLING_ERROR;
@@ -554,16 +669,30 @@ run (const gchar *name, /* name of plugin */
}
else
{
- printf("%s: noninteractive call requires a not-NULL workpoint_file parameter\n"
+ printf("%s: non-interactive call requires a not-NULL workpoint_file parameter\n"
, PLUG_IN_NAME_TWEEN
);
status = GIMP_PDB_CALLING_ERROR;
}
}
+ else if ((nparams == G_N_ELEMENTS (in_workpoint_args))
+ && (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0))
+ {
+ mgpp->osrc_layer_id = -1; /* is created later as merged copy of the specified image */
+ mgpp->fdst_layer_id = -1; /* is created later as merged copy of the frame rfered by to_frame_nr parameter */
+ mgpp->range_to = param[3].data.d_int32;
+ mgpp->numWorkpoints = param[4].data.d_int32;
+ mgpp->numOutlinePoints = param[5].data.d_int32;
+ mgpp->locateDetailMoveRadius = param[6].data.d_int32;
+ mgpp->locateDetailShapeRadius = param[7].data.d_int32;
+ mgpp->locateColordiffThreshold = param[8].data.d_float;
+ mgpp->edgeColordiffThreshold = param[9].data.d_float;
+ run_flag = TRUE;
+ }
else
{
- printf("%s: noninteractive call wrong nuber %d of params were passed. (%d params are required)\n"
+ printf("%s: non-interactive call wrong nuber %d of params were passed. (%d params are required)\n"
, PLUG_IN_NAME
, (int)nparams
, (int)G_N_ELEMENTS (in_args)
@@ -581,6 +710,8 @@ run (const gchar *name, /* name of plugin */
mgpp->fdst_layer_id = gap_morph_exec_find_dst_drawable(image_id, drawable_id);
run_flag = gap_morph_dialog(mgpp);
mgpp->do_progress = TRUE;
+ mgpp->master_progress_callback_fptr = NULL;
+ mgpp->progress_callback_fptr = NULL;
}
break;
@@ -600,7 +731,9 @@ run (const gchar *name, /* name of plugin */
gimp_set_data (PLUG_IN_NAME, mgpp, sizeof (GapMorphGlobalParams));
}
}
- else if ((strcmp(name, PLUG_IN_NAME_ONE_TWEEN) == 0) || (strcmp(name, PLUG_IN_NAME_TWEEN) == 0))
+ else if ((strcmp(name, PLUG_IN_NAME_ONE_TWEEN) == 0)
+ || (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+ || (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0))
{
gint32 tween_layer_id;
@@ -608,9 +741,10 @@ run (const gchar *name, /* name of plugin */
{
tween_layer_id = gap_morph_render_one_tween(mgpp);
}
- else if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+ else
{
- tween_layer_id = p_handle_PLUG_IN_NAME_TWEEN(mgpp);
+ /* handle processing for PLUG_IN_NAME_TWEEN or PLUG_IN_NAME_WORKPOINTS */
+ tween_layer_id = p_handle_tween_processing_pug_ins(mgpp, name);
}
@@ -628,12 +762,15 @@ run (const gchar *name, /* name of plugin */
{
/* Store variable states for next run */
gimp_set_data (name, mgpp, sizeof (GapMorphGlobalParams));
- gimp_display_new(gimp_drawable_get_image(tween_layer_id));
+ if(strcmp(name, PLUG_IN_NAME_WORKPOINTS) != 0)
+ {
+ gimp_display_new(gimp_drawable_get_image(tween_layer_id));
+ }
}
}
}
-
}
+
values[0].data.d_status = status;
} /* end run */
diff --git a/gap/gap_morph_main.h b/gap/gap_morph_main.h
index 5cbeb7c..4c9cfa6 100644
--- a/gap/gap_morph_main.h
+++ b/gap/gap_morph_main.h
@@ -41,6 +41,12 @@
#define GAP_MORPH_WORKPOINT_FILENAME_MAX_LENGTH 1024
+#define GAP_MORPH_WORKPOINT_EXTENSION "morphpoints"
+
+/* Function Typedefs */
+typedef void (*t_progress_callback_fptr)(gdouble percentage, gpointer data);
+typedef void (*t_master_progress_callback_fptr)(gdouble numDone, gdouble numTotal, const char *filename, gpointer data);
+
typedef struct GapMorphWorkPoint { /* nickname: wp */
gdouble fdst_x; /* final dest koord (as set by user for last dest. frame) */
gdouble fdst_y;
@@ -66,6 +72,9 @@ typedef struct GapMorphWorkPoint { /* nickname: wp */
gboolean is_alive;
void *next_selected;
void *next_sek;
+
+ /* for automatically workpoint genration */
+ gdouble locateColordiff; /* 0.0 exact match up to theoretical 1.0 */
} GapMorphWorkPoint;
@@ -106,7 +115,24 @@ typedef struct GapMorphGlobalParams { /* nickname: mgpp */
gint32 range_from;
gint32 range_to;
gboolean overwrite_flag;
+ gboolean append_flag;
gboolean do_simple_fade; /* bypass morph algortihm when renderiing tweens and use simple fade instead */
+
+ gdouble edgeColordiffThreshold;
+ gdouble locateColordiffThreshold;
+ gint32 locateDetailShapeRadius;
+ gint32 locateDetailMoveRadius;
+ gint32 numWorkpoints;
+ gint32 numOutlinePoints;
+
+ /* additional stuff (only relevant for tween frame rendering) */
+ gint32 master_tween_steps;
+ gboolean create_tweens_in_subdir;
+ char tween_subdir[GAP_MORPH_WORKPOINT_FILENAME_MAX_LENGTH];
+
+ t_master_progress_callback_fptr master_progress_callback_fptr;
+ t_progress_callback_fptr progress_callback_fptr;
+ gpointer callback_data_ptr;
} GapMorphGlobalParams;
typedef struct GapMorphWarpCoreAPI { /* nickname: wcap */
diff --git a/gap/gap_morph_shape.c b/gap/gap_morph_shape.c
new file mode 100644
index 0000000..4c6acb8
--- /dev/null
+++ b/gap/gap_morph_shape.c
@@ -0,0 +1,1312 @@
+/* gap_morph_shape.c
+ * image shape dependent morph workpoint creation
+ * by hof (Wolfgang Hofer)
+ * 2010/08/10
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#include "config.h"
+
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+#include <errno.h>
+
+/* GIMP includes */
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_colordiff.h"
+#include "gap_locate.h"
+#include "gap_edge_detection.h"
+#include "gap_colordiff.h"
+#include "gap_morph_shape.h"
+#include "gap_morph_main.h"
+#include "gap_morph_dialog.h"
+#include "gap_morph_exec.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_libgapbase.h"
+
+#include "gap-intl.h"
+
+#define MAX_TOLERATED_ANGLE_DIFF_DEGREE 22.0
+#define MAX_TOLERATED_LENGTH_DIFF_PIXELS 33.0
+
+
+extern int gap_debug; /* ==0 ... dont print debug infos */
+
+typedef struct GapMorphShapeContext { /* nickname: msctx */
+ gint32 edgeImageId;
+ gint32 edgeLayerId;
+ gint32 refLayerId;
+ gint32 targetLayerId;
+ GimpDrawable *edgeDrawable;
+ GimpPixelFetcher *pftEdge;
+
+ gdouble locateColordiffThreshold;
+ gint32 locateDetailMoveRadius;
+ gint32 locateDetailShapeRadius;
+ gdouble edgeColordiffThreshold;
+ gdouble colorSensitivity;
+ gint32 countEdgePixels;
+
+ gint32 numShapePoints;
+ gint32 countGeneratedPoints;
+
+ /* generate points within the clipping boundary sel1/sel2 coordinate */
+ gint32 sel1X;
+ gint32 sel1Y;
+ gint32 sel2X;
+ gint32 sel2Y;
+
+
+ gboolean doProgress;
+ GtkWidget *progressBar;
+ gboolean *cancelWorkpointGenerationPtr;
+
+ } GapMorphShapeContext;
+
+static gboolean p_pixel_check_opaque(GimpPixelFetcher *pft
+ , gint bpp
+ , gdouble needx
+ , gdouble needy
+ );
+static void p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
+ ,gint bpp
+ ,gdouble alpha_rad
+ ,gint32 width
+ ,gint32 height
+ ,gdouble *px
+ ,gdouble *py
+ );
+
+
+/* ------------------------------------
+ * gap_morph_shape_calculateAngleDegree
+ * ------------------------------------
+ */
+gdouble
+gap_morph_shape_calculateAngleDegree(GapMorphWorkPoint *wp, gdouble *length)
+{
+ gdouble angleRad;
+ gdouble angleDeg;
+ gdouble sqrLen;
+ gdouble dx;
+ gdouble dy;
+ gdouble tanAlpha;
+
+ dx = wp->fdst_x - wp->osrc_x;
+ dy = wp->fdst_y - wp->osrc_y;
+
+ sqrLen = (dx * dx) + (dy * dy);
+
+ if (dx > 0)
+ {
+ tanAlpha = dy / dx;
+ angleRad = atan(tanAlpha);
+ if(angleRad < 0)
+ {
+ angleRad += (2.0 * G_PI);
+ }
+ } else if (dx < 0)
+ {
+ tanAlpha = dy / (0.0 - dx);
+ angleRad = atan(tanAlpha);
+ angleRad = G_PI - angleRad;
+ } else
+ {
+ /* dx == 0 */
+ if(dy > 0)
+ {
+ /* + 90 degree */
+ angleRad = G_PI / 2.0;
+ } else if(dy < 0)
+ {
+ /* 270 degree */
+ angleRad = G_PI + (G_PI / 2.0);
+ } else
+ {
+ /* undefined angle */
+ angleRad = 0;
+ }
+
+ }
+
+ *length = sqrt(sqrLen);
+ angleDeg = (angleRad * 180.0) / G_PI;
+
+ if(gap_debug)
+ {
+ printf("WP: dx:%.1f dy:%.1f len:%.3f angleDeg:%.5f\n"
+ ,(float)dx
+ ,(float)dy
+ ,(float)*length
+ ,(float)angleDeg
+ );
+ }
+
+ return(angleDeg);
+
+} /* end gap_morph_shape_calculateAngleDegree */
+
+
+
+
+/* -----------------------------
+ * gap_morph_shape_new_workpont
+ * -----------------------------
+ */
+GapMorphWorkPoint *
+gap_morph_shape_new_workpont(gdouble srcx, gdouble srcy, gdouble dstx, gdouble dsty)
+{
+ GapMorphWorkPoint *wp;
+
+ wp = g_new(GapMorphWorkPoint, 1);
+ wp->next = NULL;
+ wp->fdst_x = dstx;
+ wp->fdst_y = dsty;
+ wp->osrc_x = srcx;
+ wp->osrc_y = srcy;
+
+ wp->dst_x = wp->fdst_x;
+ wp->dst_y = wp->fdst_y;
+ wp->src_x = wp->osrc_x;
+ wp->locateColordiff = 0.0;
+
+
+ wp->src_y = wp->osrc_y;
+
+ return(wp);
+} /* end gap_morph_shape_new_workpont */
+
+
+/* ---------------------------------
+ * p_doProgress
+ * ---------------------------------
+ */
+static void
+p_doProgress(GapMorphShapeContext *msctx)
+{
+ guchar *progressText;
+ gdouble percentage;
+
+ if(msctx->doProgress != TRUE)
+ {
+ return;
+ }
+ percentage = (gdouble)msctx->countGeneratedPoints / MAX(1.0, (gdouble)msctx->numShapePoints);
+
+ progressText = g_strdup_printf(_("generating workpoint:%d (%d)")
+ , (int)msctx->countGeneratedPoints
+ , (int)msctx->numShapePoints
+ );
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(msctx->progressBar), progressText);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(msctx->progressBar), percentage);
+ g_free(progressText);
+
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+} /* end p_doProgress */
+
+
+/* ---------------------------------
+ * p_find_nearest_point
+ * ---------------------------------
+ * saerch the workpoint list for the point that is the nearest
+ * to position in_x/in_y in the osrc or fdst koord system.
+ * (depending on the swp->src_flag)
+ */
+static GapMorphWorkPoint *
+p_find_workpoint_near_src_coords(GapMorphGlobalParams *mgpp
+ , gdouble in_x
+ , gdouble in_y
+ , gint32 nearRadius
+ )
+{
+ GapMorphWorkPoint *wp;
+ GapMorphWorkPoint *wp_list;
+ gdouble sqr_distance;
+ gdouble near_sqr_dist;
+
+ near_sqr_dist = (nearRadius * nearRadius) * 2;
+ wp_list = mgpp->master_wp_list;
+
+ for(wp = wp_list; wp != NULL; wp = (GapMorphWorkPoint *)wp->next)
+ {
+ gdouble adx;
+ gdouble ady;
+
+ if((wp->osrc_x == in_x)
+ && (wp->osrc_y == in_y))
+ {
+ return(wp);
+ }
+
+ adx = abs(wp->osrc_x - in_x);
+ ady = abs(wp->osrc_y - in_y);
+
+ sqr_distance = (adx * adx) + (ady * ady);
+
+ if(sqr_distance <= near_sqr_dist)
+ {
+ return(wp);
+ }
+ }
+
+ return(NULL);
+} /* end p_find_nearest_point */
+
+
+
+/* -----------------------------
+ * p_locate_workpoint
+ * -----------------------------
+ * locate point coordinate in the other drawable
+ * (by searching for best matching shape within move radius)
+ */
+static gboolean
+p_locate_workpoint(GapMorphShapeContext *msctx
+ , gdouble rx, gdouble ry
+ , gdouble *tx, gdouble *ty
+ , gdouble *locateColordiff)
+{
+ gint32 refX;
+ gint32 refY;
+ gint32 targetX;
+ gint32 targetY;
+ gboolean success;
+
+ refX = rx;
+ refY = ry;
+
+ *locateColordiff = gap_locateDetailWithinRadius(msctx->refLayerId
+ , refX
+ , refY
+ , msctx->locateDetailShapeRadius
+ , msctx->targetLayerId
+ , msctx->locateDetailMoveRadius
+ , msctx->locateColordiffThreshold
+ , &targetX
+ , &targetY
+ );
+ if(gap_debug)
+ {
+ printf("p_locate_workpoint: x/y: %d / %d MoveRadius:%d ShapeRadius:%d Threshold:%.5f colordiff:%.5f\n"
+ , (int)refX
+ , (int)refY
+ , (int)msctx->locateDetailMoveRadius
+ , (int)msctx->locateDetailShapeRadius
+ , (float)msctx->locateColordiffThreshold
+ , (float)*locateColordiff
+ );
+
+ }
+
+ if(*locateColordiff < msctx->locateColordiffThreshold)
+ {
+ /* found a sufficient matching shape in the target drawable
+ * at targetX/Y coordinate. Overwrite tx and ty with this matching point coordinate
+ */
+ *tx = targetX;
+ *ty = targetY;
+ success = TRUE;
+
+ if(gap_debug)
+ {
+ printf("p_locate_workpoint: SUCCESS, found matching shape! tx:%d ty:%d colordiff:%.4f colordiffThres:%.4f\n"
+ ,(int)targetX
+ ,(int)targetY
+ ,(float)*locateColordiff
+ ,(float)msctx->locateColordiffThreshold
+ );
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+
+ return(success);
+
+} /* end p_locate_workpoint */
+
+
+
+
+
+
+
+
+
+
+
+
+/* -----------------------------------------------
+ * p_find_workpoint_nearest_to_src_coords
+ * -----------------------------------------------
+ * find workpoint that is nearest (but not equal)
+ * to specified coords.
+ * return the nearest workpoint within check radius
+ * (and set wp->sqr_dist to the square distance)
+ * or NULLin case no workpoint is near.
+ */
+static GapMorphWorkPoint*
+p_find_workpoint_nearest_to_src_coords(GapMorphGlobalParams *mgpp
+ , gdouble in_x
+ , gdouble in_y
+ , gint32 checkRadius
+ )
+{
+ GapMorphWorkPoint *wp;
+ GapMorphWorkPoint *wp_ret;
+ GapMorphWorkPoint *wp_list;
+ gdouble sqr_distance;
+ gdouble check_sqr_dist;
+
+ check_sqr_dist = (checkRadius * checkRadius) * 2;
+ wp_list = mgpp->master_wp_list;
+ wp_ret = NULL;
+
+ for(wp = wp_list; wp != NULL; wp = (GapMorphWorkPoint *)wp->next)
+ {
+ gdouble adx;
+ gdouble ady;
+
+ if((wp->osrc_x == in_x)
+ && (wp->osrc_y == in_y))
+ {
+ continue;
+ }
+
+ adx = abs(wp->osrc_x - in_x);
+ ady = abs(wp->osrc_y - in_y);
+
+ sqr_distance = (adx * adx) + (ady * ady);
+
+ if(sqr_distance < check_sqr_dist)
+ {
+ check_sqr_dist = sqr_distance;
+ wp_ret = wp;
+ }
+ }
+
+ if(wp_ret)
+ {
+ wp_ret->sqr_dist = check_sqr_dist;
+ }
+
+ return(wp_ret);
+
+} /* end p_find_nearest_point */
+
+
+
+/* -----------------------------------------------
+ * p_compare_workpoint_direction
+ * -----------------------------------------------
+ * return FALSE in case the specified workpoint
+ * movement vector differs significant from specified angle/length.
+ * in case the vector length is 0 (just a point)
+ * TRUE is returned.
+ */
+static gboolean
+p_compare_workpoint_direction(GapMorphWorkPoint *wpRef
+ , gdouble angleDeg
+ , gdouble length
+ )
+{
+ gdouble lengthRef;
+ gdouble angleDegRef;
+ gdouble angleDiffDeg;
+ gdouble lengthDiffPixels;
+
+ angleDegRef = gap_morph_shape_calculateAngleDegree(wpRef, &lengthRef);
+ lengthDiffPixels = fabs(length - lengthRef);
+
+ if((lengthRef > 0) && (length > 0))
+ {
+ lengthDiffPixels = fabs(length - lengthRef);
+ if(lengthDiffPixels > MAX_TOLERATED_LENGTH_DIFF_PIXELS)
+ {
+ return (FALSE);
+ }
+
+
+ angleDiffDeg = fabs(angleDeg - angleDegRef);
+ if(angleDiffDeg > 180)
+ {
+ angleDiffDeg = 360 - angleDiffDeg;
+ }
+
+ if(angleDiffDeg > MAX_TOLERATED_ANGLE_DIFF_DEGREE)
+ {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+
+} /* end p_compare_workpoint_direction */
+
+
+/* -----------------------------------------------
+ * p_copy_workpoint_attributes
+ * -----------------------------------------------
+ * copy relevant attributes
+ * (but keep next pointer unchanged)
+ */
+static void
+p_copy_workpoint_attributes(GapMorphWorkPoint *wpSrc, GapMorphWorkPoint *wpDst)
+{
+ wpDst->fdst_x = wpSrc->fdst_x; /* final dest koord (as set by user for last dest. frame) */
+ wpDst->fdst_y = wpSrc->fdst_y;
+ wpDst->osrc_x = wpSrc->osrc_x; /* start source koord (as set by user for the 1.st frame) */
+ wpDst->osrc_y = wpSrc->osrc_y;
+
+ wpDst->dst_x = wpSrc->dst_x; /* koord trans */
+ wpDst->dst_y = wpSrc->dst_y;
+ wpDst->src_x = wpSrc->src_x; /* osrc_x scaled to koords of current (dest) frame */
+ wpDst->src_y = wpSrc->src_y;
+
+ wpDst->locateColordiff = wpSrc->locateColordiff;
+}
+
+/* -----------------------------------------------
+ * p_workpoint_plausiblity_filter
+ * -----------------------------------------------
+ * return FALSE in case the specified workpoint
+ * has untypical movement that differs significant from near workpoints.
+ */
+static gboolean
+p_workpoint_plausiblity_filter(GapMorphShapeContext *msctx
+ , GapMorphGlobalParams *mgpp
+ , GapMorphWorkPoint *wp
+ )
+{
+#define NEAR_CHECK_RADIUS 20.0
+#define OUTER_CHECK_RADIUS (30.0 + NEAR_CHECK_RADIUS)
+ GapMorphWorkPoint *wpRef;
+ gdouble length;
+ gdouble angleDeg;
+ gboolean isWorkpointOk;
+
+ isWorkpointOk = TRUE;
+ angleDeg = gap_morph_shape_calculateAngleDegree(wp, &length);
+
+ wpRef = p_find_workpoint_nearest_to_src_coords(mgpp
+ , wp->osrc_x /* gdouble in_x */
+ , wp->osrc_y /* gdouble in_y */
+ , OUTER_CHECK_RADIUS
+ );
+ if(wpRef != NULL)
+ {
+ /* found a near workpoint that is either already checked
+ * or was already present before start of workpoint generation
+ */
+ isWorkpointOk = p_compare_workpoint_direction(wpRef, angleDeg, length);
+
+ if(gap_debug)
+ {
+ printf("NEAREST Ref workpoint found x:%d y:%d xRef:%d yRef:%d OK:%d colordiff:%.4f colordiffRef:%.4f dist:%.4f DIST_LIMIT:%.4f\n"
+ , (int)wp->osrc_x
+ , (int)wp->osrc_y
+ , (int)wpRef->osrc_x
+ , (int)wpRef->osrc_y
+ , (int)isWorkpointOk
+ , (float)wp->locateColordiff
+ , (float)wpRef->locateColordiff
+ , (float)sqrt(wpRef->sqr_dist)
+ , (float)NEAR_CHECK_RADIUS
+ );
+ }
+
+
+ if (isWorkpointOk != TRUE)
+ {
+ if(wp->locateColordiff < wpRef->locateColordiff)
+ {
+ if(sqrt(wpRef->sqr_dist) <= NEAR_CHECK_RADIUS)
+ {
+ /* overwrite wpRef attributes because the new workpoint wp
+ */
+ p_copy_workpoint_attributes(wp, wpRef);
+ if(gap_debug)
+ {
+ printf("REPLACE Ref workpoint x:%d y:%d xRef:%d yRef:%d\n"
+ , (int)wp->osrc_x
+ , (int)wp->osrc_y
+ , (int)wpRef->osrc_x
+ , (int)wpRef->osrc_y
+ );
+ }
+ }
+ }
+ }
+
+ }
+
+
+
+ if(gap_debug)
+ {
+ if(isWorkpointOk != TRUE)
+ {
+ printf("DISCARD non-plausible workpoint at x:%d y:%d\n"
+ , (int)wp->osrc_x
+ , (int)wp->osrc_y
+ );
+ }
+ }
+
+ return(isWorkpointOk);
+
+} /* end p_workpoint_plausiblity_filter */
+
+
+
+
+
+/* --------------------------------
+ * p_generateWorkpoints
+ * --------------------------------
+ * scan the edge drawable with descending grid size (defined stepSizeTab)
+ * and descending edge threshold. This shall spread the generated
+ * workpoints all over the whole area where bright edge points
+ * are picked first.
+ */
+static void
+p_generateWorkpoints(GapMorphShapeContext *msctx, GapMorphGlobalParams *mgpp)
+{
+#define LOOP_LOOKUP_TAB_SIZE 5
+ static gint32 edgeThresTab[LOOP_LOOKUP_TAB_SIZE] = { 120, 60, 30, 10, 1 };
+ static gint32 nearRadiusTab[LOOP_LOOKUP_TAB_SIZE] = { 20, 14, 10, 6, 2 };
+ static gint32 stepSizeTab[LOOP_LOOKUP_TAB_SIZE] = { 10, 8, 6, 2, 1 };
+ gint32 ex;
+ gint32 ey;
+ gdouble srcX;
+ gdouble srcY;
+ gint32 nearRadius;
+ gint32 edgePixelThreshold255;
+ gint32 stepSize;
+ gint32 ii;
+
+ for(ii=0; ii < LOOP_LOOKUP_TAB_SIZE; ii++)
+ {
+ edgePixelThreshold255 = edgeThresTab[ii];
+ nearRadius = nearRadiusTab[ii];
+ stepSize = stepSizeTab[ii];
+
+ for(ey=msctx->sel1Y; ey < msctx->sel2Y; ey += stepSize)
+ {
+ srcY = ey +1;
+ for(ex=msctx->sel1X; ex < msctx->sel2X; ex += stepSize)
+ {
+ guchar pixel[4];
+
+ if(*msctx->cancelWorkpointGenerationPtr == TRUE)
+ {
+ return;
+ }
+
+ gimp_pixel_fetcher_get_pixel (msctx->pftEdge, ex, ey, &pixel[0]);
+
+ if(pixel[0] >= edgePixelThreshold255)
+ {
+ /* found an edge pixel */
+ GapMorphWorkPoint *wp;
+
+ srcX = ex +1;
+
+
+ /* chek if workpoint with this (or very near) coords does already exist */
+ wp = p_find_workpoint_near_src_coords(mgpp
+ , srcX
+ , srcY
+ , nearRadius
+ );
+ if(wp == NULL)
+ {
+ gboolean success;
+ gdouble dstX;
+ gdouble dstY;
+ gdouble locateColordiff;
+
+ success = p_locate_workpoint(msctx
+ , srcX, srcY
+ , &dstX, &dstY
+ , &locateColordiff
+ );
+ if(success)
+ {
+ wp = gap_morph_shape_new_workpont(srcX, srcY, dstX, dstY);
+ wp->locateColordiff = locateColordiff;
+
+ if(TRUE == p_workpoint_plausiblity_filter(msctx
+ , mgpp
+ , wp
+ ))
+ {
+ msctx->countGeneratedPoints++;
+ /* add new workpoint as 1st listelement */
+ wp->next = mgpp->master_wp_list;
+ mgpp->master_wp_list = wp;
+
+ p_doProgress(msctx);
+
+ if(msctx->countGeneratedPoints >= msctx->numShapePoints)
+ {
+ return;
+ }
+ }
+ else
+ {
+ g_free(wp);
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+ }
+ }
+
+} /* end p_generateWorkpoints */
+
+
+/* --------------------------------
+ * gap_moprhShapeDetectionEdgeBased
+ * --------------------------------
+ *
+ * generates morph workpoints via edge based shape detection.
+ * This is done by edge detection in the source image
+ * (specified via mgup->mgpp->osrc_layer_id)
+ *
+ * and picking workpoints on the detected edges
+ * and locating the corresponding point in the destination image.
+ *
+ * IN: mgup->num_shapepoints specifies the number of workpoints to be generated.
+ */
+void
+gap_moprhShapeDetectionEdgeBased(GapMorphGUIParams *mgup, gboolean *cancelFlagPtr)
+{
+ GapMorphShapeContext morphShapeContext;
+ GapMorphShapeContext *msctx;
+ gboolean deleteEdgeImage;
+ GapMorphGlobalParams *mgpp;
+
+ mgpp = mgup->mgpp;
+
+ if(mgup->workpointGenerationBusy == TRUE)
+ {
+ return;
+ }
+ mgup->workpointGenerationBusy = TRUE;
+ deleteEdgeImage = TRUE;
+
+ /* init context */
+ msctx = &morphShapeContext;
+ msctx->edgeColordiffThreshold = CLAMP(mgpp->edgeColordiffThreshold, 0.0, 1.0);
+ msctx->locateColordiffThreshold = CLAMP(mgpp->locateColordiffThreshold, 0.0, 1.0);
+ msctx->locateDetailShapeRadius = mgpp->locateDetailShapeRadius;
+ msctx->locateDetailMoveRadius = mgpp->locateDetailMoveRadius;
+ msctx->colorSensitivity = GAP_COLORDIFF_DEFAULT_SENSITIVITY;
+ msctx->refLayerId = mgpp->osrc_layer_id;
+ msctx->targetLayerId = mgpp->fdst_layer_id;
+ msctx->numShapePoints = mgpp->numWorkpoints;
+ msctx->countGeneratedPoints = 0;
+
+ msctx->doProgress = TRUE;
+ msctx->progressBar = mgup->progressBar;
+ if(msctx->progressBar == NULL)
+ {
+ msctx->doProgress = FALSE;
+ }
+ msctx->cancelWorkpointGenerationPtr = cancelFlagPtr;
+
+ if(gap_debug)
+ {
+ printf("gap_moprhShapeDetectionEdgeBased START edgeThres:%.5f locateThres:%.5f locateRadius:%d\n"
+ , (float)msctx->edgeColordiffThreshold
+ , (float)msctx->locateDetailMoveRadius
+ , (int)msctx->locateDetailMoveRadius
+ );
+ }
+
+
+ msctx->edgeLayerId = gap_edgeDetection(mgup->mgpp->osrc_layer_id /* refDrawableId */
+ ,mgup->mgpp->edgeColordiffThreshold
+ ,&msctx->countEdgePixels
+ );
+
+ msctx->edgeImageId = gimp_drawable_get_image(msctx->edgeLayerId);
+
+ if(gap_debug)
+ {
+ /* show the edge image for debug purpose
+ * (this image is intended for internal processing usage)
+ */
+ gimp_display_new(msctx->edgeImageId);
+ deleteEdgeImage = FALSE;
+ }
+ msctx->edgeDrawable = gimp_drawable_get(msctx->edgeLayerId);
+
+
+ if(msctx->edgeDrawable != NULL)
+ {
+ gint maxWidth;
+ gint maxHeight;
+
+ maxWidth = msctx->edgeDrawable->width-1;
+ maxHeight = msctx->edgeDrawable->height-1;
+
+ if((mgup->src_win.zoom < 1.0) && (mgup->src_win.zoom > 0.0))
+ {
+ gdouble fwidth;
+ gdouble fheight;
+ gint width;
+ gint height;
+
+ fwidth = mgup->src_win.zoom * (gdouble)mgup->src_win.pv_ptr->pv_width;
+ fheight = mgup->src_win.zoom * (gdouble)mgup->src_win.pv_ptr->pv_height;
+ width = CLAMP((gint)fwidth, 1, maxWidth);
+ height = CLAMP((gint)fheight, 1, maxHeight);
+ msctx->sel1X = CLAMP(mgup->src_win.offs_x, 0, maxWidth);
+ msctx->sel1Y = CLAMP(mgup->src_win.offs_y, 0, maxHeight);
+ msctx->sel2X = CLAMP((msctx->sel1X + width), 0, maxWidth);
+ msctx->sel2Y = CLAMP((msctx->sel1Y + height), 0, maxHeight);
+ }
+ else
+ {
+ msctx->sel1X = 0;
+ msctx->sel1Y = 0;
+ msctx->sel2X = maxWidth;
+ msctx->sel2Y = maxHeight;
+ }
+
+
+ if(gap_debug)
+ {
+ printf("Boundaries: sel1: %d / %d sel2: %d / %d zoom:%.5f\n"
+ , (int)msctx->sel1X
+ , (int)msctx->sel1Y
+ , (int)msctx->sel2X
+ , (int)msctx->sel2Y
+ , (float)mgup->src_win.zoom
+ );
+ }
+
+
+ msctx->pftEdge = gimp_pixel_fetcher_new (msctx->edgeDrawable, FALSE /* shadow */);
+ gimp_pixel_fetcher_set_edge_mode (msctx->pftEdge, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+ p_generateWorkpoints(msctx, mgpp);
+
+ gimp_pixel_fetcher_destroy (msctx->pftEdge);
+ }
+
+ if(msctx->edgeDrawable != NULL)
+ {
+ gimp_drawable_detach(msctx->edgeDrawable);
+ msctx->edgeDrawable = NULL;
+ }
+
+ if(deleteEdgeImage == TRUE)
+ {
+ gap_image_delete_immediate(msctx->edgeImageId);
+ }
+
+ if(gap_debug)
+ {
+ printf("gap_moprhShapeDetectionEdgeBased END\n");
+ }
+ mgup->workpointGenerationBusy = FALSE;
+
+ return ;
+
+} /* end gap_moprhShapeDetectionEdgeBased */
+
+
+
+/* -----------------------------------------------
+ * p_generateWorkpointFileForFramePair
+ * -----------------------------------------------
+ * generate workpoint file for morphing
+ * tweens between currFrameNr and nextFrameNr
+ *
+ * return TRUE on success, FALSE when Failed
+ */
+static gboolean
+p_generateWorkpointFileForFramePair(const char *workpointFileName, GapMorphGUIParams *mgup
+ , gboolean *cancelFlagPtr, gint32 currLayerId, gint32 nextLayerId)
+{
+ /* startup with empty workpoint list
+ */
+ if(mgup->mgpp->master_wp_list != NULL)
+ {
+ gap_morph_exec_free_workpoint_list(&mgup->mgpp->master_wp_list);
+ }
+ mgup->mgpp->master_wp_list = NULL;
+
+ mgup->mgpp->osrc_layer_id = currLayerId;
+ mgup->mgpp->fdst_layer_id = nextLayerId;
+
+ /* if workpointfile already exists (and overwrite flag is not specified)
+ * load and append newly generated points
+ */
+ if(g_file_test(workpointFileName, G_FILE_TEST_EXISTS))
+ {
+ if(mgup->mgpp->append_flag != TRUE)
+ {
+ if(gap_debug)
+ {
+ printf("SKIP processing for already existing workpointfile:%s\n"
+ , workpointFileName
+ );
+ }
+ return (TRUE);
+ }
+ if(mgup->mgpp->overwrite_flag != TRUE)
+ {
+ if(gap_debug)
+ {
+ printf("ADD newly generated workpoints to already existing workpointfile:%s\n"
+ , workpointFileName
+ );
+ }
+ GapMorphGUIParams morphParamsInFile;
+ GapMorphGUIParams *mgupFile;
+ GapMorphGlobalParams dummyParams;
+ GapMorphGlobalParams *mgppFile;
+
+ mgppFile = &dummyParams;
+ mgppFile->osrc_layer_id = mgup->mgpp->osrc_layer_id;
+ mgppFile->fdst_layer_id = mgup->mgpp->fdst_layer_id;
+
+ mgupFile = &morphParamsInFile;
+ mgupFile->mgpp = mgppFile;
+
+ mgup->mgpp->master_wp_list = gap_moprh_exec_load_workpointfile(workpointFileName
+ ,mgupFile
+ );
+ }
+ }
+
+ if(mgup->mgpp->numOutlinePoints > 0)
+ {
+ gap_morph_shape_generate_outline_workpoints(mgup->mgpp);
+ }
+
+
+ gap_moprhShapeDetectionEdgeBased(mgup, cancelFlagPtr);
+ if(!gap_moprh_exec_save_workpointfile(workpointFileName, mgup))
+ {
+ gint l_errno;
+
+ l_errno = errno;
+ g_message (_("Failed to write morph workpointfile\n"
+ "filename: '%s':\n%s")
+ , workpointFileName
+ , g_strerror (l_errno)
+ );
+ return (FALSE);
+ }
+
+ return (TRUE);
+
+} /* end p_generateWorkpointFileForFramePair */
+
+
+
+/* -----------------------------------------------
+ * p_getMergedFrameImageLayer
+ * -----------------------------------------------
+ *
+ * return -1 if FAILED, or layerId (positive integer) on success
+ */
+gint32
+p_getMergedFrameImageLayer(GapAnimInfo *ainfo_ptr, gint32 frameNr)
+{
+ gint32 imageId;
+ gint32 layerId;
+
+ layerId = -1;
+ if(ainfo_ptr->new_filename != NULL)
+ {
+ g_free(ainfo_ptr->new_filename);
+ }
+ ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
+ frameNr,
+ ainfo_ptr->extension);
+ if(ainfo_ptr->new_filename == NULL)
+ {
+ printf("could not create frame filename for frameNr:%d\n", (int)frameNr);
+ return -1;
+ }
+
+ if(!g_file_test(ainfo_ptr->new_filename, G_FILE_TEST_EXISTS))
+ {
+ printf("file not found: %s\n", ainfo_ptr->new_filename);
+ return -1;
+ }
+
+ /* load current frame */
+ imageId = gap_lib_load_image(ainfo_ptr->new_filename);
+ if(imageId >= 0)
+ {
+ layerId = gap_image_merge_visible_layers(imageId, GIMP_CLIP_TO_IMAGE);
+ }
+
+ return(layerId);
+}
+
+/* -----------------------------------------------
+ * p_do_master_progress
+ * -----------------------------------------------
+ */
+static void
+p_do_master_progress(GtkWidget *progressBar
+ , const char *workpointFileName, gdouble framesToProcess, gdouble frameNr)
+{
+ if(progressBar != NULL)
+ {
+ char *msg;
+ gdouble percentage;
+
+ percentage = frameNr / MAX(1.0, framesToProcess);
+ msg = gap_base_shorten_filename(NULL /* prefix */
+ ,workpointFileName /* filenamepart */
+ ,NULL /* suffix */
+ ,130 /* l_max_chars */
+ );
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), msg);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressBar), percentage);
+ g_free(msg);
+
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+ }
+}
+
+
+
+/* -----------------------------
+ * p_pixel_check_opaque
+ * -----------------------------
+ * check average opacity in an area
+ * of 2x2 pixels
+ * return TRUE if average alpha is 50% or more opaque
+ */
+static gboolean
+p_pixel_check_opaque(GimpPixelFetcher *pft, gint bpp, gdouble needx, gdouble needy)
+{
+ guchar pixel[4][4];
+ gdouble alpha_val;
+
+ gint xi, yi;
+ gint alpha_idx;
+
+
+ if (needx >= 0.0)
+ xi = (int) needx;
+ else
+ xi = -((int) -needx + 1);
+
+ if (needy >= 0.0)
+ yi = (int) needy;
+ else
+ yi = -((int) -needy + 1);
+
+ gimp_pixel_fetcher_get_pixel (pft, xi, yi, pixel[0]);
+ gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi, pixel[1]);
+ gimp_pixel_fetcher_get_pixel (pft, xi, yi + 1, pixel[2]);
+ gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi + 1, pixel[3]);
+
+ alpha_idx = bpp -1;
+ /* average aplha channel normalized to range 0.0 upto 1.0 */
+ alpha_val = (
+ (gdouble)pixel[0][alpha_idx] / 255.0
+ + (gdouble)pixel[1][alpha_idx] / 255.0
+ + (gdouble)pixel[2][alpha_idx] / 255.0
+ + (gdouble)pixel[3][alpha_idx] / 255.0
+ ) / 4.0;
+
+ if (alpha_val > 0.5) /* fix THRESHOLD half or more opaque */
+ {
+ return (TRUE);
+ }
+ return (FALSE);
+} /* end p_pixel_check_opaque */
+
+
+/* -----------------------------
+ * p_find_outmost_opaque_pixel
+ * -----------------------------
+ * returns koords in paramters px, py
+ */
+static void
+p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
+ ,gint bpp
+ ,gdouble alpha_rad
+ ,gint32 width
+ ,gint32 height
+ ,gdouble *px
+ ,gdouble *py
+ )
+{
+ gdouble center_x;
+ gdouble center_y;
+ gdouble cos_alpha;
+ gdouble sin_alpha;
+ gdouble l_x, l_y, l_r;
+ gdouble half_width;
+ gdouble half_height;
+
+ l_x = 0;
+ l_y = 0;
+ cos_alpha = cos(alpha_rad);
+ sin_alpha = sin(alpha_rad);
+
+ /* printf("sin: %.5f cos:%.5f\n", sin_alpha, cos_alpha); */
+
+ half_width = (gdouble)(width /2.0);
+ half_height = (gdouble)(height /2.0);
+ center_x = half_width;
+ center_y = half_height;
+ l_r = MAX(half_width, half_height);
+ l_r *= l_r;
+
+ /* start at the out-most point
+ * (may be out of the layer in most cases)
+ * and search towards the center along
+ * the line with angle alpha
+ */
+ while(l_r > 0)
+ {
+ l_y = l_r * sin_alpha;
+ l_x = l_r * cos_alpha;
+ if((l_x <= half_width)
+ && (l_y <= half_height))
+ {
+ if (((center_x + l_x) >= 0)
+ && ((center_y + l_y) >= 0))
+ {
+ /* now we are inside the layer area */
+ if (p_pixel_check_opaque(pft
+ ,bpp
+ ,center_x + l_x
+ ,center_y + l_y
+ ))
+ {
+ break;
+ }
+ }
+ }
+ l_r--;
+ }
+
+ *px = center_x + l_x;
+ *py = center_y + l_y;
+
+} /* end p_find_outmost_opaque_pixel */
+
+
+
+/* -------------------------------------------
+ * gap_morph_shape_generate_outline_workpoints
+ * -------------------------------------------
+ */
+void
+gap_morph_shape_generate_outline_workpoints(GapMorphGlobalParams *mgpp)
+{
+ GapMorphWorkPoint *wp;
+ GimpPixelFetcher *src_pixfet;
+ GimpPixelFetcher *dst_pixfet;
+ GimpDrawable *dst_drawable;
+ GimpDrawable *src_drawable;
+ gdouble alpha_rad;
+ gdouble step_rad;
+ gint ii;
+
+ src_drawable = gimp_drawable_get (mgpp->osrc_layer_id);
+ dst_drawable = gimp_drawable_get (mgpp->fdst_layer_id);
+
+ src_pixfet = gimp_pixel_fetcher_new (src_drawable, FALSE /*shadow*/);
+ dst_pixfet = gimp_pixel_fetcher_new (dst_drawable, FALSE /*shadow*/);
+
+ step_rad = (2.0 * G_PI) / MAX(1, mgpp->numOutlinePoints);
+ alpha_rad = 0.0;
+
+ /* loop from 0 to 360 degree */
+ for(ii=0; ii < mgpp->numOutlinePoints; ii++)
+ {
+ gdouble sx, sy, dx, dy;
+
+ p_find_outmost_opaque_pixel(src_pixfet
+ ,src_drawable->bpp
+ ,alpha_rad
+ ,src_drawable->width
+ ,src_drawable->height
+ ,&sx
+ ,&sy
+ );
+ p_find_outmost_opaque_pixel(dst_pixfet
+ ,dst_drawable->bpp
+ ,alpha_rad
+ ,dst_drawable->width
+ ,dst_drawable->height
+ ,&dx
+ ,&dy
+ );
+
+ /* create a new workpoint with sx,sy, dx, dy coords */
+ wp = gap_morph_shape_new_workpont(sx ,sy ,dx ,dy);
+ wp->next = mgpp->master_wp_list;
+ mgpp->master_wp_list = wp;
+
+ alpha_rad += step_rad;
+ }
+ gimp_pixel_fetcher_destroy (src_pixfet);
+ gimp_pixel_fetcher_destroy (dst_pixfet);
+
+ gimp_drawable_detach(src_drawable);
+ gimp_drawable_detach(dst_drawable);
+
+} /* end gap_morph_shape_generate_outline_workpoints */
+
+
+/* -----------------------------------------------
+ * gap_morph_shape_generate_frame_tween_workpoints
+ * -----------------------------------------------
+ * generate workpoint files (one per frame)
+ * for the specified frame range.
+ *
+ */
+gint32
+gap_morph_shape_generate_frame_tween_workpoints(GapAnimInfo *ainfo_ptr
+ , GapMorphGlobalParams *mgpp, GtkWidget *masterProgressBar, GtkWidget *progressBar, gboolean *cancelFlagPtr)
+{
+ GapMorphGUIParams morph_gui_params;
+ GapMorphGUIParams *mgup;
+ gint32 frameCount;
+ gint32 currFrameNr;
+ gint32 nextFrameNr;
+ gint32 currLayerId;
+ gint32 nextLayerId;
+ gint32 firstFrameNr;
+ gint32 lastFrameNr;
+ gdouble framesToProcess;
+
+
+ mgup = &morph_gui_params;
+ mgup->mgpp = mgpp;
+ mgup->src_win.zoom = 1.0; /* no zoom available here (use full drawable size for workpoint generation */
+ mgup->cancelWorkpointGeneration = FALSE;
+ mgup->progressBar = progressBar;
+ mgpp->master_wp_list = NULL;
+
+ gimp_rgb_set(&mgup->pointcolor, 0.1, 1.0, 0.1); /* startup with GREEN pointcolor */
+ gimp_rgb_set_alpha(&mgup->pointcolor, 1.0);
+ gimp_rgb_set(&mgup->curr_pointcolor, 1.0, 1.0, 0.1); /* startup with YELLOW color */
+ gimp_rgb_set_alpha(&mgup->curr_pointcolor, 1.0);
+
+ frameCount = 0;
+ nextLayerId = -1;
+
+ if(gap_lib_chk_framerange(ainfo_ptr) != 0)
+ {
+ return(0);
+ }
+
+
+ firstFrameNr = CLAMP(mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr);
+ currFrameNr = firstFrameNr;
+ currLayerId = p_getMergedFrameImageLayer(ainfo_ptr, currFrameNr);
+ nextFrameNr = currFrameNr + 1;
+ while(TRUE)
+ {
+ lastFrameNr = MIN(mgpp->range_to, ainfo_ptr->last_frame_nr);
+ framesToProcess = MAX(1, (lastFrameNr - currFrameNr) -1);
+
+ if (nextFrameNr > lastFrameNr)
+ {
+ break;
+ }
+ nextLayerId = p_getMergedFrameImageLayer(ainfo_ptr, nextFrameNr);
+
+ if((nextLayerId >= 0) && (currLayerId >= 0))
+ {
+ char *workpointFileName;
+ gboolean success;
+
+ workpointFileName = gap_lib_alloc_fname(ainfo_ptr->basename,
+ currFrameNr,
+ "." GAP_MORPH_WORKPOINT_EXTENSION);
+
+ p_do_master_progress(masterProgressBar
+ , workpointFileName
+ , framesToProcess
+ , (currFrameNr - firstFrameNr)
+ );
+
+ success = p_generateWorkpointFileForFramePair(workpointFileName
+ , mgup
+ , cancelFlagPtr
+ , currLayerId
+ , nextLayerId
+ );
+
+ g_free(workpointFileName);
+ gap_image_delete_immediate(gimp_drawable_get_image(currLayerId));
+ if(!success)
+ {
+ break;
+ }
+
+ currFrameNr = nextFrameNr;
+ currLayerId = nextLayerId;
+ }
+ nextFrameNr++;
+
+ }
+
+ if(nextLayerId >= 0)
+ {
+ gap_image_delete_immediate(gimp_drawable_get_image(nextLayerId));
+ }
+
+ return (frameCount);
+} /* end gap_morph_shape_generate_frame_tween_workpoints */
+
+
+
diff --git a/gap/gap_morph_shape.h b/gap/gap_morph_shape.h
new file mode 100644
index 0000000..02566c4
--- /dev/null
+++ b/gap/gap_morph_shape.h
@@ -0,0 +1,102 @@
+/* gap_morph_shape.h
+ * image shape dependent morph workpoint creation
+ * by hof (Wolfgang Hofer)
+ * 2010/08/10
+ *
+ */
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* revision history:
+ * version 2.7.0; hof: created
+ */
+
+#ifndef _GAP_MORPH_SHAPE_H
+#define _GAP_MORPH_SHAPE_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "gap_morph_main.h"
+#include "gap_morph_dialog.h"
+#include "gap_lib_common_defs.h"
+
+/* -----------------------------
+ * gap_morph_shape_new_workpont
+ * -----------------------------
+ */
+GapMorphWorkPoint *
+gap_morph_shape_new_workpont(gdouble srcx, gdouble srcy, gdouble dstx, gdouble dsty);
+
+
+
+/* --------------------------------
+ * gap_moprhShapeDetectionEdgeBased
+ * --------------------------------
+ *
+ * generates morph workpoints via edge based shape detection.
+ * This is done by edge detection in the source image
+ * (specified via mgup->mgpp->osrc_layer_id)
+ *
+ * and picking workpoints on the detected edges
+ * and locating the corresponding point in the destination image.
+ *
+ * IN: mgup->num_shapepoints specifies the number of workpoints to be generated.
+ */
+void
+gap_moprhShapeDetectionEdgeBased(GapMorphGUIParams *mgup, gboolean *cancelFlagPtr);
+
+
+
+/* -----------------------------------------------
+ * gap_morph_shape_generate_frame_tween_workpoints
+ * -----------------------------------------------
+ * call with progressBar in non-interactive mode
+ * or with progressBar when progress handling is required.
+ */
+gint32
+gap_morph_shape_generate_frame_tween_workpoints(GapAnimInfo *ainfo_ptr
+ , GapMorphGlobalParams *mgpp, GtkWidget *masterProgressBar, GtkWidget *progressBar, gboolean *cancelFlagPtr);
+
+
+/* -------------------------------------------
+ * gap_morph_shape_generate_outline_workpoints
+ * -------------------------------------------
+ */
+void
+gap_morph_shape_generate_outline_workpoints(GapMorphGlobalParams *mgpp);
+
+
+/* -------------------------------------------
+ * gap_morph_shape_calculateAngleDegree
+ * -------------------------------------------
+ * return angle of the workpoint movement vektor in degree
+ * Out: length of this vector
+ */
+gdouble
+gap_morph_shape_calculateAngleDegree(GapMorphWorkPoint *wp, gdouble *length);
+
+
+
+#endif
diff --git a/gap/gap_morph_tween_dialog.c b/gap/gap_morph_tween_dialog.c
index a6aa5cd..4cd809a 100644
--- a/gap/gap_morph_tween_dialog.c
+++ b/gap/gap_morph_tween_dialog.c
@@ -1,6 +1,9 @@
-/* gap_morph_one_tween_dialog.c
+/* gap_morph_tween_dialog.c
*
- * This module handles the GAP morph tween dialog
+ * This module handles the GAP morph tween dialogs for
+ * o) gap_morph_one_tween_dialog
+ * o) gap_morph_frame_tweens_dialog
+ * o) gap_morph_generate_frame_tween_workpoints_dialog (Workpointfile Generator)
*/
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
@@ -28,6 +31,9 @@
#include "gap_morph_main.h"
#include "gap_morph_tween_dialog.h"
+#include "gap_morph_shape.h"
+#include "gap_base.h"
+
#include "gap-intl.h"
extern int gap_debug; /* 1 == print debug infos , 0 dont print debug infos */
@@ -39,6 +45,21 @@ typedef struct MorphTweenGui { /* nickname: mtg */
GapMorphGlobalParams *mgpp;
GtkWidget *morph_filesel;
GtkWidget *morph_entry;
+ GtkWidget *tween_subdir_entry;
+ GapAnimInfo *ainfo_ptr;
+
+ GtkWidget *shell;
+ GtkWidget *masterProgressBar;
+ GtkWidget *progressBar;
+ gboolean processingBusy;
+ gboolean cancelProcessing;
+ gint32 ret;
+
+ GtkWidget *from_scale;
+ GtkWidget *from_spinbutton;
+ GtkWidget *to_scale;
+ GtkWidget *to_spinbutton;
+
} MorphTweenGui;
@@ -94,6 +115,108 @@ p_layerSelectionComboCallback (GtkWidget *widget)
} /* end p_layerSelectionComboCallback */
+/* --------------------------
+ * p_master_progress_callback
+ * --------------------------
+ */
+static void
+p_master_progress_callback(gdouble numDone, gdouble numTotal, const char *filename, gpointer data)
+{
+ MorphTweenGui *mtg;
+
+ mtg = (MorphTweenGui *) data;
+ if(mtg)
+ {
+ if(mtg->masterProgressBar)
+ {
+ guchar *progressText;
+ gdouble percentage;
+
+ percentage = numDone / MAX(1.0, numTotal);
+
+ if(filename != NULL)
+ {
+ char *shortFileName;
+
+ shortFileName = gap_base_shorten_filename(NULL /* prefix */
+ ,filename /* filenamepart */
+ ,NULL /* suffix */
+ ,130 /* l_max_chars */
+ );
+ progressText = g_strdup_printf("%s (%.0f / %.0f)"
+ , shortFileName
+ , (float)(MIN(numDone +1.0, numTotal))
+ , (float)(numTotal)
+ );
+ g_free(shortFileName);
+ }
+ else
+ {
+ progressText = g_strdup_printf(_("Tween %.0f / %.0f")
+ , (float)(MIN(numDone +1.0, numTotal))
+ , (float)(numTotal)
+ );
+ }
+
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mtg->masterProgressBar), progressText);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtg->masterProgressBar), percentage);
+ g_free(progressText);
+
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ }
+
+ }
+} /* end p_master_progress_callback */
+
+
+/* --------------------------
+ * p_progress_callback
+ * --------------------------
+ */
+static void
+p_progress_callback(gdouble percentage, gpointer data)
+{
+ MorphTweenGui *mtg;
+
+ mtg = (MorphTweenGui *) data;
+ if(mtg)
+ {
+ if(mtg->progressBar)
+ {
+ guchar *progressText;
+
+ if(mtg->mgpp->do_simple_fade == TRUE)
+ {
+ progressText = g_strdup_printf(_("render tween via fade algorithm %.2f%%")
+ , (float)(percentage * 100)
+ );
+ }
+ else
+ {
+ progressText = g_strdup_printf(_("render tween via morphing algorithm %.2f%%")
+ , (float)(percentage * 100)
+ );
+ }
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mtg->progressBar), progressText);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtg->progressBar), percentage);
+ g_free(progressText);
+
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ }
+ }
+
+
+} /* end p_progress_callback */
+
+
/* --------------------------
@@ -180,6 +303,25 @@ p_check_workpoint_file_and_use_single_fade_if_missing(GapMorphGlobalParams *mgpp
}
}
+
+/* --------------------------------------
+ * on_gboolean_button_update
+ * --------------------------------------
+ */
+static void
+on_gboolean_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ 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));
+}
+
/* --------------------------------------
* p_morph_workpoint_entry_update_cb
* --------------------------------------
@@ -207,6 +349,25 @@ p_morph_workpoint_entry_update_cb(GtkWidget *widget, GapMorphGlobalParams *mgpp)
} /* end p_morph_workpoint_entry_update_cb */
+/* --------------------------------------
+ * p_tween_subdir_entry_update_cb
+ * --------------------------------------
+ */
+static void
+p_tween_subdir_entry_update_cb(GtkWidget *widget, GapMorphGlobalParams *mgpp)
+{
+ g_snprintf(&mgpp->tween_subdir[0]
+ , sizeof(mgpp->tween_subdir)
+ , "%s"
+ , gtk_entry_get_text(GTK_ENTRY(widget))
+ );
+ if(gap_debug)
+ {
+ printf("p_tween_subdir_entry_update_cb tween_subdir: %s\n"
+ , &mgpp->tween_subdir[0]);
+ }
+
+} /* end p_tween_subdir_entry_update_cb */
/* --------------------------------------
@@ -236,7 +397,7 @@ p_create_morph_workpoint_entry(GapMorphGlobalParams *mgpp, MorphTweenGui *mtg, g
}
gtk_table_attach(GTK_TABLE(table), entry, 1, 2, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND, 4, 0);
gimp_help_set_help_data(entry, _("Name of a Workpointfile created with the Morph feature\n"
- "(note that tweens are created via simple fade operations when no workpointfile is available))"), NULL);
+ "(note that tweens are created via simple fade operations when no workpointfile is available)"), NULL);
gtk_widget_show(entry);
g_signal_connect(G_OBJECT(entry), "changed",
@@ -281,7 +442,7 @@ gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp)
gimp_ui_init (GAP_MORPH_TWEEN_PLUGIN_NAME, TRUE);
- dialog = gimp_dialog_new (_("Create one tween"), GAP_MORPH_TWEEN_PLUGIN_NAME,
+ dialog = gimp_dialog_new (_("Create one tween as Layer"), GAP_MORPH_TWEEN_PLUGIN_NAME,
NULL, 0,
gimp_standard_help_func, GAP_MORPH_TWEEN_HELP_ID,
@@ -380,11 +541,508 @@ gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp)
} /* end gap_morph_one_tween_dialog */
+
+
+/* -------------------------------
+ * on_workpoint_generator_response
+ * -------------------------------
+ */
+static void
+on_workpoint_generator_response(GtkWidget *w, gint response_id, MorphTweenGui *mtg)
+{
+ GtkWidget *dialog;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ if(mtg == NULL)
+ {
+ return;
+ }
+ if((mtg->cancelProcessing == FALSE))
+ {
+ if(mtg->processingBusy == TRUE)
+ {
+ return;
+ }
+ mtg->processingBusy = TRUE;
+ gtk_widget_set_sensitive(mtg->from_scale, FALSE);
+ gtk_widget_set_sensitive(mtg->from_spinbutton, FALSE);
+
+
+ mtg->mgpp->use_gravity = FALSE;
+ if(mtg->mgpp->gravity_intensity >= 1.0)
+ {
+ mtg->mgpp->use_gravity = TRUE;
+ }
+ mtg->ret = gap_morph_shape_generate_frame_tween_workpoints(mtg->ainfo_ptr
+ , mtg->mgpp
+ , mtg->masterProgressBar
+ , mtg->progressBar
+ , &mtg->cancelProcessing
+ );
+ mtg->cancelProcessing = TRUE;
+ mtg->processingBusy = FALSE;
+ }
+ /* fall through (close dialog window when cancel or done) */
+ default:
+ dialog = NULL;
+ if(mtg)
+ {
+ if((mtg->processingBusy == TRUE) && (mtg->cancelProcessing == FALSE))
+ {
+ mtg->cancelProcessing = TRUE;
+ return;
+ }
+ /* close (or terminate) dialog window */
+ dialog = mtg->shell;
+ if(dialog)
+ {
+ mtg->shell = NULL;
+ gtk_widget_destroy (dialog);
+ }
+ }
+ gtk_main_quit ();
+ break;
+ }
+} /* end on_workpoint_generator_response */
+
+
+/* ------------------------------------------------
+ * gap_morph_generate_frame_tween_workpoints_dialog
+ * ------------------------------------------------
+ */
+gint32
+gap_morph_generate_frame_tween_workpoints_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp)
+{
+ MorphTweenGui morphTweenGui;
+ MorphTweenGui *mtg;
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *table;
+ GtkObject *adj;
+ GtkWidget *scale;
+ GtkWidget *spinbutton;
+ GtkWidget *label;
+ GtkWidget *checkbutton;
+ GtkWidget *progressBar;
+ gint row;
+
+ mgpp->range_from = ainfo_ptr->curr_frame_nr;
+ mgpp->range_to = ainfo_ptr->last_frame_nr;
+ mtg = &morphTweenGui;
+ mtg->mgpp = mgpp;
+ mtg->ainfo_ptr = ainfo_ptr;
+ mtg->morph_filesel = NULL;
+ mtg->cancelProcessing = FALSE;
+ mtg->processingBusy = FALSE;
+ mtg->ret = 0;
+
+
+ p_check_workpoint_file_and_use_single_fade_if_missing(mgpp);
+
+
+ if(gap_debug)
+ {
+ printf("gap_morph_generate_frame_tween_workpoints_dialog: mgpp->range_from%d mgpp->range_to:%d first:%d, curr:%d, last:%d after_curr:%d\n"
+ , (int)mgpp->range_from
+ , (int)mgpp->range_to
+ , (int)ainfo_ptr->first_frame_nr
+ , (int)ainfo_ptr->curr_frame_nr
+ , (int)ainfo_ptr->last_frame_nr
+ , (int)ainfo_ptr->frame_nr_after_curr_frame_nr
+ );
+ }
+
+
+ gimp_ui_init (GAP_MORPH_WORKPOINTS_PLUGIN_NAME, TRUE);
+
+ dialog = gimp_dialog_new (_("Generate Workpointfiles"), GAP_MORPH_WORKPOINTS_PLUGIN_NAME,
+ NULL, 0,
+ gimp_standard_help_func, GAP_MORPH_WORKPOINTS_HELP_ID,
+
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+
+ NULL);
+
+ mtg->shell = dialog;
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+ gtk_window_set_type_hint (dialog, GDK_WINDOW_TYPE_HINT_NORMAL);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (on_workpoint_generator_response),
+ mtg);
+
+ main_vbox = gtk_vbox_new (FALSE, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
+ gtk_widget_show (main_vbox);
+
+
+ /* Controls */
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ row = 0;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("From:"), SCALE_WIDTH, 7,
+ mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("First processed frame"), NULL);
+ scale = g_object_get_data(G_OBJECT (adj), "scale");
+ spinbutton = g_object_get_data(G_OBJECT (adj), "spinbutton");
+ mtg->from_scale = scale;
+ mtg->from_spinbutton = spinbutton;
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->range_from);
+
+
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("To:"), SCALE_WIDTH, 7,
+ mgpp->range_to, mgpp->range_from +1, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("Last processed frame"), NULL);
+ scale = g_object_get_data(G_OBJECT (adj), "scale");
+ spinbutton = g_object_get_data(G_OBJECT (adj), "spinbutton");
+ mtg->to_scale = scale;
+ mtg->to_spinbutton = spinbutton;
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->range_to);
+
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Num Workpoints:"), SCALE_WIDTH, 7,
+ mgpp->numWorkpoints, 1, 5000, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("Number of workpoints to be generated par processed frame"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->numWorkpoints);
+
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Num Outlinepoints:"), SCALE_WIDTH, 7,
+ mgpp->numOutlinePoints, 1, 100, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("Number of additional workpoints on the outline of opaque image area")
+ , NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->numOutlinePoints);
+
+
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Tween Steps:"), SCALE_WIDTH, 7,
+ mgpp->tween_steps, 1, 100, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("TWEEN-STEPS attribute value to be written to the generated workpoint file."
+ " (Number of tweens to be inserted between 2 frames at tween morphprocessing) "),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->tween_steps);
+
+ row++;
+
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Deform Radius:"), SCALE_WIDTH, 7,
+ mgpp->affect_radius, 0, 10000, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("AFFECT-RADIUS attribute value to be written to the generated workpoint file."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->affect_radius);
+
+ row++;
+
+ if(!mgpp->use_gravity)
+ {
+ mgpp->gravity_intensity = 0.0;
+ }
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Intensity:"), SCALE_WIDTH, 7,
+ mgpp->gravity_intensity, 0.0, 5.0, 0.1, 0.1, 1,
+ TRUE, 0, 0,
+ _("INTENSITY attribute value to be written to the generated workpoint file. "
+ "value 0 turns off intensity desceding deformation, "
+ "morph processing will use linear deform action inside the deform radius"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mgpp->gravity_intensity);
+
+
+
+
+
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Locate Move Radius:"), SCALE_WIDTH, 7,
+ mgpp->locateDetailMoveRadius, 1, 500, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("Locate radius in pixels. "
+ "The workpoint generation searches for corresponding points "
+ "in the next frame within this radius"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->locateDetailMoveRadius);
+
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Locate Shape Radius:"), SCALE_WIDTH, 7,
+ mgpp->locateDetailShapeRadius,
+ GAP_LOCATE_MIN_REF_SHAPE_RADIUS,
+ GAP_LOCATE_MAX_REF_SHAPE_RADIUS,
+ 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("Locate Shaperadius in pixels. "
+ "Defines shape size as area around workpoint to be compared "
+ " when loacting corresponding coordinate in the next frame."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->locateDetailShapeRadius);
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Edge Threshold:"), SCALE_WIDTH, 7,
+ mgpp->edgeColordiffThreshold, 0.01, 0.35, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Edge detection threshold. "
+ "Workpoints are generated on detected edges. "
+ "Edges are detected on pixels where color or opacity differs significant "
+ "from the neighbor pixel."
+ "(e.g. more than the specified edge detection threshold)."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mgpp->edgeColordiffThreshold);
+ if(gap_debug)
+ {
+
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Locate ColordiffEdge Threshold:"), SCALE_WIDTH, 7,
+ mgpp->locateColordiffThreshold, 0.01, 0.1, 0.01, 0.1, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mgpp->locateColordiffThreshold);
+
+ }
+
+ row++;
+
+ /* the use_quality_wp_selection checkbutton */
+ checkbutton = gtk_check_button_new_with_label ( _("Quality"));
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table), checkbutton, 2, 3, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &mgpp->use_quality_wp_selection);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->use_quality_wp_selection);
+ gimp_help_set_help_data(checkbutton,
+ _("ON: Use quality workpoint selection algorithm."
+ "OFF: Use fast workpoint selection algorithm.")
+ , NULL);
+ row++;
+
+
+ /* the overwrite checkbutton */
+ checkbutton = gtk_check_button_new_with_label ( _("Overwrite"));
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table), checkbutton, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &mgpp->overwrite_flag);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->overwrite_flag);
+ gimp_help_set_help_data(checkbutton,
+ _("ON: overwrite existing workpointfiles."
+ "OFF: Skip workpoint generation or add new generated workpoints (see append checkbutton).")
+ , NULL);
+
+
+ /* the overwrite checkbutton */
+ checkbutton = gtk_check_button_new_with_label ( _("Append"));
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table), checkbutton, 2, 3, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &mgpp->append_flag);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->append_flag);
+ gimp_help_set_help_data(checkbutton,
+ _("ON: add newly generated workpoints to existing workpointfiles."
+ "OFF: Skip workpoint generation for frames where workpointfile already exists.")
+ , NULL);
+
+ row++;
+
+ /* the master progress bar */
+
+ /* master progress */
+ label = gtk_label_new (_("Create File(s):"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+ /* the master progress bar */
+ progressBar = gtk_progress_bar_new ();
+ mtg->masterProgressBar = progressBar;
+ {
+ char *suffix;
+ char *msg;
+ suffix = g_strdup_printf("%s.%s"
+ ,"######"
+ ,GAP_MORPH_WORKPOINT_EXTENSION
+ );
+ msg = gap_base_shorten_filename(NULL /* prefix */
+ ,ainfo_ptr->basename /* filenamepart */
+ ,suffix /* suffix */
+ ,130 /* l_max_chars */
+ );
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), msg);
+ g_free(msg);
+ g_free(suffix);
+ }
+
+ gtk_table_attach (GTK_TABLE (table), progressBar, 1, 3, row, row + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (progressBar);
+
+ row++;
+
+ label = gtk_label_new (_("Create Points:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+
+ /* the progress bar */
+ progressBar = gtk_progress_bar_new ();
+ mtg->progressBar = progressBar;
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), " ");
+ gtk_widget_show (progressBar);
+ gtk_table_attach( GTK_TABLE(table), progressBar, 1, 3, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+
+ /* Done */
+
+ gtk_widget_show (dialog);
+
+ gtk_main ();
+ gdk_flush ();
+
+
+ return (mtg->ret);
+} /* end gap_morph_generate_frame_tween_workpoints_dialog */
+
+
+
+/* -------------------------------
+ * on_morph_frame_tweens_response
+ * -------------------------------
+ */
+static void
+on_morph_frame_tweens_response(GtkWidget *w, gint response_id, MorphTweenGui *mtg)
+{
+ GtkWidget *dialog;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ if(mtg == NULL)
+ {
+ return;
+ }
+ if((mtg->cancelProcessing == FALSE))
+ {
+ if(mtg->processingBusy == TRUE)
+ {
+ return;
+ }
+ mtg->processingBusy = TRUE;
+ mtg->mgpp->do_progress = TRUE;
+ mtg->mgpp->master_progress_callback_fptr = p_master_progress_callback;
+ mtg->mgpp->progress_callback_fptr = p_progress_callback;
+ mtg->mgpp->callback_data_ptr = mtg;
+ gtk_widget_set_sensitive(mtg->from_scale, FALSE);
+ gtk_widget_set_sensitive(mtg->from_spinbutton, FALSE);
+
+ mtg->ret = gap_morph_render_frame_tweens(mtg->ainfo_ptr
+ , mtg->mgpp
+ , &mtg->cancelProcessing
+ );
+ mtg->cancelProcessing = TRUE;
+ mtg->processingBusy = FALSE;
+ }
+ /* fall through (close dialog window when cancel or done) */
+ default:
+ dialog = NULL;
+ if(mtg)
+ {
+ if((mtg->processingBusy == TRUE) && (mtg->cancelProcessing == FALSE))
+ {
+ mtg->cancelProcessing = TRUE;
+ return;
+ }
+ /* close (or terminate) dialog window */
+ dialog = mtg->shell;
+ if(dialog)
+ {
+ mtg->shell = NULL;
+ gtk_widget_destroy (dialog);
+ }
+ }
+ gtk_main_quit ();
+ break;
+ }
+} /* end on_morph_frame_tweens_response */
+
+
+
+
+
+
+
+
/* --------------------------------------
* gap_morph_frame_tweens_dialog
* --------------------------------------
*/
-gboolean
+gint32
gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp)
{
MorphTweenGui morphTweenGui;
@@ -396,18 +1054,28 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
GtkWidget *scale;
GtkWidget *spinbutton;
GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *checkbutton;
+ GtkWidget *progressBar;
gint row;
- gboolean run;
gboolean isFrameMissing;
mgpp->range_from = ainfo_ptr->curr_frame_nr;
mgpp->range_to = ainfo_ptr->frame_nr_after_curr_frame_nr;
mtg = &morphTweenGui;
mtg->mgpp = mgpp;
+ mtg->ainfo_ptr = ainfo_ptr;
mtg->morph_filesel = NULL;
+ mtg->ret = -1;
isFrameMissing = FALSE;
+
p_check_workpoint_file_and_use_single_fade_if_missing(mgpp);
+ isFrameMissing = (ainfo_ptr->curr_frame_nr +1) != ainfo_ptr->frame_nr_after_curr_frame_nr;
+ if(!isFrameMissing)
+ {
+ mgpp->range_to = ainfo_ptr->last_frame_nr;
+ }
if(gap_debug)
{
@@ -421,7 +1089,6 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
);
}
- isFrameMissing = (ainfo_ptr->curr_frame_nr +1) != ainfo_ptr->frame_nr_after_curr_frame_nr;
gimp_ui_init (GAP_MORPH_TWEEN_PLUGIN_NAME, TRUE);
@@ -434,6 +1101,8 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
NULL);
+ mtg->shell = dialog;
+
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
@@ -441,6 +1110,11 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
-1);
gimp_window_set_transient (GTK_WINDOW (dialog));
+ gtk_window_set_type_hint (dialog, GDK_WINDOW_TYPE_HINT_NORMAL);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (on_morph_frame_tweens_response),
+ mtg);
main_vbox = gtk_vbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
@@ -456,7 +1130,8 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
gtk_widget_show (table);
row = 0;
- /* morph workpoint entry */
+
+ /* label */
label = gtk_label_new (_("Information:"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
@@ -477,7 +1152,11 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
}
else
{
- label = gtk_label_new (_("WARNING this operation will overwrite all frames between the specified frame range"));
+ label = gtk_label_new (_("this operation creates copies of all frames in the specified range\n"
+ "and the specifed number of tweens as additional tween frames\n"
+ "between all the processed frames in the specified subdirectory.\n"
+ "Provide workpointfiles (one per frame) for morphing based tween rendering\n"
+ "(this can be done with the Morph Workpoint Generator)"));
mgpp->overwrite_flag = TRUE;
}
gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
@@ -489,25 +1168,30 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
_("From:"), SCALE_WIDTH, 7,
- mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
+ mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr-1, 1.0, 10.0, 0,
TRUE, 0, 0,
- NULL, NULL);
+ _("First processed frame"), NULL);
g_signal_connect (adj, "value-changed",
G_CALLBACK (gimp_int_adjustment_update),
&mgpp->range_from);
scale = g_object_get_data(G_OBJECT (adj), "scale");
spinbutton = g_object_get_data(G_OBJECT (adj), "spinbutton");
- gtk_widget_set_sensitive(scale, FALSE);
- gtk_widget_set_sensitive(spinbutton, FALSE);
-
+ mtg->from_scale = scale;
+ mtg->from_spinbutton = spinbutton;
+ if(isFrameMissing)
+ {
+ gtk_widget_set_sensitive(scale, FALSE);
+ gtk_widget_set_sensitive(spinbutton, FALSE);
+ }
+
row++;
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
_("To:"), SCALE_WIDTH, 7,
mgpp->range_to, mgpp->range_from +1, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
TRUE, 0, 0,
- NULL, NULL);
+ _("Last processed frame"), NULL);
g_signal_connect (adj, "value-changed",
G_CALLBACK (gimp_int_adjustment_update),
&mgpp->range_to);
@@ -519,18 +1203,142 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
gtk_widget_set_sensitive(spinbutton, FALSE);
}
+
+ if (isFrameMissing)
+ {
+ row++;
+
+ p_create_morph_workpoint_entry(mgpp, mtg, row, table);
+
+ mgpp->master_tween_steps = 0;
+ mgpp->create_tweens_in_subdir = FALSE;
+ }
+ else
+ {
+ row++;
+
+ mgpp->create_tweens_in_subdir = TRUE;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+ _("Number of Tweens:"), SCALE_WIDTH, 7,
+ mgpp->master_tween_steps, 0, 100, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("Number of tweens to be inserted between 2 frames. "
+ "Value 0 renderes missing frames (via morping or fade)"
+ "but does not create tweens where the "
+ "next frame number is equal to the current processed frame number +1"),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mgpp->master_tween_steps);
+
+ row++;
+
+ /* the create_tweens_in_subdir checkbutton */
+// checkbutton = gtk_check_button_new_with_label ( _("Subdirectory:"));
+// gtk_widget_show (checkbutton);
+// gtk_table_attach( GTK_TABLE(table), checkbutton, 0, 1, row, row+1,
+// GTK_FILL, 0, 0, 0 );
+// g_signal_connect (checkbutton, "toggled",
+// G_CALLBACK (on_gboolean_button_update),
+// &mgpp->create_tweens_in_subdir);
+// gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->create_tweens_in_subdir);
+// gimp_help_set_help_data(checkbutton,
+// _("ON: copy processed frames to a subdirectory "
+// "and create tween frames in this subdirectory via morping."
+// "OFF: Render missing frames via moprhing. ")
+// , NULL);
+
+ /* label */
+ label = gtk_label_new (_("Subdirectory:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+ /* subdirectory entry */
+ entry = gtk_entry_new();
+ mtg->tween_subdir_entry = entry;
+ gtk_widget_set_size_request(entry, SCALE_WIDTH, -1);
+ if(mgpp->tween_subdir[0] != '\0')
+ {
+ gtk_entry_set_text(GTK_ENTRY(entry), &mgpp->tween_subdir[0]);
+ }
+ gtk_table_attach(GTK_TABLE(table), entry, 1, 2, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND, 4, 0);
+ gimp_help_set_help_data(entry, _("Name of a (Sub)directoy to save copies of processed frames "
+ "and generated tweens. "
+ "Note that tweens are created via simple fade operations "
+ "when no workpointfile for the processed frame is available. "
+ "(individual workpointfiles per frame are refered by extension .morphpoints)")
+ , NULL);
+ gtk_widget_show(entry);
+
+ g_signal_connect(G_OBJECT(entry), "changed",
+ G_CALLBACK (p_tween_subdir_entry_update_cb),
+ mgpp);
+
+ row++;
+
+ /* the overwrite checkbutton */
+ checkbutton = gtk_check_button_new_with_label ( _("Overwrite"));
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table), checkbutton, 0, 1, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &mgpp->overwrite_flag);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->overwrite_flag);
+ gimp_help_set_help_data(checkbutton,
+ _("ON: overwrite existing frames."
+ "OFF: skip processing when target frame/tween already exists.")
+ , NULL);
+ }
+
+
row++;
- p_create_morph_workpoint_entry(mgpp, mtg, row, table);
+
+ /* the master progress bar */
+
+ /* master progress */
+ label = gtk_label_new (_("Create Tweenfame(s):"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+ /* the master progress bar */
+ progressBar = gtk_progress_bar_new ();
+ mtg->masterProgressBar = progressBar;
+ gtk_table_attach (GTK_TABLE (table), progressBar, 1, 3, row, row + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (progressBar);
+
+ row++;
+
+ label = gtk_label_new (_("Local Progress:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+
+ /* the progress bar */
+ progressBar = gtk_progress_bar_new ();
+ mtg->progressBar = progressBar;
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), " ");
+ gtk_widget_show (progressBar);
+ gtk_table_attach( GTK_TABLE(table), progressBar, 1, 3, row, row+1,
+ GTK_FILL, 0, 0, 0 );
/* Done */
gtk_widget_show (dialog);
- run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+ gtk_main ();
+ gdk_flush ();
- gtk_widget_destroy (dialog);
- return run;
-} /* end gap_morph_frame_tweens_dialog */
+ return (mtg->ret);
+} /* end gap_morph_frame_tweens_dialog */
diff --git a/gap/gap_morph_tween_dialog.h b/gap/gap_morph_tween_dialog.h
index 13002bc..54e9036 100644
--- a/gap/gap_morph_tween_dialog.h
+++ b/gap/gap_morph_tween_dialog.h
@@ -1,4 +1,4 @@
-/* gap_morph_one_tween_dialog.h
+/* gap_morph_tween_dialog.h
*
* This module handles the GAP morph tween dialog
*/
@@ -30,6 +30,7 @@
#include "libgimp/gimp.h"
#include "gap_libgimpgap.h"
#include "gap_morph_main.h"
+#include "gap_locate.h"
#include "gap_libgimpgap.h"
#define GAP_MORPH_ONE_TWEEN_PLUGIN_NAME "plug_in_gap_morph_one_tween"
@@ -38,7 +39,11 @@
#define GAP_MORPH_TWEEN_PLUGIN_NAME "plug_in_gap_morph_tween"
#define GAP_MORPH_TWEEN_HELP_ID "plug-in-gap-morph-tween"
-gboolean gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp);
-gboolean gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp);
+#define GAP_MORPH_WORKPOINTS_PLUGIN_NAME "plug_in_gap_morph_workpoints"
+#define GAP_MORPH_WORKPOINTS_HELP_ID "plug-in-gap-morph-workpoints"
+gboolean gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp);
+gint32 gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp);
+gint32 gap_morph_generate_frame_tween_workpoints_dialog(GapAnimInfo *ainfo_ptr
+ , GapMorphGlobalParams *mgpp);
#endif
diff --git a/gap/gap_mov_dialog.h b/gap/gap_mov_dialog.h
index c3be9a2..6c996eb 100644
--- a/gap/gap_mov_dialog.h
+++ b/gap/gap_mov_dialog.h
@@ -235,6 +235,7 @@ typedef struct {
/* for the bluebox filter */
GapBlueboxGlobalParams *bbp;
GapBlueboxGlobalParams *bbp_pv;
+
} GapMovValues;
diff --git a/gap/gap_mov_exec.c b/gap/gap_mov_exec.c
index 19b45ef..98fc37f 100644
--- a/gap/gap_mov_exec.c
+++ b/gap/gap_mov_exec.c
@@ -1157,6 +1157,7 @@ p_calculate_settings_for_current_FrameTween(
}
+
/* calculate Zoom (e.g. Size) settings for the currently processed Frame (or tween) */
if ((val_ptr->point[startOfSegmentIndex].accSize != 0)
&& (frameTweensInSegment > 0))
@@ -1186,6 +1187,13 @@ p_calculate_settings_for_current_FrameTween(
}
}
+ if(gap_debug)
+ {
+ printf("currWidth:%.4f\n"
+ ,(float)cur_ptr->currWidth
+ );
+ }
+
/* calculate Rotation settings for the currently processed Frame (or tween) */
if ((val_ptr->point[startOfSegmentIndex].accRotation != 0)
diff --git a/gap/gap_player_dialog.c b/gap/gap_player_dialog.c
index 3e255a7..cb05123 100644
--- a/gap/gap_player_dialog.c
+++ b/gap/gap_player_dialog.c
@@ -7585,7 +7585,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
(GtkAttachOptions) (0), 0, 0);
/* the framenr 1 button (does set Begin of range) */
- framenr_1_button = gtk_button_new_from_stock (GAP_STOCK_SET_RANGE_START);
+ framenr_1_button = gap_stock_button_new (GAP_STOCK_SET_RANGE_START);
gtk_widget_show (framenr_1_button);
gtk_box_pack_start (GTK_BOX (fnr_hbox), framenr_1_button, FALSE, FALSE, 0);
g_object_set_data (G_OBJECT (framenr_1_button), KEY_FRAMENR_BUTTON_TYPE, (gpointer)FRAMENR_BUTTON_BEGIN);
@@ -7612,7 +7612,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
}
/* the framenr 2 button (does set End of range) */
- framenr_2_button = gtk_button_new_from_stock (GAP_STOCK_SET_RANGE_END);
+ framenr_2_button = gap_stock_button_new (GAP_STOCK_SET_RANGE_END);
gtk_widget_show (framenr_2_button);
gtk_box_pack_start (GTK_BOX (fnr_hbox), framenr_2_button, TRUE, TRUE, 0);
g_object_set_data (G_OBJECT (framenr_2_button), KEY_FRAMENR_BUTTON_TYPE, (gpointer)FRAMENR_BUTTON_END);
@@ -7661,7 +7661,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
row++;
/* the from button */
- from_button = gtk_button_new_from_stock (GAP_STOCK_RANGE_START);
+ from_button = gap_stock_button_new (GAP_STOCK_RANGE_START);
gpp->from_button = from_button;
/* the from button */
gtk_widget_show (from_button);
@@ -7711,7 +7711,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
row++;
/* the to button */
- to_button = gtk_button_new_from_stock (GAP_STOCK_RANGE_END);
+ to_button = gap_stock_button_new (GAP_STOCK_RANGE_END);
gpp->to_button = to_button;
gtk_widget_show (to_button);
gtk_table_attach (GTK_TABLE (table2), to_button, colbutton, colbutton+1, row, row+1
@@ -7765,7 +7765,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
row++;
/* the origspeed_button */
- origspeed_button = gtk_button_new_from_stock (GAP_STOCK_SPEED);
+ origspeed_button = gap_stock_button_new (GAP_STOCK_SPEED);
gtk_widget_show (origspeed_button);
gtk_table_attach (GTK_TABLE (table2), origspeed_button, colbutton, colbutton+1, row, row+1,
(GtkAttachOptions) (GTK_FILL),
@@ -8036,7 +8036,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
/* the PLAY button */
- play_button = gtk_button_new_from_stock (GAP_STOCK_PLAY);
+ play_button = gap_stock_button_new_with_label (GAP_STOCK_PLAY, _("Play"));
gtk_widget_show (play_button);
gtk_box_pack_start (GTK_BOX (play_n_stop_hbox), play_button, FALSE, TRUE, 0);
gimp_help_set_help_data (play_button, _("Start playback. "
@@ -8048,7 +8048,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
gpp);
/* the PAUSE button */
- pause_button = gtk_button_new_from_stock (GAP_STOCK_PAUSE);
+ pause_button = gap_stock_button_new_with_label (GAP_STOCK_PAUSE, _("Pause"));
gtk_widget_show (pause_button);
gtk_widget_set_events(pause_button, GDK_BUTTON_PRESS_MASK);
gtk_box_pack_start (GTK_BOX (play_n_stop_hbox), pause_button, FALSE, TRUE, 0);
@@ -8059,7 +8059,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
gpp);
/* the PLAY_REVERSE button */
- back_button = gtk_button_new_from_stock (GAP_STOCK_PLAY_REVERSE);
+ back_button = gap_stock_button_new_with_label (GAP_STOCK_PLAY_REVERSE, _("Reverse"));
gtk_widget_show (back_button);
gtk_box_pack_start (GTK_BOX (play_n_stop_hbox), back_button, FALSE, TRUE, 0);
gimp_help_set_help_data (back_button, _("Start reverse playback. "
@@ -8727,3 +8727,7 @@ gap_player_dlg_playback_dialog(GapPlayerMainGlobalParams *gpp)
} /* end gap_player_dlg_playback_dialog */
+
+
+
+
diff --git a/gap/gap_stock.c b/gap/gap_stock.c
index c8d9aa5..afb1395 100644
--- a/gap/gap_stock.c
+++ b/gap/gap_stock.c
@@ -35,6 +35,7 @@
#include "images/gap-stock-pixbufs.h"
+extern int gap_debug; /* 1 == print debug infos , 0 dont print debug infos */
static GtkIconFactory *gap_icon_factory = NULL;
@@ -83,6 +84,16 @@ add_stock_icon (const gchar *stock_id,
pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
+ if(gap_debug)
+ {
+ printf("add_stock_icon stock_id:%s size:%d inline_data:%d pixbuf:%d\n"
+ ,stock_id
+ ,(int)size
+ ,(int)inline_data
+ ,(int)pixbuf
+ );
+ }
+
gtk_icon_source_set_pixbuf (source, pixbuf);
g_object_unref (pixbuf);
@@ -101,6 +112,11 @@ gap_stock_init (void)
{
static gboolean initialized = FALSE;
+ if(gap_debug)
+ {
+ printf("gap_stock_init START\n");
+ }
+
if (initialized)
return;
@@ -137,4 +153,57 @@ gap_stock_init (void)
gtk_stock_add_static (gap_stock_items, G_N_ELEMENTS (gap_stock_items));
initialized = TRUE;
+
+ if(gap_debug)
+ {
+ printf("gap_stock_init DONE\n");
+ }
+}
+
+
+
+GtkWidget *
+gap_stock_button_new_with_label(const char *stock_id, const char *optional_label)
+{
+ GtkWidget *button;
+ GtkWidget *image;
+
+ button = gtk_button_new ();
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_show (image);
+
+
+ if (optional_label != NULL)
+ {
+ GtkWidget *hbox;
+ GtkWidget *label;
+
+ hbox = gtk_hbox_new (FALSE, 2);
+ gtk_widget_show (hbox);
+
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+ label = gtk_label_new (optional_label);
+ gtk_widget_show (label);
+
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
+
+ gtk_container_add (GTK_CONTAINER (button), hbox);
+
+ }
+ else
+ {
+ gtk_container_add (GTK_CONTAINER (button), image);
+ }
+
+ return (button);
}
+
+GtkWidget *
+gap_stock_button_new(const char *stock_id)
+{
+ return(gap_stock_button_new_with_label(stock_id, NULL));
+}
+
diff --git a/gap/gap_stock.h b/gap/gap_stock.h
index 101a0af..2ce22f7 100644
--- a/gap/gap_stock.h
+++ b/gap/gap_stock.h
@@ -49,7 +49,11 @@
#define GAP_STOCK_SET_RANGE_START "gap-set-range-start"
#define GAP_STOCK_SPEED "gap-speed"
-void gap_stock_init (void);
+#include <gtk/gtk.h>
+
+void gap_stock_init (void);
+GtkWidget * gap_stock_button_new(const char *stock_id);
+GtkWidget * gap_stock_button_new_with_label(const char *stock_id, const char *optional_label);
#endif /* __GAP_STOCK_H__ */
diff --git a/gap/gap_story_att_trans_dlg.c b/gap/gap_story_att_trans_dlg.c
index 3171627..e75f3d2 100644
--- a/gap/gap_story_att_trans_dlg.c
+++ b/gap/gap_story_att_trans_dlg.c
@@ -84,6 +84,7 @@
extern int gap_debug; /* 1 == print debug infos , 0 dont print debug infos */
+static gdouble p_getConvertFactor(gint att_type_idx);
static void p_attw_prop_response(GtkWidget *widget
, gint response_id
, GapStbAttrWidget *attw
@@ -232,6 +233,17 @@ static void p_create_and_attach_att_arr_widgets(const char *row_title
, gint32 *att_arr_value_accel_ptr
);
+
+static gdouble
+p_getConvertFactor(gint att_type_idx)
+{
+ if (att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+ {
+ return 1.0;
+ }
+ return (CONVERT_TO_100PERCENT);
+}
+
/* ---------------------------------
* p_attw_prop_response
* ---------------------------------
@@ -347,9 +359,11 @@ p_attw_prop_reset_all(GapStbAttrWidget *attw)
, attw->stb_elem_refptr->att_arr_enable[ii]);
gtk_adjustment_set_value(GTK_ADJUSTMENT(attw->att_rows[ii].spinbutton_from_adj)
- , attw->stb_elem_refptr->att_arr_value_from[ii] * CONVERT_TO_100PERCENT);
+ , attw->stb_elem_refptr->att_arr_value_from[ii] *
+ p_getConvertFactor(ii));
gtk_adjustment_set_value(GTK_ADJUSTMENT(attw->att_rows[ii].spinbutton_to_adj)
- , attw->stb_elem_refptr->att_arr_value_to[ii] * CONVERT_TO_100PERCENT);
+ , attw->stb_elem_refptr->att_arr_value_to[ii] *
+ p_getConvertFactor(ii));
gtk_adjustment_set_value(GTK_ADJUSTMENT(attw->att_rows[ii].spinbutton_dur_adj)
, attw->stb_elem_refptr->att_arr_value_dur[ii]);
@@ -446,7 +460,7 @@ p_attw_timer_job(GapStbAttrWidget *attw)
* p_attw_update_properties
* ------------------------------------
* render graphical view respecting all enabled attributes
- * opacity, move X/Y and Zoom X/Y
+ * rotate, opacity, move X/Y and Zoom X/Y
*/
static void
p_attw_update_properties(GapStbAttrWidget *attw)
@@ -540,6 +554,10 @@ p_get_default_attribute(GapStbAttrWidget *attw
{
return (0.5); /* 1.0 scale to half size */
}
+ if (att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+ {
+ return (180.0);
+ }
return (0.5); /* indicates 50% opacity */
}
@@ -555,6 +573,10 @@ p_get_default_attribute(GapStbAttrWidget *attw
{
return (2.0); /* 1.0 scale to doble size */
}
+ if (att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+ {
+ return (360.0);
+ }
return (0.75); /* indicates 75% opacity */
}
@@ -593,7 +615,7 @@ p_attw_start_button_clicked_callback(GtkWidget *widget
gdouble attr_value;
p_attw_push_undo_and_set_unsaved_changes(attw);
- attr_value = CONVERT_TO_100PERCENT * p_get_default_attribute(attw
+ attr_value = p_getConvertFactor(att_type_idx) * p_get_default_attribute(attw
, bevent
, att_type_idx
, TRUE /* use from value for reset */
@@ -624,7 +646,7 @@ p_attw_end_button_clicked_callback(GtkWidget *widget
gdouble attr_value;
p_attw_push_undo_and_set_unsaved_changes(attw);
- attr_value = CONVERT_TO_100PERCENT * p_get_default_attribute(attw
+ attr_value = p_getConvertFactor(att_type_idx) * p_get_default_attribute(attw
, bevent
, att_type_idx
, FALSE /* use to value for reset */
@@ -728,14 +750,15 @@ p_attw_gdouble_adjustment_callback(GtkObject *obj, gdouble *val)
{
GapStbAttrWidget *attw;
gdouble l_val;
+ gint att_type_idx;
-
+ att_type_idx = g_object_get_data( G_OBJECT(obj), "att_type_idx" );
attw = g_object_get_data( G_OBJECT(obj), OBJ_DATA_KEY_ATTW );
if(attw)
{
if(attw->stb_elem_refptr)
{
- l_val = (GTK_ADJUSTMENT(obj)->value) / CONVERT_TO_100PERCENT;
+ l_val = (GTK_ADJUSTMENT(obj)->value) / p_getConvertFactor(att_type_idx);
if(gap_debug)
{
printf("gdouble_adjustment_callback: old_val:%f val:%f\n"
@@ -1031,6 +1054,7 @@ p_calculate_prefetch_render_attributes(GapStbAttrWidget *attw
, attw->stb_elem_refptr->att_keep_proportions
, attw->stb_elem_refptr->att_fit_width
, attw->stb_elem_refptr->att_fit_height
+ , att_tab [img_idx][GAP_STB_ATT_TYPE_ROTATE]
, att_tab [img_idx][GAP_STB_ATT_TYPE_OPACITY]
, att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_X]
, att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_Y]
@@ -1096,6 +1120,7 @@ p_calculate_render_attributes(GapStbAttrWidget *attw
, attw->stb_elem_refptr->att_keep_proportions
, attw->stb_elem_refptr->att_fit_width
, attw->stb_elem_refptr->att_fit_height
+ , att_tab [img_idx][GAP_STB_ATT_TYPE_ROTATE]
, att_tab [img_idx][GAP_STB_ATT_TYPE_OPACITY]
, att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_X]
, att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_Y]
@@ -1436,6 +1461,10 @@ p_create_transformed_layer(gint32 image_id
, calculated->x_offs
, calculated->y_offs
);
+
+ gap_story_transform_rotate_layer(image_id, *layer_id_ptr, calculated->rotate);
+
+
gimp_layer_set_opacity(*layer_id_ptr
, calculated->opacity
);
@@ -2117,7 +2146,7 @@ p_fetch_video_frame_as_layer(GapStbMainGlobalParams *sgpp
if(! gimp_drawable_has_alpha (l_new_layer_id))
{
- /* implicite add an alpha channel before we try to raise */
+ /* implicitly add an alpha channel before we try to raise */
gimp_layer_add_alpha(l_new_layer_id);
}
@@ -2410,9 +2439,10 @@ p_create_and_attach_att_arr_widgets(const char *row_title
GtkObject *adj;
GtkWidget *spinbutton;
gint col;
+ gdouble convertFactor;
col = column;
-
+ convertFactor = p_getConvertFactor(att_type_idx);
/* enable label */
label = gtk_label_new(row_title);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
@@ -2458,7 +2488,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
col++;
/* From (Start value of transition) button */
- adj = gtk_adjustment_new ( *att_arr_value_from_ptr * CONVERT_TO_100PERCENT
+ adj = gtk_adjustment_new ( *att_arr_value_from_ptr * convertFactor
, lower_constraint
, upper_constraint
, step_increment
@@ -2476,6 +2506,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
gtk_widget_set_size_request (spinbutton, 80, -1);
gimp_help_set_help_data (spinbutton, from_tooltip, NULL);
+ g_object_set_data(G_OBJECT(adj), "att_type_idx", att_type_idx);
g_object_set_data(G_OBJECT(adj), OBJ_DATA_KEY_ATTW, attw);
g_signal_connect (G_OBJECT (adj), "value_changed",
G_CALLBACK (p_attw_gdouble_adjustment_callback),
@@ -2500,7 +2531,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
col++;
/* the To value spinbutton */
- adj = gtk_adjustment_new ( *att_arr_value_to_ptr * CONVERT_TO_100PERCENT
+ adj = gtk_adjustment_new ( *att_arr_value_to_ptr * convertFactor
, lower_constraint
, upper_constraint
, step_increment
@@ -2518,6 +2549,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
gtk_widget_set_size_request (spinbutton, 80, -1);
gimp_help_set_help_data (spinbutton, to_tooltip, NULL);
+ g_object_set_data(G_OBJECT(adj), "att_type_idx", att_type_idx);
g_object_set_data(G_OBJECT(adj), OBJ_DATA_KEY_ATTW, attw);
g_signal_connect (G_OBJECT (adj), "value_changed",
G_CALLBACK (p_attw_gdouble_adjustment_callback),
@@ -2814,6 +2846,33 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
gint att_type_idx;
gint col = 0;
+
+ att_type_idx = GAP_STB_ATT_TYPE_ROTATE;
+ p_create_and_attach_att_arr_widgets(_("Rotate:")
+ , attw
+ , table
+ , row
+ , col
+ , att_type_idx
+ , -36000.0 /* lower constraint for the from/to values */
+ , 36000.0 /* upper constraint for the from/to values */
+ , 1.0 /* step increment for the from/to values */
+ , 45.0 /* page increment for the from/to values */
+ , 0.0 /* page size for the from/to values */
+ , 1 /* digits for the from/to values */
+ , _("ON: Enable rotation settings")
+ , &attw->stb_elem_refptr->att_arr_enable[att_type_idx]
+ , _("rotation value in degree for the first handled frame ")
+ , &attw->stb_elem_refptr->att_arr_value_from[att_type_idx]
+ , _("rotation value in degree for the last handled frame ")
+ , &attw->stb_elem_refptr->att_arr_value_to[att_type_idx]
+ , _("number of frames")
+ , &attw->stb_elem_refptr->att_arr_value_dur[att_type_idx]
+ , _("acceleration characteristic for rotation (1 for constant speed, positive: acceleration, negative: deceleration)")
+ , &attw->stb_elem_refptr->att_arr_value_accel[att_type_idx]
+ );
+
+ row++;
att_type_idx = GAP_STB_ATT_TYPE_OPACITY;
p_create_and_attach_att_arr_widgets(_("Opacity:")
, attw
@@ -2841,6 +2900,7 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
, &attw->stb_elem_refptr->att_arr_value_accel[att_type_idx]
);
+
row++;
att_type_idx = GAP_STB_ATT_TYPE_MOVE_X;
p_create_and_attach_att_arr_widgets(_("Move X:")
@@ -2858,10 +2918,10 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
, _("ON: Enable move horizontal settings")
, &attw->stb_elem_refptr->att_arr_enable[att_type_idx]
, _("move horizontal value for the first handled frame "
- " where 0.0 is centered, 100.0 is outside right, -100.0 is outside left)")
+ "where 0.0 is centered, 100.0 is outside right, -100.0 is outside left")
, &attw->stb_elem_refptr->att_arr_value_from[att_type_idx]
, _("move horizontal value for the last handled frame "
- " where 0.0 is centered, 100.0 is outside right, -100.0 is outside left)")
+ "where 0.0 is centered, 100.0 is outside right, -100.0 is outside left")
, &attw->stb_elem_refptr->att_arr_value_to[att_type_idx]
, _("number of frames")
, &attw->stb_elem_refptr->att_arr_value_dur[att_type_idx]
@@ -2887,10 +2947,10 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
, _("ON: Enable move vertical settings")
, &attw->stb_elem_refptr->att_arr_enable[att_type_idx]
, _("move vertical value for the first handled frame "
- " where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top)")
+ "where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top")
, &attw->stb_elem_refptr->att_arr_value_from[att_type_idx]
, _("move vertical value for the last handled frame "
- " where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top)")
+ "where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top")
, &attw->stb_elem_refptr->att_arr_value_to[att_type_idx]
, _("number of frames")
, &attw->stb_elem_refptr->att_arr_value_dur[att_type_idx]
diff --git a/gap/gap_story_dialog.c b/gap/gap_story_dialog.c
index a9b9bd3..6ca7576 100644
--- a/gap/gap_story_dialog.c
+++ b/gap/gap_story_dialog.c
@@ -23,7 +23,7 @@
* version 2.2.1; 2006/02/07 hof: support drag&drop destination for image/videofilenames
* version 2.1.0a; 2005/01/22 hof: copy/cut/paste handling via keys (ctrl-c, ctrl-x, ctrl-v)
* version 2.1.0a; 2004/12/05 hof: added global layout properties dialog
- * version 1.3.27a; 2004/03/15 hof: videothumbnails are kept in memory
+ * version 1.3.27a; 2004/03/15 hof: video thumbnails are kept in memory
* for the startframes in all MOVIE clips
* (common list for both storyboard and cliplist)
* Need define GAP_ENABLE_VIDEOAPI_SUPPORT
@@ -523,17 +523,17 @@ p_get_begin_and_end_for_single_clip_playback(gint32 *begin_frame, gint32 *end_fr
{
*begin_frame = stb_elem->from_frame;
*end_frame = stb_elem->to_frame;
-
+
if(stb_elem->record_type == GAP_STBREC_VID_IMAGE)
{
char *l_basename;
long l_number;
-
+
l_basename = gap_lib_alloc_basename(stb_elem->orig_filename, &l_number);
g_free(l_basename);
*begin_frame = l_number;
*end_frame = l_number;
-
+
}
} /* end p_get_begin_and_end_for_single_clip_playback */
@@ -1601,7 +1601,7 @@ gap_story_fw_abstract_properties_dialog (GapStbFrameWidget *fw)
* in case a valid (positive) vtrack number is specified.
* (this allows quick direct frame fetch operations without
* the use of the storyboard render processor engine,
- * VID_PLAY_SECTION elemnts are rendered as black frames in this
+ * VID_PLAY_SECTION elements are rendered as black frames in this
* playback mode)
*
* a negative vtrack number (-1) triggers composite video playback
@@ -1679,7 +1679,7 @@ p_story_call_player(GapStbMainGlobalParams *sgpp
/* make a copy of the storyboard list
* for the internal use in the player
* (note that section clip playback is only supported
- * if the main section is active and composite video playback is selectd)
+ * if the main section is active and composite video playback is selected)
*/
if(play_all)
{
@@ -1700,7 +1700,7 @@ p_story_call_player(GapStbMainGlobalParams *sgpp
if(stb->active_section == gap_story_find_main_section(stb))
{
/* for composite video playback of "only selected elements" in
- * the main section, we have to copy implictely
+ * the main section, we have to copy implicitly
* all referenced sub sections.
* (but we simply add all available subsection)
*/
@@ -1948,7 +1948,7 @@ p_call_master_encoder(GapStbMainGlobalParams *sgpp
{
gtk_main_iteration ();
}
-
+
dummy_layer_id = gap_image_get_any_layer(sgpp->image_id);
/* generic call of GAP master video encoder plugin */
l_params = gimp_run_procedure (GAP_PLUG_IN_MASTER_ENCODER,
@@ -2493,7 +2493,7 @@ p_cancel_button_cb (GtkWidget *w,
}
else
{
- printf("VIDEO-CANCEL occured implicite\n");
+ printf("VIDEO-CANCEL occurred implicitly\n");
}
}
@@ -2512,7 +2512,7 @@ p_cancel_button_cb (GtkWidget *w,
sgpp->vthumb_prefetch_in_progress = GAP_VTHUMB_PREFETCH_NOT_ACTIVE;
if(sgpp->menu_item_win_vthumbs != NULL)
{
- /* implicite switch off auto_vthumb (by setting the relevant menu item FALSE)
+ /* implicitly switch off auto_vthumb (by setting the relevant menu item FALSE)
*/
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sgpp->menu_item_win_vthumbs), FALSE);
}
@@ -3517,7 +3517,7 @@ p_single_clip_playback(GapStbFrameWidget *fw)
{
gint32 stb_in_track;
- /* select the secetion clip
+ /* select the section clip
* (because section playback is only possible in composite
* mode (using the storyboard render processor)
* an composite playback operates on mapping for selected clips)
@@ -3548,17 +3548,17 @@ p_single_clip_playback(GapStbFrameWidget *fw)
{
gint32 l_begin_frame;
gint32 l_end_frame;
-
+
p_get_begin_and_end_for_single_clip_playback(&l_begin_frame, &l_end_frame, fw->stb_elem_refptr);
imagename = gap_story_get_filename_from_elem(fw->stb_elem_refptr);
-
+
if(gap_debug)
{
printf("CALLING Player from single clip imagename:%s\n from:%d (%d) to:%d (%d) type:%d\n orig_filename:%s\n\n"
, imagename
,(int)fw->stb_elem_refptr->from_frame
,(int)l_begin_frame
- ,(int)fw->stb_elem_refptr->to_frame
+ ,(int)fw->stb_elem_refptr->to_frame
,(int)l_end_frame
,(int)fw->stb_elem_refptr->record_type
, fw->stb_elem_refptr->orig_filename
@@ -3755,7 +3755,7 @@ gap_story_dlg_pw_update_mask_references(GapStbTabWidgets *tabw)
* ---------------------------------
* set specified section as new active section,
* refresh the section combo box.
- * (this causes an implicite refresh in case
+ * (this causes an implicit refresh in case
* where the active_section has changed)
*/
void
@@ -5933,7 +5933,7 @@ p_make_menu_global(GapStbMainGlobalParams *sgpp, GtkWidget *menu_bar)
);
sgpp->menu_item_win_vthumbs =
- p_make_check_item_with_label(file_menu, _("Videothumbnails")
+ p_make_check_item_with_label(file_menu, _("Video thumbnails")
, p_menu_win_vthumbs_toggle_cb
, sgpp
, sgpp->auto_vthumb
@@ -6627,12 +6627,12 @@ p_prefetch_vthumbs (GapStbMainGlobalParams *sgpp, GapStoryBoard *stb)
if(sgpp->progress_bar_master)
{
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sgpp->progress_bar_master),
- _("videothumbnail cancelled"));
+ _("video thumbnail cancelled"));
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sgpp->progress_bar_master), 0);
}
if(gap_debug)
{
- printf("p_prefetch_vthumbs : (1) videothumbnail cancelled: cancel_video_api: %d auto_vthumb_refresh_canceled:%d auto_vthumb:%d\n"
+ printf("p_prefetch_vthumbs : (1) video thumbnail cancelled: cancel_video_api: %d auto_vthumb_refresh_canceled:%d auto_vthumb:%d\n"
,(int)sgpp->cancel_video_api
,(int)sgpp->auto_vthumb_refresh_canceled
,(int)sgpp->auto_vthumb
@@ -6653,7 +6653,7 @@ p_prefetch_vthumbs (GapStbMainGlobalParams *sgpp, GapStoryBoard *stb)
if(sgpp->progress_bar_master)
{
- msg = g_strdup_printf(_("Fetching videothumbnail for clip: %d (out of %d)")
+ msg = g_strdup_printf(_("Fetching video thumbnail for clip: %d (out of %d)")
,(int)l_count
,(int)l_total
);
@@ -6778,7 +6778,7 @@ p_optimized_prefetch_vthumbs (GapStbMainGlobalParams *sgpp)
* note that prefetch will be very quick for all clips where vthumb is already present
* from the cancelled previous prefetch cycle)
* (currently this attempt leads to crashes that i could not locate yet)
- * - the other (currently implemented) option is to cancel prefetch and implicite turn off auto_vthumb mode
+ * - the other (currently implemented) option is to cancel prefetch and implicitly turn off auto_vthumb mode
*/
option_restart = TRUE;
@@ -6799,7 +6799,7 @@ p_optimized_prefetch_vthumbs (GapStbMainGlobalParams *sgpp)
// if(sgpp->progress_bar_master)
// {
// gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sgpp->progress_bar_master),
-// _("videothumbnail cancelled"));
+// _("video thumbnail cancelled"));
// gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sgpp->progress_bar_master), 0);
// }
// p_cancel_button_cb(NULL, sgpp);
@@ -7450,7 +7450,7 @@ p_activate_section_by_combo_index(gint target_index
{
/* the callback p_vtrack_spinbutton_cb is normally triggered automatically
* if the current vtrack number is set to the another number.
- * this explicite call is for the case where the track number
+ * this explicit call is for the case where the track number
* did not change. (but same track number in another section requires
* always a refresh, that is triggered here)
*/
@@ -7677,7 +7677,7 @@ p_create_button_bar(GapStbTabWidgets *tabw
G_CALLBACK (p_tabw_section_properties_cut_cb),
tabw);
gimp_help_set_help_data (button,
- _("Show Section properites window"),
+ _("Show Section properties window"),
NULL);
gtk_widget_show (button);
@@ -8428,7 +8428,7 @@ gap_storyboard_dialog(GapStbMainGlobalParams *sgpp)
gtk_box_pack_start (GTK_BOX (hbox_stb_frame), button, FALSE, FALSE, 0);
gtk_widget_show (button);
gimp_help_set_help_data (button
- , _("Cancel video access if in progress and disable automatic videothumbnails")
+ , _("Cancel video access if in progress and disable automatic video thumbnails")
, NULL);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (p_cancel_button_cb),
@@ -8680,7 +8680,7 @@ p_parse_aspect_width_and_height(const char *buff, gint32 *aspect_width, gint32 *
static void
p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
{
- GapArrArg argv[12];
+ GapArrArg argv[13];
static char *radio_args[4] = { N_("automatic"), "libmpeg3", "libavformat", "quicktime4linux" };
static char *radio_aspect_args[4] = { N_("none"), "4:3", "16:9", "3:2"};
gint l_ii;
@@ -8701,6 +8701,7 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
gchar buf_preferred_decoder[60];
gchar buf_aspect_string[40];
gchar *label_txt;
+ gchar l_master_insert_alpha_format[GAP_STORY_MAX_STORYFILENAME_LEN];
gchar l_master_insert_area_format[GAP_STORY_MAX_STORYFILENAME_LEN];
gboolean l_rc;
@@ -8929,6 +8930,30 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
argv[l_ii].has_default = TRUE;
argv[l_ii].flt_default = argv[l_ii].flt_ret;
+
+
+
+ l_ii++;
+ l_master_insert_alpha_format[0] = '\0';
+ if (stb_dst->master_insert_alpha_format)
+ {
+ g_snprintf(&l_master_insert_alpha_format[0], sizeof(l_master_insert_alpha_format), "%s"
+ , stb_dst->master_insert_alpha_format
+ );
+ }
+ gap_arr_arg_init(&argv[l_ii], GAP_ARR_WGT_FILESEL);
+ argv[l_ii].label_txt = _("Ext. Transparency Format:");
+ argv[l_ii].entry_width = 250; /* pixel */
+ argv[l_ii].help_txt = _("Format string to provide external tranparency in movie clips. "
+ "(e.g automatic alpha channel insertation)"
+ "this string shall contain \%s as placeholder for the basename of a videoclip and "
+ "optional \%06d as placeholder for the framenumber.");
+ argv[l_ii].text_buf_len = sizeof(l_master_insert_alpha_format);
+ argv[l_ii].text_buf_ret = &l_master_insert_alpha_format[0];
+
+
+
+
l_ii++;
l_master_insert_area_format[0] = '\0';
if (stb_dst->master_insert_area_format)
@@ -8947,7 +8972,6 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
argv[l_ii].text_buf_ret = &l_master_insert_area_format[0];
-
l_ii++;
gap_arr_arg_init(&argv[l_ii], GAP_ARR_WGT_DEFAULT_BUTTON);
argv[l_ii].label_txt = _("Reset"); /* should use GIMP_STOCK_RESET if possible */
@@ -9038,6 +9062,34 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
}
+ if (l_master_insert_alpha_format[0] != '\0')
+ {
+ if (stb_dst->master_insert_alpha_format)
+ {
+ if (strcmp(stb_dst->master_insert_alpha_format, &l_master_insert_alpha_format[0]) != 0)
+ {
+ stb_dst->unsaved_changes = TRUE;
+ }
+ g_free(stb_dst->master_insert_alpha_format);
+ }
+ else
+ {
+ stb_dst->unsaved_changes = TRUE;
+ }
+ stb_dst->master_insert_alpha_format = g_strdup(&l_master_insert_alpha_format[0]);
+ }
+ else
+ {
+ if (stb_dst->master_insert_alpha_format)
+ {
+ stb_dst->unsaved_changes = TRUE;
+ g_free(stb_dst->master_insert_alpha_format);
+ stb_dst->master_insert_alpha_format = NULL;
+ }
+ }
+
+
+
if (l_master_insert_area_format[0] != '\0')
{
if (stb_dst->master_insert_area_format)
diff --git a/gap/gap_story_file.c b/gap/gap_story_file.c
index 4bf1869..de8b7f4 100644
--- a/gap/gap_story_file.c
+++ b/gap/gap_story_file.c
@@ -26,7 +26,7 @@
/* revision history:
* version 2.3.0; 2006/06/14 hof: added storyboard support for layer_masks,
* overlapping frames (are converted to shadow tracks at processing)
- * and image filpping
+ * and image flipping
* version 1.3.25b; 2004/01/24 hof: created
*/
@@ -80,6 +80,7 @@ static const char *gtab_att_transition_key_words[GAP_STB_ATT_TYPES_ARRAY_MAX] =
, GAP_STBKEY_VID_MOVE_Y
, GAP_STBKEY_VID_ZOOM_X
, GAP_STBKEY_VID_ZOOM_Y
+ , GAP_STBKEY_VID_ROTATE
};
@@ -234,6 +235,15 @@ gap_story_debug_print_elem(GapStoryElem *stb_elem)
{
printf(" filtermacro_file: (NULL)\n");
}
+
+ if(stb_elem->colormask_file)
+ {
+ printf(" colormask_file :%s:\n", stb_elem->colormask_file);
+ }
+ else
+ {
+ printf(" colormask_file: (NULL)\n");
+ }
printf(" fmac_total_steps: %d\n", (int)stb_elem->fmac_total_steps);
printf(" fmac_accel: %d\n", (int)stb_elem->fmac_accel);
@@ -315,6 +325,10 @@ gap_story_debug_print_list(GapStoryBoard *stb)
, (int)stb->master_aspect_height
);
printf("master_1is_toplayer : %d\n", (int)stb->master_vtrack1_is_toplayer );
+ if (stb->master_insert_alpha_format)
+ {
+ printf("master_insert_alpha_format :%s", stb->master_insert_alpha_format);
+ }
if (stb->master_insert_area_format)
{
printf("master_insert_area_format :%s", stb->master_insert_area_format);
@@ -327,7 +341,7 @@ gap_story_debug_print_list(GapStoryBoard *stb)
printf("stb_unique_id : %d\n", (int)stb->stb_unique_id );
GapStorySection *active_section;
-
+
for(active_section = stb->stb_section; active_section != NULL; active_section=active_section->next)
{
if(active_section->section_name == NULL)
@@ -624,6 +638,7 @@ gap_story_new_story_board(const char *filename)
stb->master_aspect_ratio = 0.0; /* 0.0 for none */
stb->master_aspect_width = 0;
stb->master_aspect_height = 0;
+ stb->master_insert_alpha_format = NULL;
stb->master_insert_area_format = NULL;
stb->layout_cols = -1;
@@ -654,11 +669,11 @@ gap_story_new_story_board(const char *filename)
stb->mask_section = gap_story_create_or_find_section_by_name(stb, GAP_STB_MASK_SECTION_NAME);
stb->active_section = gap_story_create_or_find_section_by_name(stb, NULL);
-
+
stb->edit_settings = g_new(GapStoryEditSettings, 1);
if (stb->edit_settings)
{
- /* init default edit settings
+ /* init default edit settings
* (show main section track 1 first page)
*/
stb->edit_settings->section_name = NULL;
@@ -702,6 +717,7 @@ gap_story_new_elem(GapStoryRecordType record_type)
stb_elem->exact_seek = 0;
stb_elem->delace = 0.0;
stb_elem->filtermacro_file = NULL;
+ stb_elem->colormask_file = NULL;
stb_elem->fmac_total_steps = 1;
stb_elem->fmac_accel = 0; /* default: use no acceleration */
stb_elem->preferred_decoder = NULL;
@@ -797,7 +813,7 @@ GapStorySection *
gap_story_new_section()
{
GapStorySection *new_section;
-
+
new_section = g_new(GapStorySection, 1);
new_section->stb_elem = NULL;
new_section->section_name = NULL;
@@ -883,12 +899,12 @@ GapStorySection *
gap_story_find_section_by_story_id(GapStoryBoard *stb, gint32 story_id)
{
GapStorySection *section;
-
+
if (stb == NULL)
{
return (NULL);
}
-
+
/* 1st attempt searches only in the active section */
if (gap_story_elem_find_in_section_by_story_id(stb->active_section, story_id) != NULL)
@@ -896,7 +912,7 @@ gap_story_find_section_by_story_id(GapStoryBoard *stb, gint32 story_id)
return (stb->active_section);
}
- /* 2nd attempt searches in all sections */
+ /* 2nd attempt searches in all sections */
for(section = stb->stb_section; section != NULL; section = section->next)
{
if (gap_story_elem_find_in_section_by_story_id(section, story_id) != NULL)
@@ -904,9 +920,9 @@ gap_story_find_section_by_story_id(GapStoryBoard *stb, gint32 story_id)
return(section);
}
}
-
+
return (NULL);
-
+
} /* end gap_story_find_section_by_story_id */
@@ -925,7 +941,7 @@ gap_story_find_section_by_stb_elem(GapStoryBoard *stb, GapStoryElem *stb_el
{
return (NULL);
}
-
+
return (gap_story_find_section_by_story_id(stb, stb_elem->story_id));
}
@@ -939,7 +955,7 @@ GapStorySection *
gap_story_find_section_by_name(GapStoryBoard *stb, const char *section_name)
{
GapStorySection *section;
-
+
for(section = stb->stb_section; section != NULL; section = section->next)
{
if(p_null_strcmp(section->section_name, section_name))
@@ -975,20 +991,20 @@ gap_story_create_or_find_section_by_name(GapStoryBoard *stb, const char *section
{
GapStorySection *section;
GapStorySection *new_section;
-
+
section = gap_story_find_section_by_name(stb, section_name);
if (section != NULL)
{
return(section);
}
-
-
+
+
new_section = gap_story_new_section();
if (section_name != NULL)
{
new_section->section_name = g_strdup(section_name);
}
-
+
for(section = stb->stb_section; section != NULL; section = section->next)
{
if(section->next == NULL)
@@ -1074,15 +1090,15 @@ gap_story_remove_section(GapStoryBoard *stb, GapStorySection *del_section)
}
}
}
-
-
+
+
prev_section = NULL;
for(section = stb->stb_section; section != NULL; section = section->next)
{
if(del_section == section)
{
stb->unsaved_changes = TRUE;
-
+
if(prev_section == NULL)
{
/* remove the 1st list element */
@@ -1093,15 +1109,15 @@ gap_story_remove_section(GapStoryBoard *stb, GapStorySection *del_section)
/* remove list element that has a rprevious element */
prev_section->next = section->next;
}
-
+
p_free_section(section);
-
+
return(TRUE);
}
prev_section = section;
}
return (FALSE);
-
+
} /* end gap_story_remove_section */
/* ------------------------------------------
@@ -1111,7 +1127,7 @@ gap_story_remove_section(GapStoryBoard *stb, GapStorySection *del_section)
gchar * gap_story_generate_new_unique_section_name(GapStoryBoard *stb)
{
gchar *new_name;
-
+
while (TRUE)
{
new_name = g_strdup_printf(_("section_%02d"), global_section_name_number);
@@ -1139,7 +1155,7 @@ gap_story_filename_is_videofile_by_ext(const char *filename)
char *l_ext;
char *l_extension;
gboolean l_isKnownMovie;
-
+
l_isKnownMovie = FALSE;
l_extension = gap_lib_alloc_extension(filename);
if(l_extension)
@@ -1289,6 +1305,7 @@ p_free_stb_elem(GapStoryElem *stb_elem)
if(stb_elem->basename) { g_free(stb_elem->basename);}
if(stb_elem->ext) { g_free(stb_elem->ext);}
if(stb_elem->filtermacro_file) { g_free(stb_elem->filtermacro_file);}
+ if(stb_elem->colormask_file) { g_free(stb_elem->colormask_file);}
if(stb_elem->preferred_decoder) { g_free(stb_elem->preferred_decoder);}
if(stb_elem->mask_name) { g_free(stb_elem->mask_name);}
} /* end p_free_stb_elem */
@@ -1370,7 +1387,7 @@ gap_story_free_stb_section(GapStorySection *stb_section)
p_free_stb_elem_list(stb_section->stb_elem);
}
stb_section->stb_elem = NULL;
-
+
if (stb_section->section_name)
{
g_free(stb_section->section_name);
@@ -1394,7 +1411,7 @@ gap_story_free_selection_mapping(GapStoryFrameNumberMap *mapping)
if(mapping->map_list)
{
GapStoryFrameNumberMappingElem *map_elem;
-
+
map_elem = mapping->map_list;
while(map_elem != NULL)
{
@@ -1408,7 +1425,7 @@ gap_story_free_selection_mapping(GapStoryFrameNumberMap *mapping)
mapping->total_frames_selected = 0;
}
}
-
+
} /* end gap_story_free_selection_mapping */
/* ----------------------------------------------------
@@ -1437,7 +1454,7 @@ gap_story_free_storyboard(GapStoryBoard **stb_ptr)
}
stb->stb_section = NULL;
}
-
+
/* free edit_settings */
if(stb->edit_settings)
{
@@ -1448,7 +1465,7 @@ gap_story_free_storyboard(GapStoryBoard **stb_ptr)
g_free(stb->edit_settings);
stb->edit_settings = NULL;
}
-
+
/* free selction mapping */
if(stb->mapping)
{
@@ -1481,6 +1498,10 @@ gap_story_free_storyboard(GapStoryBoard **stb_ptr)
{
g_free(stb->warnline);
}
+ if(stb->master_insert_alpha_format)
+ {
+ g_free(stb->master_insert_alpha_format);
+ }
if(stb->master_insert_area_format)
{
g_free(stb->master_insert_area_format);
@@ -1549,7 +1570,7 @@ gap_story_list_append_elem_at_section(GapStoryBoard *stb, GapStoryElem *stb_elem
gap_story_debug_print_elem(stb_elem);
printf("\n\n");
}
-
+
if (active_section == NULL)
{
printf("gap_story_list_append_elem: ERROR active_section is NULL\n");
@@ -1698,14 +1719,14 @@ gap_story_set_fit_size_attributes(GapStoryBoard *stb
* this procedure is typically used when parsing a storyboard file transition element.
* in the file there may be several different transition records
* in continous sequence that are collected in only one transition element in memory
- * (the presence of a video clip record breaks continous sequence).
+ * (the presence of a video clip record breaks continous sequence).
* This procedure shall provide the one transition element that collects the
* transition records read from file. Therefore the already existing list in the active section
* is checked at the last NON comment element. If there is such an elem that is a transition,
* then set *elem_already_in_the_list TRUE and return this element. Otherwise create a new
* transition element.
* if the list already contains previous, but NON continous Fit size attributes for the same track,
- * those attributes will be used to overwrite the defaults of a newly created transition attribute element.
+ * those attributes will be used to overwrite the defaults of a newly created transition attribute element.
* Note: at parsing, the active_section is managed by the line parser and set always
* to the section where the currently parsed record shall be added.
*/
@@ -1910,7 +1931,7 @@ GapStoryElem *
gap_story_find_maskdef_equal_to_ref_elem(GapStoryBoard *stb_ptr, GapStoryElem *stb_ref_elem)
{
GapStoryElem *stb_elem;
-
+
if(stb_ptr->mask_section == NULL)
{
return (NULL);
@@ -1949,7 +1970,7 @@ gap_story_find_maskdef_equal_to_ref_elem(GapStoryBoard *stb_ptr, GapStoryElem *s
* where the elements of hidden mask definition tracks
* are NOT included. (those elements are already processed in this procedure
* and added to the destination storyboard if necessary.
- * -- for implicite copy of mask definitions --
+ * -- for implicit copy of mask definitions --
* this includes the required updates in case of mask_name uniqueness conflicts)
*
* the elements in the returned storyboard all have set the
@@ -1984,8 +2005,8 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
{
/* we are pasting into a normal video track of a non mask section
* check if there are maskdefinitions in the src storyboard that have to
- * be pasted implicite (into mask definition track at end of destination storyboard)
- * Note: implicite mask definitions are already present in the stb_src->mask_section
+ * be pasted implicitly (into mask definition track at end of destination storyboard)
+ * Note: implicit mask definitions are already present in the stb_src->mask_section
* (the cut/copy processing has put them there, using a hidden track number
* GAP_STB_HIDDEN_MASK_TRACK_NUMBER)
*/
@@ -2007,7 +2028,7 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
if(p_story_is_maskdef_equal(stb_elem, stb_maskdef_elem))
{
/* the destination storyboard already includes this
- * maskdefinition (same mask_name and mask same clip), therefore no implicite copy
+ * maskdefinition (same mask_name and mask same clip), therefore no implicit copy
* is necessary
*/
maskdef_copy_required = FALSE;
@@ -2024,7 +2045,7 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
*
* TODO: check if destination already includes same mask_definition clip
* using another mask_name.
- * in such a case copy is not required, but have to
+ * in such a case copy is not required, but have to
* gap_story_update_mask_name_references(stb_src, another_mask_name, stb_elem->mask_name);
*/
@@ -2057,9 +2078,9 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
* In case of explicite pasting elements into the mask section.
* the elements require a new unique mask_name that is generated.
* (existing mask_name references must be removed)
- *
+ *
*/
-
+
for(stb_elem = stb_src->active_section->stb_elem; stb_elem != NULL; stb_elem = stb_elem->next)
{
if(stb_elem->record_type == GAP_STBREC_ATT_TRANSITION)
@@ -2263,7 +2284,7 @@ p_list_insert_at_story_id(GapStorySection *dst_section
*
* Paste into non-mask section: main or sub_section:
* in this case stb_dst->active_section != stb_dst->mask_section
- * all clips in non mask sections of stb_src (this is the are copied into
+ * all clips in non mask sections of stb_src (this is the are copied into
*/
void
gap_story_lists_merge(GapStoryBoard *stb_dst
@@ -2296,7 +2317,7 @@ gap_story_lists_merge(GapStoryBoard *stb_dst
);
stb_vtrack_main_section = gap_story_find_main_section(stb_vtrack_src);
stb_dst_main_section = gap_story_find_main_section(stb_dst);
-
+
if (stb_dst->active_section != stb_dst_main_section)
{
/* other sections than main section do NOT support VID_PLAY_SECTION elements
@@ -2310,9 +2331,9 @@ gap_story_lists_merge(GapStoryBoard *stb_dst
{
/* convert VID_PLAY_SECTION to VID_PLAY_BLACKSECTION
* because we copy from another storyboard into a main section.
- * note: this is done because the more complex implicite copying
- * of all refered sections and references updates is NOT yet supported.
- * a future implemtation must take care to copy only those sections
+ * note: this is done because the more complex implicit copying
+ * of all referred sections and references updates is NOT yet supported.
+ * a future implementation must take care to copy only those sections
* that are not already present in the destination storyboard stb_dst.
* where checking must test if an adequate section is available
* where all list elements match.
@@ -2320,7 +2341,7 @@ gap_story_lists_merge(GapStoryBoard *stb_dst
p_section_to_blacksection(stb_vtrack_main_section);
}
}
-
+
/* insert regular clips (non-mask clips) to the active section in the
* destination storyboard. (e.g. paste explicte selected clips)
*/
@@ -2541,7 +2562,7 @@ p_fetch_string(char **scan_ptr
{
break;
}
- /* continue scann after doublequotes (until EOL or whitespace) */
+ /* continue scan after doublequotes (until EOL or whitespace) */
l_termchar = ' ';
}
if(l_termchar != '"')
@@ -2764,9 +2785,9 @@ p_scan_gboolean(char *ptr, gboolean default_value,
/* -----------------------------
* p_assign_parsed_flip_value
* -----------------------------
- * parse typical (optional) video parameters via predfined pointers.
+ * parse typical (optional) video parameters via predefined pointers.
* if the relevant pointer points to a not null string
- * then scann the parameter and assign it the specified stb_elem
+ * then scan the parameter and assign it the specified stb_elem
*/
static void
p_assign_parsed_flip_value(GapStoryElem *stb_elem
@@ -2805,9 +2826,9 @@ p_assign_parsed_flip_value(GapStoryElem *stb_elem
/* -----------------------------
* p_assign_parsed_video_values
* -----------------------------
- * parse typical (optional) video parameters via predfined pointers.
+ * parse typical (optional) video parameters via predefined pointers.
* if the relevant pointer points to a not null string
- * then scann the parameter and assign it the specified stb_elem
+ * then scan the parameter and assign it the specified stb_elem
*/
static void
p_assign_parsed_video_values(GapStoryElem *stb_elem
@@ -2823,6 +2844,7 @@ p_assign_parsed_video_values(GapStoryElem *stb_elem
, char *mask_disable_ptr
, char *fmac_total_steps_ptr
, char *fmac_accel_ptr
+ , char *colormask_file_ptr
)
{
if(nloops_ptr)
@@ -2862,6 +2884,17 @@ p_assign_parsed_video_values(GapStoryElem *stb_elem
}
}
+
+ if(colormask_file_ptr)
+ {
+ if(*colormask_file_ptr)
+ {
+ stb_elem->colormask_file = g_strdup(colormask_file_ptr);
+ p_flip_dir_separators(stb_elem->colormask_file);
+ }
+ }
+
+
p_assign_parsed_flip_value(stb_elem, flip_ptr);
if(mask_name_ptr)
@@ -2886,6 +2919,10 @@ p_assign_parsed_video_values(GapStoryElem *stb_elem
case 'M':
stb_elem->mask_anchor = GAP_MSK_ANCHOR_MASTER;
break;
+ case 'x':
+ case 'X':
+ stb_elem->mask_anchor = GAP_MSK_ANCHOR_XCOLOR;
+ break;
}
}
}
@@ -2984,7 +3021,7 @@ static void
p_story_parse_line(GapStoryBoard *stb, char *longline
, gint32 longlinenr, char *multi_lines)
{
-#define GAP_MAX_STB_PARAMS_PER_LINE 20
+#define GAP_MAX_STB_PARAMS_PER_LINE 25
char *l_scan_ptr;
char *l_record_key;
char *l_parname;
@@ -3201,6 +3238,15 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
if(*l_wordval[1]) { stb->preferred_decoder = g_strdup(l_wordval[1]); }
goto cleanup; /* master RECORD does not create frame range elements */
}
+ /* Master informations: GAP_STBKEY_VID_MASTER_INSERT_ALPHA */
+ if (strcmp(l_record_key, GAP_STBKEY_VID_MASTER_INSERT_ALPHA) == 0)
+ {
+ char *l_format_ptr = l_wordval[1];
+ if(*l_format_ptr) { p_flip_dir_separators(l_format_ptr);;
+ stb->master_insert_alpha_format = g_strdup(l_format_ptr);
+ }
+ goto cleanup; /* master RECORD does not create frame range elements */
+ }
/* Master informations: GAP_STBKEY_VID_MASTER_INSERT_AREA */
if (strcmp(l_record_key, GAP_STBKEY_VID_MASTER_INSERT_AREA) == 0)
{
@@ -3244,7 +3290,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
char *l_track_ptr = l_wordval[2];
char *l_page_ptr = l_wordval[3];
GapStoryEditSettings *edit_settings;
-
+
edit_settings = stb->edit_settings;
if (edit_settings != NULL)
@@ -3262,18 +3308,19 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
{
stb->active_section = gap_story_create_or_find_section_by_name(stb, NULL);
goto cleanup; /* main section RECORD does not create frame range elements */
- }
+ }
if (strcmp(l_record_key, GAP_STBKEY_SUB_SECTION) == 0)
{
char *l_section_name_ptr = l_wordval[1];
-
+
stb->active_section = gap_story_create_or_find_section_by_name(stb, l_section_name_ptr);
goto cleanup; /* sub section RECORD does not create frame range elements */
- }
+ }
/* =========== Vid Attributes ===== start =========== */
if ((strcmp(l_record_key, GAP_STBKEY_VID_OPACITY) == 0)
+ || (strcmp(l_record_key, GAP_STBKEY_VID_ROTATE) == 0)
|| (strcmp(l_record_key, GAP_STBKEY_VID_ZOOM_X) == 0)
|| (strcmp(l_record_key, GAP_STBKEY_VID_ZOOM_Y) == 0)
|| (strcmp(l_record_key, GAP_STBKEY_VID_MOVE_X) == 0)
@@ -3340,7 +3387,35 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
if(*l_to_ptr) { stb_elem->att_arr_value_to[ii] = p_scan_gdouble(l_to_ptr, 0.0, 1.0, stb); }
else { stb_elem->att_arr_value_to[ii] = stb_elem->att_arr_value_from[ii]; }
if(*l_dur_ptr) { stb_elem->att_arr_value_dur[ii] = p_scan_gint32(l_dur_ptr, 0, 999, stb); }
-
+
+ p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
+
+ if(elem_already_in_the_list != TRUE)
+ {
+ gap_story_list_append_elem(stb, stb_elem);
+ }
+ }
+ goto cleanup;
+ }
+
+ /* ATTRIBUTE: GAP_STBKEY_VID_ROTATE */
+ if (strcmp(l_record_key, GAP_STBKEY_VID_ROTATE) == 0)
+ {
+ gboolean elem_already_in_the_list = FALSE;
+ ii = GAP_STB_ATT_TYPE_ROTATE;
+
+ stb_elem = p_get_att_transition_elem(stb, &elem_already_in_the_list, l_track_nr);
+ if(stb_elem)
+ {
+ stb_elem->att_arr_enable[ii] = TRUE;
+ stb_elem->file_line_nr = longlinenr;
+ stb_elem->orig_src_line = g_strdup(multi_lines);
+ stb_elem->track = l_track_nr;
+ if(*l_from_ptr) { stb_elem->att_arr_value_from[ii] = p_scan_gdouble(l_from_ptr, -36000.0, 36000.0, stb); }
+ if(*l_to_ptr) { stb_elem->att_arr_value_to[ii] = p_scan_gdouble(l_to_ptr, -36000.0, 36000.0, stb); }
+ else { stb_elem->att_arr_value_to[ii] = stb_elem->att_arr_value_from[ii]; }
+ if(*l_dur_ptr) { stb_elem->att_arr_value_dur[ii] = p_scan_gint32(l_dur_ptr, 0, 999, stb); }
+
p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
if(elem_already_in_the_list != TRUE)
@@ -3368,7 +3443,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
if(*l_to_ptr) { stb_elem->att_arr_value_to[ii] = p_scan_gdouble(l_to_ptr, 0.0001, 999.9, stb); }
else { stb_elem->att_arr_value_to[ii] = stb_elem->att_arr_value_from[ii]; }
if(*l_dur_ptr) { stb_elem->att_arr_value_dur[ii] = p_scan_gint32(l_dur_ptr, 0, 999, stb); }
-
+
p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
if(elem_already_in_the_list != TRUE)
@@ -3396,7 +3471,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
if(*l_to_ptr) { stb_elem->att_arr_value_to[ii] = p_scan_gdouble(l_to_ptr, 0.0001, 999.9, stb); }
else { stb_elem->att_arr_value_to[ii] = stb_elem->att_arr_value_from[ii]; }
if(*l_dur_ptr) { stb_elem->att_arr_value_dur[ii] = p_scan_gint32(l_dur_ptr, 0, 999, stb); }
-
+
p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
if(elem_already_in_the_list != TRUE)
@@ -3424,7 +3499,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
if(*l_to_ptr) { stb_elem->att_arr_value_to[ii] = p_scan_gdouble(l_to_ptr, -99999.9, 99999.9, stb); }
else { stb_elem->att_arr_value_to[ii] = stb_elem->att_arr_value_from[ii]; }
if(*l_dur_ptr) { stb_elem->att_arr_value_dur[ii] = p_scan_gint32(l_dur_ptr, 0, 999, stb); }
-
+
p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
if(elem_already_in_the_list != TRUE)
@@ -3452,7 +3527,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
if(*l_to_ptr) { stb_elem->att_arr_value_to[ii] = p_scan_gdouble(l_to_ptr, -99999.9, 99999.9, stb); }
else { stb_elem->att_arr_value_to[ii] = stb_elem->att_arr_value_from[ii]; }
if(*l_dur_ptr) { stb_elem->att_arr_value_dur[ii] = p_scan_gint32(l_dur_ptr, 0, 999, stb); }
-
+
p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
if(elem_already_in_the_list != TRUE)
@@ -3578,6 +3653,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
char *l_mask_disable_ptr = l_wordval[9];
char *l_fmac_total_steps_ptr = l_wordval[10];
char *l_fmac_accel_ptr = l_wordval[11];
+ char *l_colormask_file_ptr = l_wordval[12];
stb_elem = gap_story_new_elem(GAP_STBREC_VID_IMAGE);
@@ -3602,6 +3678,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
, l_mask_disable_ptr
, l_fmac_total_steps_ptr
, l_fmac_accel_ptr
+ , l_colormask_file_ptr
);
p_check_image_numpart(stb_elem, l_filename_ptr);
@@ -3632,6 +3709,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
char *l_mask_disable_ptr = l_wordval[14];
char *l_fmac_total_steps_ptr = l_wordval[15];
char *l_fmac_accel_ptr = l_wordval[16];
+ char *l_colormask_file_ptr = l_wordval[17];
stb_elem = gap_story_new_elem(GAP_STBREC_VID_FRAMES);
if(stb_elem)
@@ -3661,6 +3739,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
, l_mask_disable_ptr
, l_fmac_total_steps_ptr
, l_fmac_accel_ptr
+ , l_colormask_file_ptr
);
if((*l_basename_ptr)
@@ -3702,6 +3781,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
char *l_mask_disable_ptr = l_wordval[17];
char *l_fmac_total_steps_ptr = l_wordval[18];
char *l_fmac_accel_ptr = l_wordval[19];
+ char *l_colormask_file_ptr = l_wordval[20];
stb_elem = gap_story_new_elem(GAP_STBREC_VID_MOVIE);
if(stb_elem)
@@ -3734,6 +3814,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
, l_mask_disable_ptr
, l_fmac_total_steps_ptr
, l_fmac_accel_ptr
+ , l_colormask_file_ptr
);
@@ -3766,8 +3847,9 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
char *l_mask_disable_ptr = l_wordval[13];
char *l_fmac_total_steps_ptr = l_wordval[14];
char *l_fmac_accel_ptr = l_wordval[15];
+ char *l_colormask_file_ptr = l_wordval[16];
GapStorySection *l_main_section;
-
+
l_main_section = gap_story_find_main_section(stb);
if ((strcmp(l_record_key, GAP_STBKEY_VID_PLAY_SECTION) == 0)
@@ -3783,7 +3865,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
*/
stb_elem = gap_story_new_elem(GAP_STBREC_VID_BLACKSECTION);
}
-
+
if(stb_elem)
{
stb_elem->file_line_nr = longlinenr;
@@ -3807,6 +3889,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
, l_mask_disable_ptr
, l_fmac_total_steps_ptr
, l_fmac_accel_ptr
+ , l_colormask_file_ptr
);
gap_story_list_append_elem(stb, stb_elem);
@@ -3834,6 +3917,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
char *l_mask_disable_ptr = l_wordval[12];
char *l_fmac_total_steps_ptr = l_wordval[13];
char *l_fmac_accel_ptr = l_wordval[14];
+ char *l_colormask_file_ptr = l_wordval[15];
stb_elem = gap_story_new_elem(GAP_STBREC_VID_COLOR);
if(stb_elem)
@@ -3859,6 +3943,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
, l_mask_disable_ptr
, l_fmac_total_steps_ptr
, l_fmac_accel_ptr
+ , l_colormask_file_ptr
);
gap_story_list_append_elem(stb, stb_elem);
@@ -3886,6 +3971,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
char *l_mask_disable_ptr = l_wordval[13];
char *l_fmac_total_steps_ptr = l_wordval[14];
char *l_fmac_accel_ptr = l_wordval[15];
+ char *l_colormask_file_ptr = l_wordval[16];
stb_elem = gap_story_new_elem(GAP_STBREC_VID_ANIMIMAGE);
if(stb_elem)
@@ -3911,6 +3997,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
, l_mask_disable_ptr
, l_fmac_total_steps_ptr
, l_fmac_accel_ptr
+ , l_colormask_file_ptr
);
gap_story_list_append_elem(stb, stb_elem);
@@ -4299,7 +4386,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
- /* accept lines with filenames as implicite VID_PLAY_IMAGE commands */
+ /* accept lines with filenames as implicit VID_PLAY_IMAGE commands */
{
char *l_filename_ptr;
@@ -4387,7 +4474,7 @@ p_stb_is_empty_in_all_sections(GapStoryBoard *stb)
return(FALSE);
}
}
-
+
return (TRUE);
} /* end p_stb_is_empty_in_all_sections */
@@ -4784,7 +4871,7 @@ p_story_save_att_transition(FILE *fp, GapStoryElem *stb_elem)
if(ii !=0 )
{
/* print one extra space
- * VID_MOVE_X, VID_MOVE_Y, VID_ZOOM_X, VID_ZOOM_Y
+ * VID_MOVE_X, VID_MOVE_Y, VID_ZOOM_X, VID_ZOOM_Y VID_ROTATE
* are 1 character shorter than VID_OPACITY
*/
fprintf(fp, " ");
@@ -4904,7 +4991,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
{
static gchar *l_bool_tab[2] = { GAP_STB_VTRACK1_TOP_TRUE, GAP_STB_VTRACK1_TOP_FALSE };
gint l_bool_idx;
-
+
l_bool_idx = 0;
if (stb->master_vtrack1_is_toplayer != TRUE)
{
@@ -4993,7 +5080,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
char *section_name;
int l_track;
int l_page;
-
+
l_track = stb->edit_settings->track;
l_page = stb->edit_settings->page;
section_name = "\0";
@@ -5001,7 +5088,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
{
section_name = stb->edit_settings->section_name;
}
-
+
gap_stb_syntax_get_parname_tab(GAP_STBKEY_EDIT_SETTINGS
,&l_parnam_tab
);
@@ -5032,6 +5119,21 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
}
}
+ if (stb->master_insert_alpha_format)
+ {
+ if(*stb->master_insert_alpha_format != '\0')
+ {
+ gap_stb_syntax_get_parname_tab(GAP_STBKEY_VID_MASTER_INSERT_ALPHA
+ ,&l_parnam_tab
+ );
+ fprintf(fp, "%s %s:\"%s\"\n"
+ , GAP_STBKEY_VID_MASTER_INSERT_ALPHA
+ , l_parnam_tab.parname[1]
+ , stb->master_insert_alpha_format
+ );
+ }
+ }
+
if (stb->master_insert_area_format)
{
if(*stb->master_insert_area_format != '\0')
@@ -5047,6 +5149,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
}
}
+
} /* end p_story_save_header */
@@ -5478,6 +5581,7 @@ p_story_save_flip_and_mask_params(FILE *fp, GapStoryElem *stb_elem
, const char *parname_mask_anchor
, const char *parname_mask_stepsize
, const char *parname_mask_disable
+ , const char *parname_colormask_file
)
{
p_story_save_flip_param(fp, stb_elem, parname_flip);
@@ -5501,6 +5605,11 @@ p_story_save_flip_and_mask_params(FILE *fp, GapStoryElem *stb_elem
, parname_mask_anchor
);
break;
+ case GAP_MSK_ANCHOR_XCOLOR:
+ fprintf(fp, " %s:xcolor"
+ , parname_mask_anchor
+ );
+ break;
}
if(stb_elem->mask_stepsize != 1.0)
@@ -5527,6 +5636,14 @@ p_story_save_flip_and_mask_params(FILE *fp, GapStoryElem *stb_elem
}
+ if(stb_elem->colormask_file)
+ {
+ fprintf(fp, " %s:\"%s\""
+ , parname_colormask_file
+ , stb_elem->colormask_file
+ );
+
+ }
} /* end p_story_save_flip_and_mask_params */
/* ---------------------------------
@@ -5671,9 +5788,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
);
p_story_save_filtermacro_params(fp, stb_elem
- , l_parnam_tab.parname[9] /* parname_fmac_file */
- , l_parnam_tab.parname[15] /* parname_fmac_steps */
- , l_parnam_tab.parname[16] /* parname_fmac_accel */
+ , l_parnam_tab.parname[9] /* parname_fmac_file */
+ , l_parnam_tab.parname[15] /* parname_fmac_steps */
+ , l_parnam_tab.parname[16] /* parname_fmac_accel */
);
p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5682,6 +5799,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
, l_parnam_tab.parname[12] /* parname_mask_anchor */
, l_parnam_tab.parname[13] /* parname_mask_stepsize */
, l_parnam_tab.parname[14] /* parname_mask_disable */
+ , l_parnam_tab.parname[17] /* parname_colormask_file */
);
fprintf(fp, "\n");
@@ -5705,9 +5823,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
);
p_story_save_filtermacro_params(fp, stb_elem
- , l_parnam_tab.parname[4] /* parname_fmac_file */
- , l_parnam_tab.parname[10] /* parname_fmac_steps */
- , l_parnam_tab.parname[11] /* parname_fmac_accel */
+ , l_parnam_tab.parname[4] /* parname_fmac_file */
+ , l_parnam_tab.parname[10] /* parname_fmac_steps */
+ , l_parnam_tab.parname[11] /* parname_fmac_accel */
);
p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5716,6 +5834,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
, l_parnam_tab.parname[7] /* parname_mask_anchor */
, l_parnam_tab.parname[8] /* parname_mask_stepsize */
, l_parnam_tab.parname[9] /* parname_mask_disable */
+ , l_parnam_tab.parname[12] /* parname_colormask_file */
);
fprintf(fp, "\n");
if(gap_debug)
@@ -5750,9 +5869,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
);
p_story_save_filtermacro_params(fp, stb_elem
- , l_parnam_tab.parname[11] /* parname_fmac_file */
- , l_parnam_tab.parname[18] /* parname_fmac_steps */
- , l_parnam_tab.parname[19] /* parname_fmac_accel */
+ , l_parnam_tab.parname[11] /* parname_fmac_file */
+ , l_parnam_tab.parname[18] /* parname_fmac_steps */
+ , l_parnam_tab.parname[19] /* parname_fmac_accel */
);
if(stb_elem->preferred_decoder)
@@ -5768,6 +5887,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
, l_parnam_tab.parname[15] /* parname_mask_anchor */
, l_parnam_tab.parname[16] /* parname_mask_stepsize */
, l_parnam_tab.parname[17] /* parname_mask_disable */
+ , l_parnam_tab.parname[20] /* parname_colormask_file */
);
fprintf(fp, "\n");
}
@@ -5795,7 +5915,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
{
l_gap_stb_key_vid_play_section = GAP_STBKEY_VID_PLAY_BLACKSECTION;
}
-
+
gap_stb_syntax_get_parname_tab(GAP_STBKEY_VID_PLAY_SECTION
,&l_parnam_tab
@@ -5815,9 +5935,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
);
p_story_save_filtermacro_params(fp, stb_elem
- , l_parnam_tab.parname[8] /* parname_fmac_file */
- , l_parnam_tab.parname[14] /* parname_fmac_steps */
- , l_parnam_tab.parname[15] /* parname_fmac_accel */
+ , l_parnam_tab.parname[8] /* parname_fmac_file */
+ , l_parnam_tab.parname[14] /* parname_fmac_steps */
+ , l_parnam_tab.parname[15] /* parname_fmac_accel */
);
p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5826,6 +5946,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
, l_parnam_tab.parname[11] /* parname_mask_anchor */
, l_parnam_tab.parname[12] /* parname_mask_stepsize */
, l_parnam_tab.parname[13] /* parname_mask_disable */
+ , l_parnam_tab.parname[16] /* parname_colormask_file */
);
fprintf(fp, "\n");
}
@@ -5854,9 +5975,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
);
p_story_save_filtermacro_params(fp, stb_elem
- , l_parnam_tab.parname[8] /* parname_fmac_file */
- , l_parnam_tab.parname[14] /* parname_fmac_steps */
- , l_parnam_tab.parname[15] /* parname_fmac_accel */
+ , l_parnam_tab.parname[8] /* parname_fmac_file */
+ , l_parnam_tab.parname[14] /* parname_fmac_steps */
+ , l_parnam_tab.parname[15] /* parname_fmac_accel */
);
p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5865,6 +5986,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
, l_parnam_tab.parname[11] /* parname_mask_anchor */
, l_parnam_tab.parname[12] /* parname_mask_stepsize */
, l_parnam_tab.parname[13] /* parname_mask_disable */
+ , l_parnam_tab.parname[16] /* parname_colormask_file */
);
fprintf(fp, "\n");
@@ -5914,7 +6036,7 @@ gap_story_save(GapStoryBoard *stb, const char *filename)
p_story_save_header(stb, fp);
p_story_save_mask_elem_list(stb, fp);
-
+
for(section = stb->stb_section; section != NULL; section = section->next)
{
@@ -6217,7 +6339,7 @@ p_story_locate_framenr(GapStorySection *section, gint32 in_framenr, gint32 in_
/* ------------------------------
* gap_story_locate_framenr
* ------------------------------
- * IN: stb The Storyboardfile
+ * IN: stb The Storyboardfile
* NOTE: this procedure operates on the active_section of the
* specified storyboard.
* IN: in_framenr The Framenumber in sequence to locate
@@ -6297,7 +6419,7 @@ gap_story_count_total_frames_in_section(GapStorySection *section)
l_total_frames = 0;
for(ii=0; ii < GAP_STB_MAX_VID_TRACKS; ii++)
{
- l_total_frames = MAX(l_framecount[ii], l_total_frames);
+ l_total_frames = MAX(l_framecount[ii], l_total_frames);
}
return(l_total_frames);
@@ -6434,7 +6556,7 @@ gap_story_get_master_pixelsize(GapStoryBoard *stb
{
GapStoryLocateRet *stb_ret;
GapStorySection *section;
-
+
section = gap_story_find_main_section(stb);
stb_ret = p_story_locate_framenr(section
@@ -6670,7 +6792,7 @@ gap_story_fake_ainfo_from_stb(GapStoryBoard *stb, gint32 in_track)
l_ainfo_ptr->height = l_height;
l_max_framecount = 0;
-
+
if (stb->mapping != NULL)
{
/* use framecount from selction mapping if
@@ -6678,7 +6800,7 @@ gap_story_fake_ainfo_from_stb(GapStoryBoard *stb, gint32 in_track)
*/
l_max_framecount = stb->mapping->total_frames_selected;
}
-
+
if(l_max_framecount == 0)
{
if(in_track < 0)
@@ -6699,7 +6821,7 @@ gap_story_fake_ainfo_from_stb(GapStoryBoard *stb, gint32 in_track)
l_max_framecount = p_story_count_frames_in_track(stb->active_section ,in_track);
}
}
-
+
l_ainfo_ptr->curr_frame_nr = 1;
l_ainfo_ptr->first_frame_nr = 1;
@@ -6751,6 +6873,7 @@ gap_story_elem_duplicate(GapStoryElem *stb_elem)
if(stb_elem->basename) stb_elem_dup->basename = g_strdup(stb_elem->basename);
if(stb_elem->ext) stb_elem_dup->ext = g_strdup(stb_elem->ext);
if(stb_elem->filtermacro_file) stb_elem_dup->filtermacro_file = g_strdup(stb_elem->filtermacro_file);
+ if(stb_elem->colormask_file) stb_elem_dup->colormask_file = g_strdup(stb_elem->colormask_file);
if(stb_elem->preferred_decoder) stb_elem_dup->preferred_decoder = g_strdup(stb_elem->preferred_decoder);
stb_elem_dup->seltrack = stb_elem->seltrack;
stb_elem_dup->exact_seek = stb_elem->exact_seek;
@@ -6837,6 +6960,7 @@ gap_story_elem_copy(GapStoryElem *stb_elem_dst, GapStoryElem *stb_elem)
if(stb_elem_dst->basename) g_free(stb_elem_dst->basename);
if(stb_elem_dst->ext) g_free(stb_elem_dst->ext);
if(stb_elem_dst->filtermacro_file) g_free(stb_elem_dst->filtermacro_file);
+ if(stb_elem_dst->colormask_file) g_free(stb_elem_dst->colormask_file);
if(stb_elem_dst->preferred_decoder) g_free(stb_elem_dst->preferred_decoder);
if(stb_elem_dst->mask_name) g_free(stb_elem_dst->mask_name);
if(stb_elem_dst->aud_filename) g_free(stb_elem_dst->aud_filename);
@@ -6847,6 +6971,7 @@ gap_story_elem_copy(GapStoryElem *stb_elem_dst, GapStoryElem *stb_elem)
stb_elem_dst->basename = NULL;
stb_elem_dst->ext = NULL;
stb_elem_dst->filtermacro_file = NULL;
+ stb_elem_dst->colormask_file = NULL;
stb_elem_dst->preferred_decoder = NULL;
stb_elem_dst->mask_name = NULL;
stb_elem_dst->aud_filename = NULL;
@@ -6865,6 +6990,7 @@ gap_story_elem_copy(GapStoryElem *stb_elem_dst, GapStoryElem *stb_elem)
if(stb_elem->basename) stb_elem_dst->basename = g_strdup(stb_elem->basename);
if(stb_elem->ext) stb_elem_dst->ext = g_strdup(stb_elem->ext);
if(stb_elem->filtermacro_file) stb_elem_dst->filtermacro_file = g_strdup(stb_elem->filtermacro_file);
+ if(stb_elem->colormask_file) stb_elem_dst->colormask_file = g_strdup(stb_elem->colormask_file);
if(stb_elem->preferred_decoder) stb_elem_dst->preferred_decoder = g_strdup(stb_elem->preferred_decoder);
if(stb_elem->mask_name) stb_elem_dst->mask_name = g_strdup(stb_elem->mask_name);
if(stb_elem->aud_filename) stb_elem_dst->aud_filename = g_strdup(stb_elem->aud_filename);
@@ -6985,7 +7111,7 @@ gap_story_find_mask_reference_by_name(GapStoryBoard *stb_ptr, const char *mask_n
{
return (NULL);
}
-
+
GapStorySection *section;
for(section = stb_ptr->stb_section; section != NULL; section = section->next)
{
@@ -6993,7 +7119,7 @@ gap_story_find_mask_reference_by_name(GapStoryBoard *stb_ptr, const char *mask_n
{
continue;
}
-
+
for(stb_elem = section->stb_elem; stb_elem != NULL; stb_elem = stb_elem->next)
{
if((stb_elem->track != GAP_STB_MASK_TRACK_NUMBER)
@@ -7019,13 +7145,13 @@ gap_story_find_mask_reference_by_name(GapStoryBoard *stb_ptr, const char *mask_n
* copy mask definitions of the specified stb_src
* as hidden mask definitions (track = GAP_STB_HIDDEN_MASK_TRACK_NUMBER)
* to the mask_section of the specified destination storyboard stb_dest.
- * Only those mask definitions are copied, that are refered
+ * Only those mask definitions are copied, that are referred
* in the destination storyboard.
* NOTES: this procedure is often called in cut or copy operations,
* where the destination (stb_dest) is the paste buffer that contains
* already copies of all selected elements (where copy source was stb_src)
- * This procedure is now responsible, to add all refered mask definition
- * elements implicitely as hidden elements to the mask_section.
+ * This procedure is now responsible, to add all referred mask definition
+ * elements implicitly as hidden elements to the mask_section.
*
* the hidden mask definition elements are only relevant for
* paste processing that may follow later on.
@@ -7045,7 +7171,7 @@ p_story_board_duplicate_refered_mask_definitions(GapStoryBoard *stb_dest
printf("@@@@@ p_story_board_duplicate_refered_mask_definitions\n");
gap_story_debug_print_list(stb_src);
}
-
+
if (stb_src->mask_section == NULL)
{
printf("** ERROR p_story_board_duplicate_refered_mask_definitions src mask_section is NULL\n");
@@ -7103,12 +7229,12 @@ p_story_copy_edit_settings (GapStoryEditSettings *dst_edit_settings
g_free(dst_edit_settings->section_name);
dst_edit_settings->section_name = NULL;
}
-
+
if (src_edit_settings->section_name)
{
dst_edit_settings->section_name = g_strdup(src_edit_settings->section_name);
}
-
+
dst_edit_settings->track = src_edit_settings->track;
dst_edit_settings->page = src_edit_settings->page;
}
@@ -7140,7 +7266,7 @@ p_story_copy_edit_settings (GapStoryEditSettings *dst_edit_settings
* (all those mask_definitions that are referred by the copied clips)
* IN: all_masks TRUE: explicite include all mask definitions with regular
* mask track number GAP_STB_MASK_TRACK_NUMBER in the duplicate.
- * FALSE: mask definitions (elements of the mask_section with
+ * FALSE: mask definitions (elements of the mask_section with
* track GAP_STB_MASK_TRACK_NUMBER)
* are automatically included in the duplicate (as GAP_STB_HIDDEN_MASK_TRACK_NUMBER)
* but only when the copied video elements refere to them.
@@ -7190,6 +7316,10 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
{
stb_dup->preferred_decoder = g_strdup(stb_ptr->preferred_decoder);
}
+ if(stb_ptr->master_insert_alpha_format)
+ {
+ stb_dup->master_insert_alpha_format = g_strdup(stb_ptr->master_insert_alpha_format);
+ }
if(stb_ptr->master_insert_area_format)
{
stb_dup->master_insert_area_format = g_strdup(stb_ptr->master_insert_area_format);
@@ -7226,7 +7356,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
{
/* mask_section is the active_section
* this case handled copy from mask definition
- * into main section
+ * into main section
* (the mask_section of the duplicate will be left empty in that case)
*/
dup_to_section = gap_story_find_main_section(stb_dup);
@@ -7239,7 +7369,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
/* handle only specified active section.
* the duplicate will include only clips from the active section
* in its MAIN section.
- *
+ *
*/
if(section == active_section)
{
@@ -7251,7 +7381,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
/* handle all available sections
* (the duplicate will include all the sections from the original with same names and structure)
*/
- dup_to_section =
+ dup_to_section =
gap_story_create_or_find_section_by_name(stb_dup, section->section_name);
/* handle current_vtrack settings of copied section
@@ -7264,15 +7394,15 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
);
}
}
-
-
-
+
+
+
if(dup_to_section == NULL)
{
/* current section not relevant */
continue;
}
-
+
/* copy tracks of the currently processed section */
for(stb_elem = section->stb_elem; stb_elem != NULL; stb_elem = stb_elem->next)
{
@@ -7302,7 +7432,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
dup_flag = TRUE;
}
}
-
+
if(trackselect_flag)
{
if( ((stb_elem->selected) && (dup_mode == GAP_STB_DUPLICATE_ONLY_SELECTED_ELEMS))
@@ -7313,7 +7443,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
dup_flag = TRUE;
}
}
-
+
if(dup_flag)
{
stb_elem_dup = gap_story_elem_duplicate(stb_elem);
@@ -7375,7 +7505,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
}
}
-
+
if(stb_elem_dup)
{
gap_story_list_append_elem_at_section(stb_dup, stb_elem_dup, dup_to_section);
@@ -7383,7 +7513,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
}
}
-
+
} /* end for section loop */
@@ -7420,14 +7550,14 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
/* -----------------------------
* gap_story_duplicate_full
* -----------------------------
- * make a full duplicate of all lists in all sections
+ * make a full duplicate of all lists in all sections
* (but without the comment sublists)
*/
GapStoryBoard *
gap_story_duplicate_full(GapStoryBoard *stb_ptr)
{
GapStoryBoard *stb_dup;
-
+
stb_dup = p_story_board_duplicate(stb_ptr
, GAP_STB_DUPLICATE_ALL_ELEMS
, -1 /* include all video tracks */
@@ -7443,7 +7573,7 @@ gap_story_duplicate_full(GapStoryBoard *stb_ptr)
/* -------------------------------------------
* gap_story_duplicate_active_and_mask_section
* -------------------------------------------
- * make a duplicate of all list in all sections
+ * make a duplicate of all list in all sections
* (but without the comment sublists)
*/
GapStoryBoard *
@@ -7472,7 +7602,7 @@ void
gap_story_enable_hidden_maskdefinitions(GapStoryBoard *stb_ptr)
{
GapStoryElem *stb_elem;
-
+
if (stb_ptr->mask_section == NULL)
{
return;
@@ -7516,7 +7646,7 @@ gap_story_duplicate_vtrack(GapStoryBoard *stb_ptr, gint32 in_vtrack)
* gap_story_duplicate_sel_only
* -----------------------------
*
- * make a filtered duplicate of the storyboard where the element lists
+ * make a filtered duplicate of the storyboard where the element lists
* contains only elements of the
* specified track in the active_section
* with the selected flag set.
@@ -7548,7 +7678,7 @@ gap_story_duplicate_sel_only(GapStoryBoard *stb_ptr, gint32 in_vtrack)
* gap_story_duplicate_one_elem_and_masks
* ---------------------------------------
*
- * make a filtered duplicate of the storyboard where the element lists
+ * make a filtered duplicate of the storyboard where the element lists
* contains only one element in the main section, that is specifed by story_id.
* and must be in the active_section
*
@@ -7576,7 +7706,7 @@ gap_story_duplicate_one_elem_and_masks(GapStoryBoard *stb_ptr
* gap_story_duplicate_one_elem
* ---------------------------------------
*
- * make a filtered duplicate of the storyboard where the element lists
+ * make a filtered duplicate of the storyboard where the element lists
* contains only one element in the main section, that is specifed by story_id.
* and must be in the active_section
*
@@ -7611,7 +7741,7 @@ gap_story_duplicate_one_elem(GapStoryBoard *stb_ptr
* and audio clips are ignored.
*
* typical usage at storyboard dialog startup,
- * to speed up videothumbnail creation.
+ * to speed up video thumbnail creation.
* (minimize frame seek times)
*
* NOTE: mask definitions (elements of the track GAP_STB_MASK_TRACK_NUMBER)
@@ -7672,7 +7802,7 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard *stb_dup, GapStoryBoard
stb_dup->unsaved_changes = TRUE;
}
-
+
dup_main_section = gap_story_find_section_by_name(stb_dup, NULL);
for(section = stb_ptr->stb_section; section != NULL; section = section->next)
@@ -7751,7 +7881,7 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard *stb_dup, GapStoryBoard
new_main_section =
gap_story_find_section_by_name(stb_new, NULL);
-
+
gap_story_list_append_elem_at_section(stb_new, stb_elem_dup, new_main_section);
/* insert before stb_elem_same */
@@ -7777,8 +7907,8 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard *stb_dup, GapStoryBoard
} /* end for section loop */
-
-
+
+
if(gap_debug)
@@ -7805,7 +7935,7 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard *stb_dup, GapStoryBoard
* (If the destination storyboard already has subsection(s) with the same
* section_name(s) as the source storyboard, the elements are appended
* at end.)
- *
+ *
*/
void
gap_story_copy_sub_sections(GapStoryBoard *stb_src, GapStoryBoard *stb_dst)
@@ -7835,7 +7965,7 @@ gap_story_copy_sub_sections(GapStoryBoard *stb_src, GapStoryBoard *stb_dst)
{
/* copy all available sub sections (except main and mask section)
*/
- dup_to_section =
+ dup_to_section =
gap_story_create_or_find_section_by_name(stb_dst, section->section_name);
/* copy tracks of the currently processed section */
@@ -7886,7 +8016,17 @@ gap_story_set_properties_like_sample_storyboard (GapStoryBoard *stb
{
stb->preferred_decoder = g_strdup(stb_sample->preferred_decoder);
}
-
+
+ if(stb->master_insert_alpha_format)
+ {
+ g_free(stb->master_insert_alpha_format);
+ stb->master_insert_alpha_format = NULL;
+ }
+ if(stb_sample->master_insert_alpha_format)
+ {
+ stb->master_insert_alpha_format = g_strdup(stb_sample->master_insert_alpha_format);
+ }
+
if(stb->master_insert_area_format)
{
g_free(stb->master_insert_area_format);
@@ -7896,7 +8036,7 @@ gap_story_set_properties_like_sample_storyboard (GapStoryBoard *stb
{
stb->master_insert_area_format = g_strdup(stb_sample->master_insert_area_format);
}
-
+
}
}
} /* end gap_story_set_properties_like_sample_storyboard */
@@ -7926,7 +8066,7 @@ gap_story_remove_sel_elems(GapStoryBoard *stb)
unsaved_changes_counter = 0;
stb_elem_prev = NULL;
-
+
for(stb_elem = section->stb_elem; stb_elem != NULL; stb_elem = stb_elem_next)
{
stb_elem_next = (GapStoryElem *)stb_elem->next;
@@ -8008,7 +8148,7 @@ gap_story_count_active_elements(GapStoryBoard *stb_ptr, gint32 in_track)
{
return (0);
}
-
+
for(stb_elem = stb_ptr->active_section->stb_elem; stb_elem != NULL; stb_elem = stb_elem->next)
{
if(stb_elem->track == in_track)
@@ -8049,7 +8189,7 @@ gap_story_fetch_nth_active_elem(GapStoryBoard *stb
{
return (NULL);
}
-
+
for(stb_elem = stb->active_section->stb_elem; stb_elem != NULL; stb_elem = stb_elem->next)
{
if(stb_elem->track == in_track)
@@ -8099,7 +8239,7 @@ gap_story_selection_all_set(GapStoryBoard *stb, gboolean sel_state)
* gap_story_selection_by_story_id
* ---------------------------------
* set the element with specified story_id to the specified selection state.
- * Notes:
+ * Notes:
* - searching for the element is done in all sections of the
* specified storyboard.
*
@@ -8219,7 +8359,7 @@ gap_story_set_aud_movie_min_max(GapStoryBoard *stb)
{
GapStoryElem *stb_element;
GapStoryElem *stb_elem;
-
+
if (stb->active_section == NULL)
{
return;
@@ -8786,7 +8926,7 @@ gap_story_get_default_attribute(gint att_typ_idx)
{
return (1.0); /* 1.0 indicates fully opaque, or no scaling */
}
- return (0.0); /* indicates centerd positioning */
+ return (0.0); /* indicates centerd positioning or rotate 0 degree */
} /* end gap_story_get_default_attribute */
@@ -8823,6 +8963,7 @@ gap_story_file_calculate_render_attributes(GapStoryCalcAttr *result_attr
, gboolean keep_proportions
, gboolean fit_width
, gboolean fit_height
+ , gdouble rotate
, gdouble opacity
, gdouble scale_x
, gdouble scale_y
@@ -8849,6 +8990,7 @@ gap_story_file_calculate_render_attributes(GapStoryCalcAttr *result_attr
result_height = -1;
l_tmp_width = frame_width;
l_tmp_height = frame_height;
+ result_attr->rotate = rotate;
if(keep_proportions)
{
@@ -8997,17 +9139,17 @@ gap_story_file_calculate_render_attributes(GapStoryCalcAttr *result_attr
* get the current video track
* for the specified section.
* Typically used in the storyboard dialog
- * when switching sections. (for implicite switch
+ * when switching sections. (for implicit switch
* to the video track that was active when the section
* was edited the last time)
- * Note that the mask section has only one implicte track with
+ * Note that the mask section has only one implicit track with
* GAP_STB_MASK_TRACK_NUMBER (0)
*/
gint32
gap_story_get_current_vtrack (GapStoryBoard *stb, GapStorySection *section)
{
gint32 current_vtrack;
-
+
if(section == stb->mask_section)
{
return (GAP_STB_MASK_TRACK_NUMBER);
@@ -9016,7 +9158,7 @@ gap_story_get_current_vtrack (GapStoryBoard *stb, GapStorySection *section)
section->current_vtrack = current_vtrack;
return (current_vtrack);
-
+
} /* end gap_story_get_current_vtrack */
@@ -9038,7 +9180,7 @@ gap_story_set_current_vtrack (GapStoryBoard *stb, GapStorySection *section
{
return;
}
-
+
if(section == stb->mask_section)
{
section->current_vtrack = GAP_STB_MASK_TRACK_NUMBER;
@@ -9083,10 +9225,10 @@ gap_story_set_current_vtrack (GapStoryBoard *stb, GapStorySection *section
* and the 1st element must describe mapped_frame_number == 1
* Mapping Example:
* the current vtrack has 5 clips
- * clip1 has 7 unselected frames
- * clip2 has 10 SELECTED frames
+ * clip1 has 7 unselected frames
+ * clip2 has 10 SELECTED frames
* clip3 has 4 unselected frames
- * clip4 has 5 SELECTED frames
+ * clip4 has 5 SELECTED frames
* clip5 has 4 unselected frames
*
* o .. unselected frame,
@@ -9116,12 +9258,12 @@ gap_story_get_mapped_master_frame_number(GapStoryFrameNumberMap *mapping
GapStoryFrameNumberMappingElem *map_elem;
gint32 mapped_nr;
gint32 l_frame_number;
-
+
if(mapping == NULL)
{
return (frame_number);
}
-
+
if (mapping->map_list == NULL)
{
return (frame_number);
@@ -9166,12 +9308,12 @@ p_story_add_selection_mapping_elem(GapStoryFrameNumberMap *mapping,
{
GapStoryFrameNumberMappingElem *new_map_elem;
GapStoryFrameNumberMappingElem *map_elem;
-
+
new_map_elem = g_new(GapStoryFrameNumberMappingElem, 1);
new_map_elem->next = NULL;
new_map_elem->orig_frame_number = orig_frame_number;
new_map_elem->mapped_frame_number = mapped_frame_number;
-
+
/* add new map element at and of the list */
for(map_elem = mapping->map_list; map_elem != NULL; map_elem = map_elem->next)
@@ -9189,7 +9331,7 @@ p_story_add_selection_mapping_elem(GapStoryFrameNumberMap *mapping,
{
mapping->map_list = new_map_elem;
}
-
+
} /* end p_story_add_selection_mapping_elem */
@@ -9265,9 +9407,9 @@ gap_story_create_new_mapping_from_selection_OLD(GapStorySection *active_section
*/
capture_maped_frame_number = TRUE;
rest_frames = stb_elem->nframes;
-
+
}
-
+
}
mapping->total_frames_selected = mapped_frame_number + rest_frames -1;
@@ -9293,20 +9435,20 @@ gap_story_create_new_mapping_from_selection_OLD(GapStorySection *active_section
*
* Example:
* Storyboard track witch videoclip sequence: a,b,(overlap 6),c,d,e
- *
+ *
* 1 2 5 6 8 11
* +-+-+-+-+-+-+-+ +-+-+-+-+
* | a=4 |b=3 | | e=4 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |c=2| d=7 |
* +-+-+-+-+-+-+-+-+-+
- *
+ *
* Selected: a,b,e total_frames_selected = (4 + 3 + 4) - (0) = 11
- *
+ *
* Selected: a,b,c total_frames_selected = (4 + 3 + 2) - (2) = 7
- *
+ *
* Selected: a,b,c,d total_frames_selected = (4 + 3 + 2 + 7) - ( 2 + 4) = 10
- *
+ *
* Selected: a,b,d total_frames_selected = (4 + 3 + 7) - (4) = 10
* in this case the 2 frames of clip 2 are not mapped (cause they are not selected)
* but due to overlapping those frames are involved at rendering of selected frames
@@ -9315,7 +9457,7 @@ gap_story_create_new_mapping_from_selection_OLD(GapStorySection *active_section
* but it reduces the configured overlap amount (from 6 to 4 frames).
* the first 4 frames of clip d overlap frames of clip a and clip b,
* therefore 4 frames are subtracted from the total_frames_selected sum.
- *
+ *
*/
GapStoryFrameNumberMap *
gap_story_create_new_mapping_from_selection(GapStorySection *active_section
@@ -9329,7 +9471,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
gint32 sum_overlap_sel;
gint32 curr_overlap;
gint32 expect_next;
-
+
gboolean capture_maped_frame_number;
if (active_section == NULL)
@@ -9346,7 +9488,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
sum_overlap_sel = 0;
curr_overlap = 0;
expect_next = 1;
-
+
orig_frame_number = 1;
capture_maped_frame_number = FALSE;
@@ -9375,17 +9517,17 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
{
curr_overlap += expect_next - next_orig_frame_number;
}
-
-
+
+
}
if( (stb_elem->track == vtrack ) && (gap_story_elem_is_video(stb_elem) == TRUE))
{
gint32 overlap_for_this_frame;
-
+
overlap_for_this_frame = MAX(0, MIN(curr_overlap, stb_elem->nframes));
curr_overlap -= overlap_for_this_frame;
-
+
if (stb_elem->selected)
{
orig_frame_number = gap_story_get_framenr_by_story_id(active_section
@@ -9403,7 +9545,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
expect_next = orig_frame_number + stb_elem->nframes; /* expected frame without overlapping */
}
}
-
+
}
mapping->total_frames_selected = sum_all_selected - sum_overlap_sel;
@@ -9417,7 +9559,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
{
GapStoryFrameNumberMap *old_mapping;
old_mapping = gap_story_create_new_mapping_from_selection_OLD(active_section, vtrack);
-
+
if (old_mapping->total_frames_selected != mapping->total_frames_selected)
{
printf("\nOLD MAPPING\n");
@@ -9460,7 +9602,7 @@ gap_story_debug_print_mapping(GapStoryFrameNumberMap *mapping)
}
}
printf("gap_story_debug_print_mapping END\n");
-
+
} /* end gap_story_debug_print_mapping */
@@ -9479,7 +9621,7 @@ gap_story_free_GapStoryVideoFileRef(GapStoryVideoFileRef *vref_list)
while(vref != NULL)
{
vref_next = vref->next;
-
+
if (vref->videofile != NULL)
{
g_free(vref->videofile);
@@ -9490,16 +9632,16 @@ gap_story_free_GapStoryVideoFileRef(GapStoryVideoFileRef *vref_list)
g_free(vref->userdata);
vref->userdata = NULL;
}
-
+
if (vref->preferred_decoder != NULL)
{
g_free(vref->preferred_decoder);
vref->preferred_decoder = NULL;
}
-
+
g_free(vref);
vref = vref_next;
-
+
}
} /* end gap_story_free_GapStoryVideoFileRef */
@@ -9515,7 +9657,7 @@ p_new_GapStoryVideoFileRef(const char *videofile
, gint32 max_ref_framenr)
{
GapStoryVideoFileRef *vref;
-
+
vref = g_new(GapStoryVideoFileRef, 1);
if(vref)
{
@@ -9525,12 +9667,12 @@ p_new_GapStoryVideoFileRef(const char *videofile
vref->preferred_decoder = NULL;
vref->seltrack = seltrack;
vref->max_ref_framenr = max_ref_framenr;
-
+
if (videofile != NULL)
{
vref->videofile = g_strdup(videofile);
}
-
+
if (preferred_decoder != NULL)
{
vref->preferred_decoder = g_strdup(preferred_decoder);
@@ -9561,14 +9703,14 @@ gap_story_find_vref_by_name_and_seltrack(GapStoryVideoFileRef *vref_list
}
return (NULL);
-
+
} /* end gap_story_find_vref_by_name_and_seltrack */
/* ---------------------------------
* gap_story_get_video_file_ref_list
* ---------------------------------
- * IN: stb The Storyboardfile
+ * IN: stb The Storyboardfile
* NOTE: this procedure operates on all sections of the
* specified storyboard.
* OUT:
@@ -9584,7 +9726,7 @@ gap_story_get_video_file_ref_list(GapStoryBoard *stb)
{
GapStoryVideoFileRef *vref_list;
GapStorySection *section;
-
+
vref_list = NULL;
for(section = stb->stb_section; section != NULL; section = section->next)
@@ -9595,7 +9737,7 @@ gap_story_get_video_file_ref_list(GapStoryBoard *stb)
if(stb_elem->record_type == GAP_STBREC_VID_MOVIE)
{
GapStoryVideoFileRef *vref;
-
+
vref = gap_story_find_vref_by_name_and_seltrack(vref_list
,stb_elem->orig_filename
,stb_elem->seltrack
@@ -9640,3 +9782,71 @@ gap_story_build_basename(const char *filename)
basename = g_filename_display_basename(filename);
return(basename);
} /* end gap_story_build_basename */
+
+
+
+/* ----------------------------------------------------
+ * gap_story_transform_rotate_layer
+ * ----------------------------------------------------
+ * rotate layer by the specified angle in degree
+ */
+void
+gap_story_transform_rotate_layer(gint32 image_id, gint32 layer_id, gdouble rotate)
+{
+ gint32 l_mask_id;
+ gint32 l_center_x;
+ gint32 l_center_y;
+ gdouble l_angle_rad;
+ gdouble l_angle_deg;
+
+ l_angle_deg = rotate;
+ while(l_angle_deg >= 360.0)
+ {
+ l_angle_deg -=360;
+ }
+ while(l_angle_deg <= -360.0)
+ {
+ l_angle_deg +=360;
+ }
+
+ if ((l_angle_deg < 0.05) && (l_angle_deg > -0.05))
+ {
+ /* ignore very small rotations */
+ return;
+ }
+ l_angle_rad = (l_angle_deg * G_PI) / 180.0;
+
+ /* check for layermask */
+ l_mask_id = gimp_layer_get_mask(layer_id);
+ if(l_mask_id >= 0)
+ {
+ /* apply the layermask
+ * some transitions (especially rotate) can't operate proper on
+ * layers with masks !
+ * (tests with gimp_rotate resulted in trashed images,
+ * even if the mask was rotated too)
+ */
+ gimp_layer_remove_mask (layer_id, GIMP_MASK_APPLY);
+ }
+
+ /* remove selection (if there is one)
+ * if there is a selection transitions (gimp_rotate)
+ * will create new layer and do not operate on l_cp_layer_id
+ */
+ gimp_selection_none(image_id);
+
+ l_center_x = gimp_drawable_width(layer_id) / 2.0;
+ l_center_y = gimp_drawable_height(layer_id) / 2.0;
+
+ /* have to rotate the layer (rotation also changes size as needed) */
+ gimp_drawable_transform_rotate_default(layer_id
+ , l_angle_rad
+ , TRUE /* auto_center */
+ , l_center_x
+ , l_center_y
+ , TRUE /* interpolation */
+ , FALSE /* clip_results */
+ );
+
+
+} /* end gap_story_transform_rotate_layer */
diff --git a/gap/gap_story_file.h b/gap/gap_story_file.h
index 3534641..5551e1a 100644
--- a/gap/gap_story_file.h
+++ b/gap/gap_story_file.h
@@ -1,6 +1,6 @@
/* gap_story_file.h
*
- * This module handles GAP storyboard file
+ * This module handles GAP storyboard file
* parsing of storyboard level1 files (load informations into a list)
* and (re)write storyboard files from the list (back to storyboard file)
*
@@ -36,15 +36,16 @@
#include "gap_story_syntax.h"
#include "gap_story_render_types.h"
-/* transition attribute types
+/* transition attribute types
* (values are used as index for look-up tables)
*/
-#define GAP_STB_ATT_TYPES_ARRAY_MAX 5
+#define GAP_STB_ATT_TYPES_ARRAY_MAX 6
#define GAP_STB_ATT_TYPE_OPACITY 0
#define GAP_STB_ATT_TYPE_MOVE_X 1
#define GAP_STB_ATT_TYPE_MOVE_Y 2
#define GAP_STB_ATT_TYPE_ZOOM_X 3
#define GAP_STB_ATT_TYPE_ZOOM_Y 4
+#define GAP_STB_ATT_TYPE_ROTATE 5
#define GAP_STB_MASK_SECTION_NAME "Masks"
@@ -57,11 +58,11 @@
typedef enum
{
GAP_STBREC_VID_SILENCE
- ,GAP_STBREC_VID_COLOR
- ,GAP_STBREC_VID_IMAGE
- ,GAP_STBREC_VID_ANIMIMAGE
- ,GAP_STBREC_VID_FRAMES
- ,GAP_STBREC_VID_MOVIE
+ ,GAP_STBREC_VID_COLOR
+ ,GAP_STBREC_VID_IMAGE
+ ,GAP_STBREC_VID_ANIMIMAGE
+ ,GAP_STBREC_VID_FRAMES
+ ,GAP_STBREC_VID_MOVIE
,GAP_STBREC_VID_COMMENT
,GAP_STBREC_VID_UNKNOWN
@@ -88,14 +89,14 @@
typedef enum
{
GAP_STB_PM_NORMAL
- ,GAP_STB_PM_PINGPONG
+ ,GAP_STB_PM_PINGPONG
} GapStoryVideoPlaymode;
typedef enum
{
GAP_STB_MASTER_TYPE_UNDEFINED
,GAP_STB_MASTER_TYPE_STORYBOARD
- ,GAP_STB_MASTER_TYPE_CLIPLIST
+ ,GAP_STB_MASTER_TYPE_CLIPLIST
} GapStoryMasterType;
@@ -112,13 +113,13 @@
GapStoryRecordType record_type;
GapStoryVideoPlaymode playmode;
gint32 track;
-
- char *orig_filename; /* full filename use for IMAGE and MOVIE Files
+
+ char *orig_filename; /* full filename use for IMAGE and MOVIE Files
* and SECTIONS (for section_name)
*/
char *orig_src_line; /* without \n, used to store header, comment and unknown lines */
-
- /* basename + ext are used for FRAME range elements only */
+
+ /* basename + ext are used for FRAME range elements only */
gchar *basename; /* path+filename (without number part and without extension */
gchar *ext; /* extenson ".xcf" ".jpg" ... including the dot */
gint32 seltrack; /* selected videotrack in a videofile (for GAP_FRN_MOVIE) */
@@ -130,6 +131,10 @@
* if track == GAP_STB_MASK_TRACK_NUMBER this atribute
* is the mandatory definition of the mask_name.
*/
+ char *colormask_file; /* optional reference to a colormask parameter file
+ * relevant for ancor mode GAP_MSK_ANCHOR_CLIPCOLOR
+ * where mask is applied as colormask
+ */
gdouble mask_stepsize;
GapStoryMaskAnchormode mask_anchor;
gboolean mask_disable;
@@ -138,11 +143,11 @@
gchar *filtermacro_file;
gint32 fmac_total_steps;
gint32 fmac_accel;
-
+
gint32 from_frame;
gint32 to_frame;
gint32 nloop; /* 1 play one time */
-
+
gint32 nframes; /* if playmode == normal
* then frames = nloop * (ABS(from_frame - to_frame) + 1);
* else frames = (nloop * 2 * ABS(from_frame - to_frame)) + 1;
@@ -160,7 +165,7 @@
gdouble color_green;
gdouble color_blue;
gdouble color_alpha;
-
+
/* members for attribute Record types */
gboolean att_keep_proportions;
gboolean att_fit_width;
@@ -188,7 +193,7 @@
gdouble aud_min_play_sec; /* for optimzed audio extract from videofiles */
gdouble aud_max_play_sec;
gdouble aud_framerate; /* framerate that is used to convert audio unit frame <-> secs */
-
+
struct GapStoryElem *comment;
struct GapStoryElem *next;
} GapStoryElem;
@@ -198,7 +203,7 @@
GapStoryElem *stb_elem;
gchar *section_name; /* null refers to the main section */
gint32 current_vtrack;
-
+
gint32 section_id; /* unique ID, NOT persistent */
gint32 version; /* numer of changes while editing, NOT persistent */
@@ -216,9 +221,9 @@
typedef struct GapStoryFrameNumberMappingElem {
gint32 mapped_frame_number;
gint32 orig_frame_number;
-
+
struct GapStoryFrameNumberMappingElem *next;
-
+
} GapStoryFrameNumberMappingElem;
@@ -251,7 +256,7 @@
gint32 layout_thumbsize;
gchar *preferred_decoder;
-
+
/* for error handling while parsing */
gchar *errtext;
gchar *errline;
@@ -278,11 +283,12 @@
gboolean unsaved_changes;
GapStoryEditSettings *edit_settings;
-
+
+ gchar *master_insert_alpha_format;
gchar *master_insert_area_format;
} GapStoryBoard;
-
+
typedef struct GapStoryLocateRet {
GapStoryElem *stb_elem;
gint32 ret_framenr;
@@ -296,7 +302,8 @@
gint32 x_offs;
gint32 y_offs;
gdouble opacity;
-
+ gdouble rotate;
+
gint32 visible_width;
gint32 visible_height;
} GapStoryCalcAttr;
@@ -308,7 +315,7 @@
gchar *preferred_decoder;
gint32 seltrack;
gint32 max_ref_framenr;
-
+
void *next;
} GapStoryVideoFileRef;
@@ -447,6 +454,7 @@ void gap_story_file_calculate_render_attributes(GapStoryCalcAttr
, gboolean keep_proportions
, gboolean fit_width
, gboolean fit_height
+ , gdouble rotate
, gdouble opacity
, gdouble scale_x
, gdouble scale_y
@@ -481,4 +489,6 @@ GapStoryVideoFileRef * p_new_GapStoryVideoFileRef(const char *videofile
GapStoryVideoFileRef * gap_story_get_video_file_ref_list(GapStoryBoard *stb);
char * gap_story_build_basename(const char *filename);
+void gap_story_transform_rotate_layer(gint32 image_id, gint32 layer_id, gdouble rotate);
+
#endif
diff --git a/gap/gap_story_main.h b/gap/gap_story_main.h
index a59a528..ee3a554 100644
--- a/gap/gap_story_main.h
+++ b/gap/gap_story_main.h
@@ -65,8 +65,8 @@ typedef gpointer t_GVA_Handle;
typedef enum
{
GAP_STB_EDMO_SEQUENCE_NUMBER
- ,GAP_STB_EDMO_FRAME_NUMBER
- ,GAP_STB_EDMO_TIMECODE
+ ,GAP_STB_EDMO_FRAME_NUMBER
+ ,GAP_STB_EDMO_TIMECODE
} GapStoryElemDisplayMode;
typedef enum
@@ -85,10 +85,10 @@ typedef enum
typedef enum
{
GAP_VTHUMB_PREFETCH_NOT_ACTIVE
- ,GAP_VTHUMB_PREFETCH_IN_PROGRESS
- ,GAP_VTHUMB_PREFETCH_RESTART_REQUEST
- ,GAP_VTHUMB_PREFETCH_CANCEL_REQUEST
- ,GAP_VTHUMB_PREFETCH_PENDING
+ ,GAP_VTHUMB_PREFETCH_IN_PROGRESS
+ ,GAP_VTHUMB_PREFETCH_RESTART_REQUEST
+ ,GAP_VTHUMB_PREFETCH_CANCEL_REQUEST
+ ,GAP_VTHUMB_PREFETCH_PENDING
} GapVThumbPrefetchProgressMode;
/* video list element describes base resource that has
@@ -96,7 +96,7 @@ typedef enum
* per resource.
*
* currently supported video resource types are defined via GapStoryVthumbEnum.
- *
+ *
* The storyboard dialog has a global video thumbnail list that keeps
* all non-persistent thumbnails in memory, connected to the video list elem
* by theis video_id.
@@ -113,18 +113,18 @@ typedef struct GapStoryVTResurceElem {
gchar *video_filename; /* filename of the video or anim image,
* or section_name
*/
- gint32 seltrack; /* not relevant for
- * GAP_STB_VLIST_SECTION and
+ gint32 seltrack; /* not relevant for
+ * GAP_STB_VLIST_SECTION and
* GAP_STB_VLIST_ANIM_IMAGE
*/
gint32 video_id; /* uniqie video resource id */
gint32 total_frames; /* total frames of the video resource */
-
+
void *next;
} GapStoryVTResurceElem;
-/* video thumbnail elemnt is used in the storyboard dialog
+/* video thumbnail elemnt is used in the storyboard dialog
* (and clip properties dialog)
*/
typedef struct GapVThumbElem {
@@ -134,7 +134,7 @@ typedef struct GapVThumbElem {
gint32 th_bpp;
gint32 framenr;
gint32 video_id;
-
+
void *next;
} GapVThumbElem;
@@ -153,7 +153,7 @@ typedef struct GapStbPropWidget /* nickname: pw */
gint32 go_timertag;
gboolean scene_detection_busy;
gboolean close_flag;
-
+
gdouble delace_threshold;
gint32 delace_mode;
gint32 flip_request;
@@ -174,6 +174,10 @@ typedef struct GapStbPropWidget /* nickname: pw */
GtkWidget *comment_entry;
GtkWidget *pw_fmac_filesel;
GtkWidget *fmac_entry;
+ GtkWidget *pw_colormask_file_filesel;
+ GtkWidget *colormask_file_entry;
+ GtkWidget *colormask_file_filesel_button;
+ GtkWidget *colormask_file_label;
GtkObject *pw_spinbutton_from_adj;
GtkObject *pw_spinbutton_to_adj;
GtkObject *pw_spinbutton_loops_adj;
@@ -191,14 +195,14 @@ typedef struct GapStbPropWidget /* nickname: pw */
GtkWidget *pw_mask_definition_name_label;
gboolean is_mask_definition;
GtkWidget *pw_mask_name_entry; /* relevant to enter mask definition */
- GtkWidget *mask_name_combo; /* selected mask_name reference */
+ GtkWidget *mask_name_combo; /* selected mask_name reference */
gint32 mask_name_combo_elem_count;
GtkWidget *mask_anchor_label;
GtkWidget *pingpong_label;
GtkWidget *pw_mask_enable_toggle;
- GtkWidget *pw_mask_anchor_radio_button_arr[2];
+ GtkWidget *pw_mask_anchor_radio_button_arr[3];
GtkObject *pw_spinbutton_mask_stepsize_adj;
-
+
/* for filermacro handling */
GtkObject *pw_spinbutton_fmac_accel_adj;
GtkWidget *pw_spinbutton_fmac_accel;
@@ -207,7 +211,7 @@ typedef struct GapStbPropWidget /* nickname: pw */
GtkWidget *pw_label_alternate_fmac2;
GtkWidget *pw_label_alternate_fmac_file;
GtkWidget *pw_hbox_fmac2;
-
+
struct GapStbPropWidget *next;
} GapStbPropWidget;
@@ -220,7 +224,7 @@ typedef struct GapStbAttrLayerInfo /* nickname: linfo */
gint32 layer_local_framenr;
gint32 layer_seltrack;
gchar *layer_filename;
-
+
} GapStbAttrLayerInfo;
@@ -231,7 +235,7 @@ typedef struct GapStbSecpropWidget /* nickname: spw */
void *sgpp; /* never g_free this one ! */
void *tabw; /* never g_free this one ! (pointer to parent GapStbTabWidgets) */
-
+
GapPView *typ_icon_pv_ptr; /* for display of the section type */
GtkWidget *spw_prop_dialog;
GtkWidget *spw_section_name_entry;
@@ -252,13 +256,13 @@ typedef struct GapStbAttGfx
gint32 orig_layer_id; /* invisible frame at original image size */
gint32 opre_layer_id; /* invisible prefetch frame at original image size */
-
+
gint32 deco_layer_id; /* decor layer (always on top of stack) */
gint32 curr_layer_id; /* copy of orig_layer_id, after transformations */
gint32 pref_layer_id; /* copy of opre_layer_id, after transformations (visible if overlapping is active) */
gint32 base_layer_id; /* decor layer (always BG) */
gboolean auto_update;
-
+
/* information about the orig layer */
GapStbAttrLayerInfo orig_info;
GapStbAttrLayerInfo opre_info;
@@ -302,14 +306,14 @@ typedef struct GapStbAttrWidget /* nickname: attw */
gint32 go_timertag;
gboolean timer_full_update_request;
gboolean close_flag;
-
+
GtkWidget *attw_prop_dialog;
GtkWidget *master_table;
-
+
GtkWidget *fit_width_toggle;
GtkWidget *fit_height_toggle;
GtkWidget *keep_proportions_toggle;
-
+
GapStbAttRow att_rows[GAP_STB_ATT_TYPES_ARRAY_MAX];
GapStbAttGfx gfx_tab[GAP_STB_ATT_GFX_ARRAY_MAX]; /* 0 .. from, 1 .. to */
@@ -339,7 +343,7 @@ typedef struct GapStbFrameWidget /* nickname: fw */
gint32 seq_nr; /* position in the storyboard list (starting at 1) */
char *frame_filename;
-
+
void *sgpp; /* never g_free this one ! */
void *tabw; /* never g_free this one ! (pointer to parent GapStbTabWidgets) */
} GapStbFrameWidget;
@@ -401,7 +405,7 @@ typedef struct GapStbTabWidgets /* nickname: tabw */
GapStbAttrWidget *attw;
GapStoryElemDisplayMode edmode;
- GapStoryUndoElem *undo_stack_list;
+ GapStoryUndoElem *undo_stack_list;
GapStoryUndoElem *undo_stack_ptr;
gdouble undo_stack_group_counter;
@@ -428,7 +432,7 @@ typedef struct GapStbMainGlobalParams /* nickname: sgpp */
/* GUI widget pointers */
GapStbTabWidgets *stb_widgets;
GapStbTabWidgets *cll_widgets;
-
+
GapStoryVTResurceElem *video_list;
GapVThumbElem *vthumb_list;
t_GVA_Handle *gvahand;
@@ -460,8 +464,8 @@ typedef struct GapStbMainGlobalParams /* nickname: sgpp */
gint32 stb_thumbsize;
/* end layout values */
- GtkWidget *shell_window;
- GtkWidget *player_frame;
+ GtkWidget *shell_window;
+ GtkWidget *player_frame;
GtkWidget *menu_item_win_vthumbs;
@@ -490,7 +494,7 @@ typedef struct GapStbMainGlobalParams /* nickname: sgpp */
GtkWidget *menu_item_cll_undo;
GtkWidget *menu_item_cll_redo;
GtkWidget *menu_item_cll_close;
-
+
GdkPixbuf *dnd_pixbuf;
} GapStbMainGlobalParams;
diff --git a/gap/gap_story_properties.c b/gap/gap_story_properties.c
index 0d032e1..0a2a5ae 100644
--- a/gap/gap_story_properties.c
+++ b/gap/gap_story_properties.c
@@ -149,6 +149,7 @@ static void p_pw_mask_name_reference_update(GapStbPropWidget *pw, const char
static void p_pw_mask_name_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
static void p_pw_nullable_widget_set_sensitive(GtkWidget *widget, gboolean sensitive, gboolean show);
static void p_pw_nullable_gimp_scale_entry_set_sensitive(GtkObject *adj, gboolean sensitive, gboolean show);
+static void p_pw_set_colormask_param_sensitivity (GapStbPropWidget *pw);
static void p_pw_set_mask_name_dependent_widget_sensitivity(GapStbPropWidget *pw, gint active_index);
static void p_pw_set_strings_for_mask_name_combo(GapStbPropWidget *pw);
static void p_pw_mask_name_combo_changed_cb( GtkWidget *widget, GapStbPropWidget *pw);
@@ -157,10 +158,14 @@ static void p_pw_filename_changed(const char *filename, GapStbPropWidget *pw
static void p_filesel_pw_ok_cb (GtkWidget *widget, GapStbPropWidget *pw);
static void p_filesel_pw_close_cb ( GtkWidget *widget, GapStbPropWidget *pw);
static void p_pw_filesel_button_cb ( GtkWidget *w, GapStbPropWidget *pw);
+static void p_pw_colormask_file_filesel_pw_ok_cb (GtkWidget *widget, GapStbPropWidget *pw);
+static void p_pw_colormask_file_filesel_pw_close_cb ( GtkWidget *widget, GapStbPropWidget *pw);
+static void p_pw_colormask_file_filesel_button_cb ( GtkWidget *w, GapStbPropWidget *pw);
static void p_pw_fmac_filesel_pw_ok_cb (GtkWidget *widget, GapStbPropWidget *pw);
static void p_pw_fmac_filesel_pw_close_cb ( GtkWidget *widget, GapStbPropWidget *pw);
static void p_pw_fmac_filesel_button_cb ( GtkWidget *w, GapStbPropWidget *pw);
static void p_pw_comment_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
+static void p_pw_colormask_file_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
static void p_pw_fmac_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
static void p_pw_update_info_labels_and_cliptype_senstivity(GapStbPropWidget *pw);
static void p_pw_update_framenr_labels(GapStbPropWidget *pw, gint32 framenr);
@@ -306,7 +311,7 @@ p_pw_prop_reset_all(GapStbPropWidget *pw)
, TRUE);
}
- idx = CLAMP((gint32)pw->stb_elem_refptr->mask_anchor, 0, (2 -1));
+ idx = CLAMP((gint32)pw->stb_elem_refptr->mask_anchor, 0, (3 -1));
if(pw->pw_mask_anchor_radio_button_arr[idx])
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pw->pw_mask_anchor_radio_button_arr[idx])
@@ -706,7 +711,7 @@ p_pw_auto_scene_split(GapStbPropWidget *pw, gboolean all_scenes)
&& (sgpp->auto_vthumb == FALSE))
{
g_message(_("Scene detection depends on video thumbnails. "
- "Please enable videothumbnails (in the Windows Menu)"));
+ "Please enable video thumbnails (in the Windows Menu)"));
return;
}
@@ -1282,7 +1287,7 @@ p_pw_timer_go_job(GapStbPropWidget *pw)
* from_frame number at this point. (dont know exactly why)
* the simple workaround is just render twice
* and now it seems to work fine.
- * (rendering twice should not be too slow, since the requested videothumbnails
+ * (rendering twice should not be too slow, since the requested video thumbnails
* are now cached in memory)
*/
p_pv_pview_render_immediate(pw, pw->stb_elem_refptr, pw->pv_ptr);
@@ -1334,7 +1339,7 @@ p_pv_pview_render (GapStbPropWidget *pw)
}
pw->go_job_framenr = pw->stb_elem_refptr->from_frame;
- /* videothumb is rendered deferred (16 millisec) via timer */
+ /* video thumb is rendered deferred (16 millisec) via timer */
if(pw->go_timertag < 0)
{
pw->go_timertag = (gint32) g_timeout_add(16, (GtkFunction)p_pw_timer_go_job, pw);
@@ -1995,6 +2000,25 @@ p_pw_nullable_gimp_scale_entry_set_sensitive(GtkObject *adj, gboolean sensitive,
}
+/* ------------------------------------
+ * p_pw_set_colormask_param_sensitivity
+ * ------------------------------------
+ */
+static void
+p_pw_set_colormask_param_sensitivity (GapStbPropWidget *pw)
+{
+ gint active_index;
+
+ if(pw == NULL) { return; }
+ if(pw->mask_name_combo == NULL) {return;}
+ if(pw->stb_elem_refptr == NULL) {return;}
+
+ active_index = gtk_combo_box_get_active(GTK_COMBO_BOX (pw->mask_name_combo));
+ p_pw_set_mask_name_dependent_widget_sensitivity(pw, active_index);
+
+
+} /* end p_pw_set_colormask_param_sensitivity */
+
/* ------------------------------------------------
* p_pw_set_mask_name_dependent_widget_sensitivity
* ------------------------------------------------
@@ -2017,13 +2041,22 @@ p_pw_set_mask_name_dependent_widget_sensitivity(GapStbPropWidget *pw, gint activ
p_pw_nullable_widget_set_sensitive(pw->pw_mask_enable_toggle, l_sensitive, l_show);
p_pw_nullable_widget_set_sensitive(pw->pw_mask_anchor_radio_button_arr[0], l_sensitive, l_show);
p_pw_nullable_widget_set_sensitive(pw->pw_mask_anchor_radio_button_arr[1], l_sensitive, l_show);
-
+ p_pw_nullable_widget_set_sensitive(pw->pw_mask_anchor_radio_button_arr[2], l_sensitive, l_show);
+
p_pw_nullable_gimp_scale_entry_set_sensitive(pw->pw_spinbutton_mask_stepsize_adj
, l_sensitive
, l_show
);
-
-} /* end p_pw_set_mask_name_dependent_widget_sensitivity */
+
+ if(pw->mask_anchor != GAP_MSK_ANCHOR_XCOLOR)
+ {
+ l_sensitive = FALSE;
+ }
+
+ p_pw_nullable_widget_set_sensitive(pw->colormask_file_label, l_sensitive, l_show);
+ p_pw_nullable_widget_set_sensitive(pw->colormask_file_entry, l_sensitive, l_show);
+ p_pw_nullable_widget_set_sensitive(pw->colormask_file_filesel_button, l_sensitive, l_show);
+ } /* end p_pw_set_mask_name_dependent_widget_sensitivity */
/* ------------------------------------
* p_pw_set_strings_for_mask_name_combo
@@ -2380,6 +2413,105 @@ p_pw_filesel_button_cb ( GtkWidget *w
/* ==================================================== END FILESEL stuff ====== */
+/* ==================================================== START COLORMASK FILESEL stuff ====== */
+/* ------------------------------------
+ * p_pw_colormask_file_filesel_pw_ok_cb
+ * -----------------------------------
+ */
+static void
+p_pw_colormask_file_filesel_pw_ok_cb (GtkWidget *widget
+ ,GapStbPropWidget *pw)
+{
+ const gchar *colormask_file_name;
+ gchar *dup_colormask_file_name;
+
+ if(pw == NULL) return;
+ if(pw->pw_colormask_file_filesel == NULL) return;
+
+ dup_colormask_file_name = NULL;
+ colormask_file_name = gtk_file_selection_get_filename (GTK_FILE_SELECTION (pw->pw_colormask_file_filesel));
+ if(colormask_file_name)
+ {
+ dup_colormask_file_name = g_strdup(colormask_file_name);
+ }
+
+ gtk_widget_destroy(GTK_WIDGET(pw->pw_colormask_file_filesel));
+
+ if(dup_colormask_file_name)
+ {
+ gtk_entry_set_text(GTK_ENTRY(pw->colormask_file_entry), dup_colormask_file_name);
+ g_free(dup_colormask_file_name);
+ }
+
+ pw->pw_colormask_file_filesel = NULL;
+} /* end p_pw_colormask_file_filesel_pw_ok_cb */
+
+
+/* ---------------------------------------
+ * p_pw_colormask_file_filesel_pw_close_cb
+ * ---------------------------------------
+ */
+static void
+p_pw_colormask_file_filesel_pw_close_cb ( GtkWidget *widget
+ , GapStbPropWidget *pw)
+{
+ if(pw->pw_colormask_file_filesel == NULL) return;
+
+ gtk_widget_destroy(GTK_WIDGET(pw->pw_colormask_file_filesel));
+ pw->pw_colormask_file_filesel = NULL; /* indicate that filesel is closed */
+
+} /* end p_pw_colormask_file_filesel_pw_close_cb */
+
+
+
+/* -------------------------------------
+ * p_pw_colormask_file_filesel_button_cb
+ * -------------------------------------
+ */
+static void
+p_pw_colormask_file_filesel_button_cb ( GtkWidget *w
+ , GapStbPropWidget *pw)
+{
+ GtkWidget *filesel = NULL;
+
+ if(pw->scene_detection_busy)
+ {
+ return;
+ }
+
+ if(pw->pw_colormask_file_filesel != NULL)
+ {
+ gtk_window_present(GTK_WINDOW(pw->pw_colormask_file_filesel));
+ return; /* filesel is already open */
+ }
+ if(pw->stb_elem_refptr == NULL) { return; }
+
+ filesel = gtk_file_selection_new ( _("Set Colormark Parmeter Filename"));
+ pw->pw_colormask_file_filesel = filesel;
+
+ gtk_window_set_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE);
+ g_signal_connect (GTK_FILE_SELECTION (filesel)->ok_button,
+ "clicked", G_CALLBACK (p_pw_colormask_file_filesel_pw_ok_cb),
+ pw);
+ g_signal_connect (GTK_FILE_SELECTION (filesel)->cancel_button,
+ "clicked", G_CALLBACK (p_pw_colormask_file_filesel_pw_close_cb),
+ pw);
+ g_signal_connect (filesel, "destroy",
+ G_CALLBACK (p_pw_colormask_file_filesel_pw_close_cb),
+ pw);
+
+
+ if(pw->stb_elem_refptr->colormask_file)
+ {
+ gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel),
+ pw->stb_elem_refptr->colormask_file);
+ }
+ gtk_widget_show (filesel);
+
+} /* end p_pw_colormask_file_filesel_button_cb */
+
+
+/* ==================================================== END COLORMASK FILESEL stuff ====== */
/* ==================================================== START FMAC FILESEL stuff ====== */
/* --------------------------------
@@ -2517,6 +2649,29 @@ p_pw_comment_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw)
}
} /* end p_pw_comment_entry_update_cb */
+/* -----------------------------------
+ * p_pw_colormask_file_entry_update_cb
+ * -----------------------------------
+ */
+static void
+p_pw_colormask_file_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw)
+{
+ if(pw == NULL) { return; }
+ if(pw->stb_elem_refptr == NULL) { return; }
+ if(pw->stb_refptr)
+ {
+ p_pw_push_undo_and_set_unsaved_changes(pw);
+ }
+
+ if(pw->stb_elem_refptr->colormask_file)
+ {
+ g_free(pw->stb_elem_refptr->colormask_file);
+ }
+
+ pw->stb_elem_refptr->colormask_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
+
+} /* end p_pw_colormask_file_entry_update_cb */
+
/* ----------------------------
* p_pw_fmac_entry_update_cb
@@ -2825,6 +2980,7 @@ p_radio_mask_anchor_clip_callback(GtkWidget *widget, GapStbPropWidget *pw)
{
p_pw_push_undo_and_set_unsaved_changes(pw);
pw->stb_elem_refptr->mask_anchor = pw->mask_anchor;
+ p_pw_set_colormask_param_sensitivity(pw);
}
}
} /* end p_radio_mask_anchor_clip_callback */
@@ -2844,10 +3000,30 @@ p_radio_mask_anchor_master_callback(GtkWidget *widget, GapStbPropWidget *pw)
{
p_pw_push_undo_and_set_unsaved_changes(pw);
pw->stb_elem_refptr->mask_anchor = pw->mask_anchor;
+ p_pw_set_colormask_param_sensitivity(pw);
}
}
} /* end p_radio_mask_anchor_master_callback */
+/* -----------------------------------
+ * p_radio_mask_anchor_xcolor_callback
+ * -----------------------------------
+ */
+static void
+p_radio_mask_anchor_xcolor_callback(GtkWidget *widget, GapStbPropWidget *pw)
+{
+ if((pw) && (GTK_TOGGLE_BUTTON (widget)->active))
+ {
+ pw->mask_anchor = GAP_MSK_ANCHOR_XCOLOR;
+ if(pw->stb_elem_refptr)
+ {
+ p_pw_push_undo_and_set_unsaved_changes(pw);
+ pw->stb_elem_refptr->mask_anchor = pw->mask_anchor;
+ p_pw_set_colormask_param_sensitivity(pw);
+ }
+ }
+} /* end p_radio_mask_anchor_xcolor_callback */
+
/* ---------------------------------
* p_radio_delace_none_callback
@@ -3150,10 +3326,10 @@ p_radio_flip_both_callback(GtkWidget *widget, GapStbPropWidget *pw)
*
* The triggered action is to create (or replace) the mask_name reference
* of the current property widget.
- * If the droped element was not a mask definition, then implicite
+ * If the dropped element was not a mask definition, then implicitly
* add it to the mask definitions.
*
- * NOTE: mask definition property windows can NOT act as drop destionation.
+ * NOTE: mask definition property windows can NOT act as drop destination.
*/
static void
on_clip_elements_dropped_as_mask_ref (GtkWidget *widget,
@@ -3252,8 +3428,8 @@ on_clip_elements_dropped_as_mask_ref (GtkWidget *widget,
GapStoryElem *stb_elem_dup;
mask_name_new = gap_story_generate_unique_maskname(pw->stb_refptr);
- /* implicite create a new mask definition from the dropped
- * clip and change properties of current pw to refere
+ /* implicitly create a new mask definition from the dropped
+ * clip and change properties of current pw to refer
* to the newly created mask definition
*/
stb_elem_dup = gap_story_elem_duplicate(fw_drop->stb_elem_refptr);
@@ -3303,7 +3479,7 @@ on_clip_elements_dropped_as_mask_ref (GtkWidget *widget,
* accept dropping of application internal storyboard elements
* to add (or replace) the current mask_name (only for mask references)
*
- * if the dropped element is NOT a mask definition, then implicite
+ * if the dropped element is NOT a mask definition, then implicitly
* add it to the mask definitions.
*/
static void
@@ -3345,13 +3521,13 @@ p_pw_dialog_init_dnd(GapStbPropWidget *pw)
* -------------------------------
* - set sensitivity for the filtermacro steps spinbutton.
* It is sensitive if both filtermacro_file and an
- * implicite alternative filtermacro_file (MACRO2) are existent and valid.
+ * implicit alternative filtermacro_file (MACRO2) are existent and valid.
* In this case filter apply with varying values can be used
* by entering a value > 1 into the filtermacro steps spinbutton.
*
* - The presence of the alternative filtermacro_file is also
* displayed in the label pw_label_alternate_fmac_file
- * via shortened version of the alternat filtermacro filename.
+ * via shortened version of the alternate filtermacro filename.
* - This label is set to space if no valid alternate filtermacro_file
* is available. This label also indicates if MACRO2 is active
* or not active by adding Suffix "(ON)" or "(OFF)"
@@ -3680,6 +3856,10 @@ p_pw_clear_widgets(GapStbPropWidget *pw)
pw->comment_entry = NULL;
pw->pw_fmac_filesel = NULL;
pw->fmac_entry = NULL;
+ pw->pw_colormask_file_filesel = NULL;
+ pw->colormask_file_filesel_button = NULL;
+ pw->colormask_file_entry = NULL;
+ pw->colormask_file_label = NULL;
pw->pw_spinbutton_from_adj = NULL;
pw->pw_spinbutton_to_adj = NULL;
pw->pw_spinbutton_loops_adj = NULL;
@@ -3699,6 +3879,8 @@ p_pw_clear_widgets(GapStbPropWidget *pw)
pw->pw_mask_enable_toggle = NULL;
pw->pw_mask_anchor_radio_button_arr[0] = NULL;
pw->pw_mask_anchor_radio_button_arr[1] = NULL;
+ pw->pw_mask_anchor_radio_button_arr[2] = NULL;
+
pw->pw_spinbutton_mask_stepsize_adj = NULL;
pw->pw_spinbutton_fmac_steps = NULL;
@@ -4499,7 +4681,7 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
radio_table = gtk_table_new (1, 2, FALSE);
l_idx = 0;
- /* radio button mask_anchor None */
+ /* radio button mask_anchor Clip */
radio_button = gtk_radio_button_new_with_label ( radio_group, _("Clip") );
radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1
@@ -4517,7 +4699,7 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
pw);
l_idx++;
- /* radio button mask_anchor odd */
+ /* radio button mask_anchor Master */
radio_button = gtk_radio_button_new_with_label ( radio_group, _("Master") );
radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1
@@ -4534,12 +4716,82 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
G_CALLBACK (p_radio_mask_anchor_master_callback),
pw);
+ l_idx++;
+ /* radio button mask_anchor ColormaskClip */
+ radio_button = gtk_radio_button_new_with_label ( radio_group, _("ClipColormask") );
+ radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
+ gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1
+ , 0, 1, 0, 0, 0, 0);
+
+ pw->pw_mask_anchor_radio_button_arr[2] = radio_button;
+ l_radio_pressed = (pw->mask_anchor == GAP_MSK_ANCHOR_XCOLOR);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button),
+ l_radio_pressed);
+ gimp_help_set_help_data(radio_button, _("Apply as colormask to clip at clip position in clip size"), NULL);
+
+ gtk_widget_show (radio_button);
+ g_signal_connect ( G_OBJECT (radio_button), "toggled",
+ G_CALLBACK (p_radio_mask_anchor_xcolor_callback),
+ pw);
+
+
gtk_widget_show (radio_table);
gtk_table_attach_defaults (GTK_TABLE(table_ptr), radio_table, 1, 2, row, row+1);
}
+ row++;
+
+ /* the colormask parameter file label */
+ label = gtk_label_new (_("Mask Params:"));
+ pw->colormask_file_label = label;
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+ gtk_widget_show (label);
+
+
+
+ /* colormask parameter file entry and filesel invoker button */
+ {
+ GtkWidget *hbox_colormask_file;
+
+ hbox_colormask_file = gtk_hbox_new (FALSE, 1);
+ gtk_widget_show (hbox_colormask_file);
+ gtk_table_attach_defaults (GTK_TABLE(table), hbox_colormask_file, 1, 2, row, row+1);
+
+ /* the filtermacro entry */
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request(entry, PW_ENTRY_WIDTH, -1);
+ if(pw->stb_elem_refptr)
+ {
+ if(pw->stb_elem_refptr->colormask_file)
+ {
+ gtk_entry_set_text(GTK_ENTRY(entry), pw->stb_elem_refptr->colormask_file);
+ }
+ }
+
+ gtk_box_pack_start (GTK_BOX (hbox_colormask_file), entry, TRUE, TRUE, 1);
+
+ g_signal_connect(G_OBJECT(entry), "changed",
+ G_CALLBACK(p_pw_colormask_file_entry_update_cb),
+ pw);
+ gtk_widget_show (entry);
+ gimp_help_set_help_data (entry, _("parameter file for the colormask filter")
+ , NULL);
+ pw->colormask_file_entry = entry;
+
+
+ /* the filesel invoker button */
+ button = gtk_button_new_with_label ("...");
+ pw->colormask_file_filesel_button = button;
+ gtk_box_pack_start (GTK_BOX (hbox_colormask_file), button, FALSE, FALSE, 1);
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(p_pw_colormask_file_filesel_button_cb),
+ pw);
+ gtk_widget_show (button);
+ }
+
if(!pw->is_mask_definition)
{
row++;
@@ -4622,7 +4874,7 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
gtk_widget_show (entry);
gimp_help_set_help_data (entry, _("filter macro to be performed when frames "
"of this clips are rendered. "
- "A 2nd macrofile is implicite referenced by naming convetion "
+ "A 2nd macrofile is implicitly referenced by naming convention "
"via the keyword .VARYING (as suffix or before the extension)")
, NULL);
pw->fmac_entry = entry;
@@ -4875,7 +5127,7 @@ gap_story_stb_elem_properties_dialog ( GapStbTabWidgets *tabw
{
pw->pw_flip_request_radio_button_arr[idx] = NULL;
}
- for(idx=0; idx < 2; idx++)
+ for(idx=0; idx < sizeof(pw->pw_mask_anchor_radio_button_arr); idx++)
{
pw->pw_mask_anchor_radio_button_arr[idx] = NULL;
}
diff --git a/gap/gap_story_render_audio.c b/gap/gap_story_render_audio.c
index efad98d..d8220bd 100644
--- a/gap/gap_story_render_audio.c
+++ b/gap/gap_story_render_audio.c
@@ -356,7 +356,7 @@ p_find_min_max_aud_tracknumbers(GapStoryRenderAudioRangeElem *aud_list
/* ---------------------------------
* p_get_audio_sample
* ---------------------------------
- * - scann the aud_list to findout
+ * - scan the aud_list to findout
* audiofile that is played at desired track
* at desired sample_index position.
* - return the sample (amplitude) at desired sample_index position
diff --git a/gap/gap_story_render_lossless.c b/gap/gap_story_render_lossless.c
index d999ac5..25f07b8 100644
--- a/gap/gap_story_render_lossless.c
+++ b/gap/gap_story_render_lossless.c
@@ -344,6 +344,7 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
gint32 l_track_max;
gchar *l_framename;
gchar *l_videofile;
+ gdouble l_rotate;
gdouble l_opacity;
gdouble l_scale_x;
gdouble l_scale_y;
@@ -395,6 +396,7 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
, &l_green_f
, &l_blue_f
, &l_alpha_f
+ , &l_rotate
, &l_opacity /* output opacity 0.0 upto 1.0 */
, &l_scale_x /* output 0.0 upto 10.0 where 1.0 is 1:1 */
, &l_scale_y /* output 0.0 upto 10.0 where 1.0 is 1:1 */
@@ -425,6 +427,7 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
{
/* check for transformations */
if((l_opacity == 1.0)
+ && (l_rotate == 0.0)
&& (l_scale_x == 1.0)
&& (l_scale_y == 1.0)
&& (l_move_x == 0.0)
diff --git a/gap/gap_story_render_processor.c b/gap/gap_story_render_processor.c
index 05b02c0..c7f9989 100644
--- a/gap/gap_story_render_processor.c
+++ b/gap/gap_story_render_processor.c
@@ -88,6 +88,7 @@ typedef struct GapStbFetchData { /* nick: gfd */
gint32 layer_id;
gchar *framename;
+ gdouble rotate;
gdouble opacity;
gdouble scale_x;
gdouble scale_y;
@@ -168,6 +169,7 @@ static char * p_fetch_framename (GapStoryRenderFrameRangeElem *frn_list
, gdouble *green_f
, gdouble *blue_f
, gdouble *alpha_f
+ , gdouble *rotate /* output rotate in degree */
, gdouble *opacity /* output opacity 0.0 upto 1.0 */
, gdouble *scale_x /* output 0.0 upto 10.0 where 1.0 is 1:1 */
, gdouble *scale_y /* output 0.0 upto 10.0 where 1.0 is 1:1 */
@@ -200,6 +202,7 @@ static GapStoryRenderFrameRangeElem * p_new_framerange_element(
,gboolean mask_disable
,gint32 fmac_total_steps
,gint32 fmac_accel
+ ,const char *colormask_file /* IN: NULL, or name of the colormask parameter file */
);
static void p_add_frn_list(GapStoryRenderVidHandle *vidhand, GapStoryRenderFrameRangeElem *frn_elem);
static void p_step_all_vtrack_attributes(gint32 track, gint32 frames_to_handle
@@ -273,12 +276,14 @@ static gint32 p_mask_fetcher(GapStoryRenderVidHandle *vidhand
, gint32 mask_height
, gint32 *layer_id /* OUT: Id of the only layer in the composite image */
, gboolean *was_last_maskframe /* OUT: true if this was the last maskframe */
+ , gboolean makeGrayFlattened /* IN true flatten and convert to GRAY, false keep color and alpha channel */
);
static void p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
, GapStoryRenderFrameRangeElem *frn_elem
, gint32 local_stepcount
, gint32 image_id
, gint32 layer_id
+ , GapStoryMaskAnchormode mask_anchor
);
static GapStoryRenderVidHandle * p_open_video_handle_private( gboolean ignore_audio
@@ -321,6 +326,7 @@ static gint32 p_transform_and_add_layer( gint32 comp_image_id
, gboolean keep_proportions
, gboolean fit_width
, gboolean fit_height
+ , gdouble rotate /* rotation in degree */
, gdouble opacity /* 0.0 upto 1.0 */
, gdouble scale_x /* 0.0 upto 10.0 where 1.0 = 1:1 */
, gdouble scale_y /* 0.0 upto 10.0 where 1.0 = 1:1 */
@@ -384,8 +390,11 @@ static void p_paste_logo_pattern(gint32 drawable_id
static void p_do_insert_area_processing(GapStbFetchData *gfd
, GapStoryRenderVidHandle *vidhand);
+static gint32 p_prepare_GRAY_image(gint32 image_id);
+static void p_do_insert_alpha_processing(GapStbFetchData *gfd
+ , GapStoryRenderVidHandle *vidhand);
+
-
static gint32 p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
, gint32 master_frame_nr /* starts at 1 */
, gint32 vid_width /* desired Video Width in pixels */
@@ -530,6 +539,12 @@ gap_story_render_debug_print_frame_elem(GapStoryRenderFrameRangeElem *frn_elem,
printf(" [%d] opacity_accel : %d\n", (int)l_idx, (int)frn_elem->opacity_accel );
printf(" [%d] opacity_frames_done : %d\n", (int)l_idx, (int)frn_elem->opacity_frames_done );
+ printf(" [%d] rotate_from : %f\n", (int)l_idx, (float)frn_elem->rotate_from );
+ printf(" [%d] rotate_to : %f\n", (int)l_idx, (float)frn_elem->rotate_to );
+ printf(" [%d] rotate_dur : %d\n", (int)l_idx, (int)frn_elem->rotate_dur );
+ printf(" [%d] rotate_accel : %d\n", (int)l_idx, (int)frn_elem->rotate_accel );
+ printf(" [%d] rotate_frames_done : %d\n", (int)l_idx, (int)frn_elem->rotate_frames_done );
+
printf(" [%d] scale_x_from : %f\n", (int)l_idx, (float)frn_elem->scale_x_from );
printf(" [%d] scale_x_to : %f\n", (int)l_idx, (float)frn_elem->scale_x_to );
printf(" [%d] scale_x_dur : %d\n", (int)l_idx, frn_elem->scale_x_dur );
@@ -957,10 +972,10 @@ p_find_min_max_vid_tracknumbers(GapStoryRenderFrameRangeElem *frn_list
/* --------------------------------
* p_attribute_query_at_step
* --------------------------------
- * return
- * o) from_val (before and at start of transition) or
+ * return
+ * o) from_val (before and at start of transition) or
* o) to_val (at end and after transition) or
- * o) a mixed value according to frame_step progress and acceleratin characteristics
+ * o) a mixed value according to frame_step progress and acceleratin characteristics
* when frame_step is a frame number within the transition.
*/
static gdouble
@@ -975,15 +990,15 @@ p_attribute_query_at_step(gint32 frame_step /* current frame (since start of cur
gdouble l_mixed_val;
gdouble l_mixed_factor;
gint32 l_steps_since_transition_start;
-
+
l_steps_since_transition_start = frames_done + frame_step;
-
+
if (l_steps_since_transition_start <= 0)
{
return (from_val);
}
-
+
if (l_steps_since_transition_start >= frames_dur)
{
return (to_val);
@@ -1098,6 +1113,7 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
, gdouble *green_f
, gdouble *blue_f
, gdouble *alpha_f
+ , gdouble *rotate /* output rotate in degree */
, gdouble *opacity /* output opacity 0.0 upto 1.0 */
, gdouble *scale_x /* output 0.0 upto 10.0 where 1.0 is 1:1 */
, gdouble *scale_y /* output 0.0 upto 10.0 where 1.0 is 1:1 */
@@ -1118,6 +1134,7 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
l_framename = NULL;
/* default attributes (used if storyboard does not define settings) */
+ *rotate = 0.0;
*opacity = 1.0;
*scale_x = 1.0;
*scale_y = 1.0;
@@ -1160,11 +1177,11 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
{
gint32 fnrInt;
-
+
fnrInt = fnr; /* truncate to integer */
-
+
*localframe_tween_rest = fnr - fnrInt;
-
+
if(gap_debug)
{
printf("fnr:%.4f, fnrInt:%d localframe_tween_rest:%.4f\n"
@@ -1232,6 +1249,13 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
l_step = (master_frame_nr - 1) - l_frame_group_count;
+ *rotate = p_attribute_query_at_step(l_step
+ , frn_elem->rotate_from
+ , frn_elem->rotate_to
+ , frn_elem->rotate_dur
+ , frn_elem->rotate_frames_done
+ , frn_elem->rotate_accel
+ );
*opacity = p_attribute_query_at_step(l_step
, frn_elem->opacity_from
, frn_elem->opacity_to
@@ -1276,11 +1300,12 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
if(gap_debug)
{
- printf("p_fetch_framename: track:%d master_frame_nr:%d framename:%s: found_at_idx:%d opa:%f scale:%f %f move:%f %f layerstack_idx:%d\n"
+ printf("p_fetch_framename: track:%d master_frame_nr:%d framename:%s: found_at_idx:%d rotate:%f opa:%f scale:%f %f move:%f %f layerstack_idx:%d\n"
, (int)track
, (int)master_frame_nr
, l_framename
, (int)l_found_at_idx
+ , (float)*rotate
, (float)*opacity
, (float)*scale_x
, (float)*scale_y
@@ -1354,6 +1379,7 @@ p_new_framerange_element(GapStoryRenderFrameType frn_type
,gboolean mask_disable
,gint32 fmac_total_steps
,gint32 fmac_accel
+ ,const char *colormask_file /* IN: NULL, or name of the colormask parameter file */
)
{
GapStoryRenderFrameRangeElem *frn_elem;
@@ -1369,6 +1395,7 @@ p_new_framerange_element(GapStoryRenderFrameType frn_type
if(ext) printf(" ext:%s:\n", ext);
if(storyboard_file) printf(" storyboard_file:%s:\n", storyboard_file);
if(preferred_decoder) printf(" preferred_decoder:%s:\n", preferred_decoder);
+ if(colormask_file) printf(" colormask_file:%s:\n", colormask_file);
}
@@ -1391,14 +1418,31 @@ p_new_framerange_element(GapStoryRenderFrameType frn_type
* by using a mask_name == NULL
*/
frn_elem->mask_name = NULL;
+ frn_elem->colormask_file = NULL;
if(!mask_disable)
{
if(mask_name)
{
frn_elem->mask_name = g_strdup(mask_name);
}
+ if(colormask_file)
+ {
+ frn_elem->colormask_file = gap_file_make_abspath_filename(colormask_file, storyboard_file);
+
+ if(!g_file_test(frn_elem->colormask_file, G_FILE_TEST_EXISTS))
+ {
+ char *l_errtxt;
+
+ l_errtxt = g_strdup_printf("colormask_file not found: %s", frn_elem->colormask_file);
+ p_set_stb_error(sterr, l_errtxt);
+ g_free(l_errtxt);
+ g_free(frn_elem->colormask_file);
+ frn_elem->colormask_file = NULL;
+ }
+ }
}
+
/* default attributes (used if storyboard does not define settings) */
frn_elem->red_f = 0.0;
frn_elem->green_f = 0.0;
@@ -1409,6 +1453,9 @@ p_new_framerange_element(GapStoryRenderFrameType frn_type
frn_elem->fit_height = TRUE;
frn_elem->wait_untiltime_sec = 0.0;
frn_elem->wait_untilframes = 0;
+ frn_elem->rotate_from = 0.0;
+ frn_elem->rotate_to = 0.0;
+ frn_elem->rotate_dur = 0;
frn_elem->opacity_from = 1.0;
frn_elem->opacity_to = 1.0;
frn_elem->opacity_dur = 0;
@@ -1578,6 +1625,7 @@ p_step_all_vtrack_attributes(gint32 track
, GapStoryRenderVTrackArray *vtarr
)
{
+ vtarr->attr[track].rotate_frames_done += frames_to_handle;
vtarr->attr[track].opacity_frames_done += frames_to_handle;
vtarr->attr[track].scale_x_frames_done += frames_to_handle;
vtarr->attr[track].scale_y_frames_done += frames_to_handle;
@@ -1626,30 +1674,36 @@ p_set_vtrack_attributes(GapStoryRenderFrameRangeElem *frn_elem
frn_elem->mask_framecount = vtarr->attr[track].mask_framecount;
+ frn_elem->rotate_from = vtarr->attr[track].rotate_from;
+ frn_elem->rotate_to = vtarr->attr[track].rotate_to;
+ frn_elem->rotate_dur = vtarr->attr[track].rotate_dur;
+ frn_elem->rotate_accel = vtarr->attr[track].rotate_accel;
+ frn_elem->rotate_frames_done = vtarr->attr[track].rotate_frames_done;
+
frn_elem->opacity_from = vtarr->attr[track].opacity_from;
frn_elem->opacity_to = vtarr->attr[track].opacity_to;
frn_elem->opacity_dur = vtarr->attr[track].opacity_dur;
frn_elem->opacity_accel = vtarr->attr[track].opacity_accel;
frn_elem->opacity_frames_done = vtarr->attr[track].opacity_frames_done;
-
+
frn_elem->scale_x_from = vtarr->attr[track].scale_x_from;
frn_elem->scale_x_to = vtarr->attr[track].scale_x_to;
frn_elem->scale_x_dur = vtarr->attr[track].scale_x_dur;
frn_elem->scale_x_accel = vtarr->attr[track].scale_x_accel;
frn_elem->scale_x_frames_done = vtarr->attr[track].scale_x_frames_done;
-
+
frn_elem->scale_y_from = vtarr->attr[track].scale_y_from;
frn_elem->scale_y_to = vtarr->attr[track].scale_y_to;
frn_elem->scale_y_dur = vtarr->attr[track].scale_y_dur;
frn_elem->scale_y_accel = vtarr->attr[track].scale_y_accel;
frn_elem->scale_y_frames_done = vtarr->attr[track].scale_y_frames_done;
-
+
frn_elem->move_x_from = vtarr->attr[track].move_x_from;
frn_elem->move_x_to = vtarr->attr[track].move_x_to;
frn_elem->move_x_dur = vtarr->attr[track].move_x_dur;
frn_elem->move_x_accel = vtarr->attr[track].move_x_accel;
frn_elem->move_x_frames_done = vtarr->attr[track].move_x_frames_done;
-
+
frn_elem->move_y_from = vtarr->attr[track].move_y_from;
frn_elem->move_y_to = vtarr->attr[track].move_y_to;
frn_elem->move_y_dur = vtarr->attr[track].move_y_dur;
@@ -1730,6 +1784,7 @@ p_vidclip_shadow_add_silence(gint32 shadow_track
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
if(frn_elem)
{
@@ -1871,30 +1926,36 @@ p_copy_vattr_values(gint32 src_track
vtarr->attr[dst_track].fit_height = vtarr->attr[src_track].fit_height;
+ vtarr->attr[dst_track].rotate_from = vtarr->attr[src_track].rotate_from;
+ vtarr->attr[dst_track].rotate_to = vtarr->attr[src_track].rotate_to;
+ vtarr->attr[dst_track].rotate_dur = vtarr->attr[src_track].rotate_dur;
+ vtarr->attr[dst_track].rotate_accel = vtarr->attr[src_track].rotate_accel;
+ vtarr->attr[dst_track].rotate_frames_done = vtarr->attr[src_track].rotate_frames_done;
+
vtarr->attr[dst_track].opacity_from = vtarr->attr[src_track].opacity_from;
vtarr->attr[dst_track].opacity_to = vtarr->attr[src_track].opacity_to;
vtarr->attr[dst_track].opacity_dur = vtarr->attr[src_track].opacity_dur;
vtarr->attr[dst_track].opacity_accel = vtarr->attr[src_track].opacity_accel;
vtarr->attr[dst_track].opacity_frames_done = vtarr->attr[src_track].opacity_frames_done;
-
+
vtarr->attr[dst_track].scale_x_from = vtarr->attr[src_track].scale_x_from;
vtarr->attr[dst_track].scale_x_to = vtarr->attr[src_track].scale_x_to;
vtarr->attr[dst_track].scale_x_dur = vtarr->attr[src_track].scale_x_dur;
vtarr->attr[dst_track].scale_x_accel = vtarr->attr[src_track].scale_x_accel;
vtarr->attr[dst_track].scale_x_frames_done = vtarr->attr[src_track].scale_x_frames_done;
-
+
vtarr->attr[dst_track].scale_y_from = vtarr->attr[src_track].scale_y_from;
vtarr->attr[dst_track].scale_y_to = vtarr->attr[src_track].scale_y_to;
vtarr->attr[dst_track].scale_y_dur = vtarr->attr[src_track].scale_y_dur;
vtarr->attr[dst_track].scale_y_accel = vtarr->attr[src_track].scale_y_accel;
vtarr->attr[dst_track].scale_y_frames_done = vtarr->attr[src_track].scale_y_frames_done;
-
+
vtarr->attr[dst_track].move_x_from = vtarr->attr[src_track].move_x_from;
vtarr->attr[dst_track].move_x_to = vtarr->attr[src_track].move_x_to;
vtarr->attr[dst_track].move_x_dur = vtarr->attr[src_track].move_x_dur;
vtarr->attr[dst_track].move_x_accel = vtarr->attr[src_track].move_x_accel;
vtarr->attr[dst_track].move_x_frames_done = vtarr->attr[src_track].move_x_frames_done;
-
+
vtarr->attr[dst_track].move_y_from = vtarr->attr[src_track].move_y_from;
vtarr->attr[dst_track].move_y_to = vtarr->attr[src_track].move_y_to;
vtarr->attr[dst_track].move_y_dur = vtarr->attr[src_track].move_y_dur;
@@ -2017,6 +2078,7 @@ p_vidclip_split_and_add_frn_list(GapStoryRenderFrameRangeElem *frn_elem
,FALSE /* keep mask enabled if the element has one */
,frn_elem->fmac_total_steps
,frn_elem->fmac_accel
+ ,frn_elem->colormask_file
);
@@ -2137,31 +2199,37 @@ p_clear_vattr_array(GapStoryRenderVTrackArray *vtarr)
vtarr->attr[l_idx].keep_proportions = FALSE;
vtarr->attr[l_idx].fit_width = TRUE;
vtarr->attr[l_idx].fit_height = TRUE;
-
+
+ vtarr->attr[l_idx].rotate_from = 0.0;
+ vtarr->attr[l_idx].rotate_to = 0.0;
+ vtarr->attr[l_idx].rotate_dur = 0;
+ vtarr->attr[l_idx].rotate_accel = 0;
+ vtarr->attr[l_idx].rotate_frames_done = 0;
+
vtarr->attr[l_idx].opacity_from = 1.0;
vtarr->attr[l_idx].opacity_to = 1.0;
vtarr->attr[l_idx].opacity_dur = 0;
vtarr->attr[l_idx].opacity_accel = 0;
vtarr->attr[l_idx].opacity_frames_done = 0;
-
+
vtarr->attr[l_idx].scale_x_from = 1.0;
vtarr->attr[l_idx].scale_x_to = 1.0;
vtarr->attr[l_idx].scale_x_dur = 0;
vtarr->attr[l_idx].scale_x_accel = 0;
vtarr->attr[l_idx].scale_x_frames_done = 0;
-
+
vtarr->attr[l_idx].scale_y_from = 1.0;
vtarr->attr[l_idx].scale_y_to = 1.0;
vtarr->attr[l_idx].scale_y_dur = 0;
vtarr->attr[l_idx].scale_y_accel = 0;
vtarr->attr[l_idx].scale_y_frames_done = 0;
-
+
vtarr->attr[l_idx].move_x_from = 0.0;
vtarr->attr[l_idx].move_x_to = 0.0;
vtarr->attr[l_idx].move_x_dur = 0;
vtarr->attr[l_idx].move_x_accel = 0;
vtarr->attr[l_idx].move_x_frames_done = 0;
-
+
vtarr->attr[l_idx].move_y_from = 0.0;
vtarr->attr[l_idx].move_y_to = 0.0;
vtarr->attr[l_idx].move_y_dur = 0;
@@ -2182,7 +2250,7 @@ p_fmt_string_has_framenumber_format(const char *fmt_string)
{
const char *ptr;
gboolean l_found;
-
+
l_found = FALSE;
ptr = fmt_string;
while(ptr)
@@ -2191,7 +2259,7 @@ p_fmt_string_has_framenumber_format(const char *fmt_string)
{
break;
}
-
+
if (ptr[0] == '%')
{
if(ptr[1] != '\0')
@@ -2215,7 +2283,7 @@ p_fmt_string_has_framenumber_format(const char *fmt_string)
ptr++;
}
return (l_found);
-
+
} /* end p_fmt_string_has_framenumber_format */
/* -------------------------------------
@@ -2230,7 +2298,7 @@ p_fmt_string_has_videobasename_format(const char *fmt_string)
{
const char *ptr;
gboolean l_found;
-
+
l_found = FALSE;
ptr = fmt_string;
while(ptr)
@@ -2239,7 +2307,7 @@ p_fmt_string_has_videobasename_format(const char *fmt_string)
{
break;
}
-
+
if ((ptr[0] == '%') && (ptr[1] == 's'))
{
l_found = TRUE;
@@ -2248,7 +2316,7 @@ p_fmt_string_has_videobasename_format(const char *fmt_string)
ptr++;
}
return (l_found);
-
+
} /* end p_fmt_string_has_videobasename_format */
/* ----------------------------------------------------
@@ -2310,6 +2378,16 @@ p_storyboard_analyze(GapStoryBoard *stb
vidhand->preferred_decoder = g_strdup(stb->preferred_decoder);
}
+ vidhand->master_insert_alpha_format = NULL;
+ if(stb->master_insert_alpha_format)
+ {
+ vidhand->master_insert_alpha_format = g_strdup(stb->master_insert_alpha_format);
+ vidhand->master_insert_alpha_format_has_framenumber =
+ p_fmt_string_has_framenumber_format(vidhand->master_insert_alpha_format);
+ vidhand->master_insert_alpha_format_has_videobasename =
+ p_fmt_string_has_videobasename_format(vidhand->master_insert_alpha_format);
+ }
+
vidhand->master_insert_area_format = NULL;
if(stb->master_insert_area_format)
{
@@ -2449,6 +2527,13 @@ p_storyboard_analyze(GapStoryBoard *stb
{
switch(ii)
{
+ case GAP_STB_ATT_TYPE_ROTATE:
+ vtarr->attr[l_track].rotate_from = stb_elem->att_arr_value_from[ii];
+ vtarr->attr[l_track].rotate_to = stb_elem->att_arr_value_to[ii];
+ vtarr->attr[l_track].rotate_dur = stb_elem->att_arr_value_dur[ii];
+ vtarr->attr[l_track].rotate_accel = stb_elem->att_arr_value_accel[ii];
+ vtarr->attr[l_track].rotate_frames_done = 0;
+ break;
case GAP_STB_ATT_TYPE_OPACITY:
vtarr->attr[l_track].opacity_from = stb_elem->att_arr_value_from[ii];
vtarr->attr[l_track].opacity_to = stb_elem->att_arr_value_to[ii];
@@ -2516,6 +2601,7 @@ p_storyboard_analyze(GapStoryBoard *stb
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
if(frn_elem)
{
@@ -2552,6 +2638,7 @@ p_storyboard_analyze(GapStoryBoard *stb
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
if(frn_elem)
{
@@ -2585,6 +2672,7 @@ p_storyboard_analyze(GapStoryBoard *stb
, stb_elem->mask_disable
, stb_elem->fmac_total_steps
, stb_elem->fmac_accel
+ , stb_elem->colormask_file
);
if(frn_elem)
{
@@ -2623,6 +2711,7 @@ p_storyboard_analyze(GapStoryBoard *stb
, stb_elem->mask_disable
, stb_elem->fmac_total_steps
, stb_elem->fmac_accel
+ , stb_elem->colormask_file
);
if(frn_elem)
{
@@ -2709,6 +2798,7 @@ p_storyboard_analyze(GapStoryBoard *stb
, stb_elem->mask_disable
, stb_elem->fmac_total_steps
, stb_elem->fmac_accel
+ , stb_elem->colormask_file
);
if(frn_elem)
{
@@ -3339,7 +3429,7 @@ p_find_maskdef_by_name(GapStoryRenderVidHandle *vidhand, const char *mask_name)
/* ----------------------------------------------------
* p_mask_image_fetcher
* ----------------------------------------------------
- * fetch specified mask frame as gray image with only one composite layer.
+ * fetch specified mask frame with only one composite layer.
* if the mask definition is not found,
* then deliver -1.
* if the mask definition has less frames than master_frame_nr, then deliver the
@@ -3354,6 +3444,7 @@ p_mask_fetcher(GapStoryRenderVidHandle *vidhand
, gint32 mask_height
, gint32 *layer_id_ptr /* OUT: Id of the only layer in the composite image */
, gboolean *was_last_maskframe /* OUT: true if this was the last maskframe */
+ , gboolean makeGrayFlattened /* IN true flatten and convert to GRAY, false keep color and alpha channel */
)
{
GapStoryRenderMaskDefElem *maskdef_elem;
@@ -3378,18 +3469,15 @@ p_mask_fetcher(GapStoryRenderVidHandle *vidhand
l_framenr = MIN(master_frame_nr, maskdef_elem->frame_count);
- if(gap_debug)
- {
+ if(gap_debug)
+ {
printf("\n############# MASK start FETCH ##########\n");
printf("MASK relevant framenr:%d\n"
, (int)l_framenr
);
gap_story_render_debug_print_maskdef_elem(maskdef_elem, -7);
- }
+ }
- /* the composite image fecther already converts to gray when called
- * with a mask videohandle (marked with is_mask_handle flag)
- */
image_id = p_story_render_fetch_composite_image_private(maskdef_elem->mask_vidhand
, l_framenr /* starts at 1 */
, mask_width /* desired Width in pixels */
@@ -3406,12 +3494,22 @@ p_mask_fetcher(GapStoryRenderVidHandle *vidhand
printf("\n.........#### MASK end FETCH ####......\n");
}
+ if(makeGrayFlattened == TRUE)
+ {
+ if(gimp_image_base_type(image_id) != GIMP_GRAY)
+ {
+ gimp_image_convert_grayscale(image_id);
+ }
+ }
*layer_id_ptr = gap_layer_flip(*layer_id_ptr, maskdef_elem->flip_request);
- if(gimp_drawable_has_alpha(*layer_id_ptr))
+ if(makeGrayFlattened == TRUE)
{
- *layer_id_ptr = gimp_image_flatten(image_id);
+ if(gimp_drawable_has_alpha(*layer_id_ptr))
+ {
+ *layer_id_ptr = gimp_image_flatten(image_id);
+ }
}
if(gap_debug)
@@ -3443,6 +3541,7 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
, gint32 local_stepcount
, gint32 image_id
, gint32 layer_id
+ , GapStoryMaskAnchormode mask_anchor
)
{
gint32 l_tmp_mask_image_id;
@@ -3451,10 +3550,17 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
gdouble l_framenr;
gboolean l_found_in_cache;
gboolean l_was_last_maskframe;
+ gboolean l_makeGrayFlattened;
/* both local_stepcount and mask_framecount start with 0 for the 1st element */
l_framenr = frn_elem->mask_stepsize * (gdouble)(frn_elem->mask_framecount + local_stepcount);
l_master_framenr = 1 + (gint32)(l_framenr);
+ l_makeGrayFlattened = TRUE;
+
+ if (mask_anchor == GAP_MSK_ANCHOR_XCOLOR)
+ {
+ l_makeGrayFlattened = FALSE;
+ }
if(gap_debug)
{
@@ -3490,6 +3596,7 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
, gimp_drawable_height(layer_id)
,&l_tmp_mask_layer_id
,&l_was_last_maskframe
+ , l_makeGrayFlattened
);
}
@@ -3519,14 +3626,29 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
{
gimp_layer_add_alpha(layer_id);
}
- l_new_layer_mask_id = gimp_layer_create_mask(layer_id, GIMP_ADD_WHITE_MASK);
- gimp_layer_add_mask(layer_id, l_new_layer_mask_id);
+ if (mask_anchor == GAP_MSK_ANCHOR_XCOLOR)
+ {
+ /* render the layermask by applying the mask as colormask */
+ gap_colormask_apply_to_layer_of_same_size_from_file (layer_id
+ , l_tmp_mask_layer_id /* the colormask to be applied */
+ , frn_elem->colormask_file /* colormask parameter file */
+ , TRUE /* keepLayerMask */
+ , FALSE /* doProgress */
+ );
+ }
+ else
+ {
+ l_new_layer_mask_id = gimp_layer_create_mask(layer_id, GIMP_ADD_WHITE_MASK);
+ gimp_layer_add_mask(layer_id, l_new_layer_mask_id);
+
+
+ /* overwrite the white layer mask with the fetched mask */
+ gap_layer_copy_content(l_new_layer_mask_id /* dst_drawable_id */
+ ,l_tmp_mask_layer_id /* src_drawable_id */
+ );
+ }
- /* overwrite the white layer mask with the fetched mask */
- gap_layer_copy_content(l_new_layer_mask_id /* dst_drawable_id */
- ,l_tmp_mask_layer_id /* src_drawable_id */
- );
if(!l_found_in_cache)
@@ -3551,6 +3673,7 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
} /* end p_fetch_and_add_layermask */
+
/* ----------------------------------------------------
* p_open_video_handle_private
* ----------------------------------------------------
@@ -3617,10 +3740,14 @@ p_open_video_handle_private( gboolean ignore_audio
vidhand->frn_list = NULL;
vidhand->preferred_decoder = NULL;
+ vidhand->master_insert_alpha_format = NULL;
+ vidhand->master_insert_alpha_format_has_videobasename = FALSE;
+ vidhand->master_insert_alpha_format_has_framenumber = FALSE;
+
vidhand->master_insert_area_format = NULL;
vidhand->master_insert_area_format_has_videobasename = FALSE;
vidhand->master_insert_area_format_has_framenumber = FALSE;
-
+
vidhand->do_gimp_progress = do_gimp_progress;
*vidhand->progress = 0.0;
vidhand->sterr = p_new_stb_error();
@@ -3722,6 +3849,7 @@ p_open_video_handle_private( gboolean ignore_audio
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
if(frn_elem)
{
@@ -3755,6 +3883,7 @@ p_open_video_handle_private( gboolean ignore_audio
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
render_section->frn_list->frames_to_handle = l_from -1;
render_section->frn_list->next = frn_elem;
@@ -3815,6 +3944,7 @@ p_open_video_handle_private( gboolean ignore_audio
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
if(frn_elem) *frame_count = frn_elem->frames_to_handle;
@@ -3846,6 +3976,7 @@ p_open_video_handle_private( gboolean ignore_audio
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
render_section->frn_list->frames_to_handle = l_from -1;
render_section->frn_list->next = frn_elem;
@@ -3894,6 +4025,7 @@ p_open_video_handle_private( gboolean ignore_audio
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
if(frn_elem)
{
@@ -3932,6 +4064,7 @@ p_open_video_handle_private( gboolean ignore_audio
, TRUE /* mask_disable */
, 1 /* fmac_total_steps */
, 0 /* fmac_accel */
+ , NULL /* colormask_file */
);
if(frn_elem)
{
@@ -4165,7 +4298,7 @@ p_exec_filtermacro(gint32 image_id, gint32 layer_id, const char *filtermacro_fil
, (int)total_steps
);
}
-
+
if(! gimp_drawable_has_alpha (layer_id))
{
/* some filtermacros do not work with layer that do not have an alpha channel
@@ -4194,7 +4327,7 @@ p_exec_filtermacro(gint32 image_id, gint32 layer_id, const char *filtermacro_fil
else
{
gdouble current_accel_step;
-
+
current_accel_step = gap_calculate_current_step_with_acceleration(current_step
, total_steps
, accelerationCharacteristic
@@ -4254,11 +4387,15 @@ p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated, gint32 comp_imag
gboolean l_ret;
l_ret = TRUE;
-
+
+ if(calculated->rotate != 0.0)
+ {
+ return (l_ret);
+ }
calculated_area = calculated->width * calculated->height;
visible_on_composite_area = calculated->visible_width * calculated->visible_height;
composite_img_area = gimp_image_width(comp_image_id) * gimp_image_height(comp_image_id);
-
+
if(gap_debug)
{
tmp_image_area = gimp_image_width(tmp_image_id) * gimp_image_height(tmp_image_id);
@@ -4269,15 +4406,15 @@ p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated, gint32 comp_imag
, (int)composite_img_area
);
}
-
+
if ((calculated_area > composite_img_area)
|| (visible_on_composite_area < composite_img_area))
{
if((frn_elem->mask_name != NULL)
- && (frn_elem->mask_anchor == GAP_MSK_ANCHOR_CLIP))
+ && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
{
tmp_image_area = gimp_image_width(tmp_image_id) * gimp_image_height(tmp_image_id);
-
+
/* operation on clipped rectangle requires creation of the mask already at
* tmp_image_area size. in case the calculated area
* is smaller than the original tmp image, it will be
@@ -4285,19 +4422,21 @@ p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated, gint32 comp_imag
*/
if (tmp_image_area < calculated_area)
{
- l_ret = FALSE; /* operate on visble rectanngle only */
+ l_ret = FALSE; /* operate on visble rectangle only */
}
}
else
{
- l_ret = FALSE; /* operate on visble rectanngle only */
+ l_ret = FALSE; /* operate on visble rectangle only */
}
}
-
+
return (l_ret);
} /* end p_transform_operate_on_full_layer */
+
+
/* ----------------------------------------------------
* p_transform_and_add_layer
* ----------------------------------------------------
@@ -4317,6 +4456,7 @@ p_transform_and_add_layer( gint32 comp_image_id
, gboolean keep_proportions
, gboolean fit_width
, gboolean fit_height
+ , gdouble rotate /* rotation in degree */
, gdouble opacity /* 0.0 upto 1.0 */
, gdouble scale_x /* 0.0 upto 10.0 where 1.0 = 1:1 */
, gdouble scale_y /* 0.0 upto 10.0 where 1.0 = 1:1 */
@@ -4338,9 +4478,10 @@ p_transform_and_add_layer( gint32 comp_image_id
if(gap_debug)
{
- printf("p_transform_and_add_layer: called at layer_id: %d, tmp_image_id:%d\n"
+ printf("p_transform_and_add_layer: called at layer_id: %d, tmp_image_id:%d comp_image_id:%d\n"
, (int)layer_id
- ,(int)tmp_image_id );
+ , (int)tmp_image_id
+ , (int)comp_image_id);
gap_story_render_debug_print_frame_elem(frn_elem, -77);
}
@@ -4385,6 +4526,7 @@ p_transform_and_add_layer( gint32 comp_image_id
, keep_proportions
, fit_width
, fit_height
+ , rotate
, opacity
, scale_x
, scale_y
@@ -4438,7 +4580,7 @@ p_transform_and_add_layer( gint32 comp_image_id
if((frn_elem->mask_name != NULL)
- && (frn_elem->mask_anchor == GAP_MSK_ANCHOR_CLIP))
+ && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
{
/* fetch and add mask at calculated scaled size of layer_id */
p_fetch_and_add_layermask(vidhand
@@ -4446,6 +4588,7 @@ p_transform_and_add_layer( gint32 comp_image_id
, local_stepcount
, tmp_image_id
, layer_id
+ , frn_elem->mask_anchor
);
/* apply the mask (necessary because the following copy with this layer
* as source would ignore the layer mask)
@@ -4454,6 +4597,21 @@ p_transform_and_add_layer( gint32 comp_image_id
}
+ if((rotate > 0.05) || (rotate < -0.05))
+ {
+ gint32 l_orig_width;
+ gint32 l_orig_height;
+
+ l_orig_width = gimp_drawable_width(layer_id);
+ l_orig_height = gimp_drawable_height(layer_id);
+
+ gap_story_transform_rotate_layer(tmp_image_id, layer_id, rotate);
+
+ /* recalculate offests to compensate size changes caused by rotation */
+ calculated->x_offs = calculated->x_offs + (l_orig_width / 2.0) - (gimp_drawable_width(layer_id) / 2.0);
+ calculated->y_offs = calculated->y_offs + (l_orig_height / 2.0) - (gimp_drawable_height(layer_id) / 2.0);
+ }
+
/* copy from tmp_image and paste to composite_image
* force copying of the complete layer by clearing the selection
*/
@@ -4470,20 +4628,20 @@ p_transform_and_add_layer( gint32 comp_image_id
}
else
{
- /* operate on clipped rectangle size */
+ /* operate on clipped rectangle size (rotation not handled in this case) */
if(gap_debug)
{
printf("p_transform operate on CLIPPED RECTANGLE\n");
}
-
+
if ((calculated->visible_width <= 0) || (calculated->visible_height <= 0))
{
/* nothing will be visible (width or height is 0), so we can skip copying and scaling */
return (l_new_layer_id); /* that is still fully transparent */
}
-
+
if((frn_elem->mask_name != NULL)
- && (frn_elem->mask_anchor == GAP_MSK_ANCHOR_CLIP))
+ && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
{
/* add and apply layermask at original unscaled tmp_image size */
p_fetch_and_add_layermask(vidhand
@@ -4491,6 +4649,7 @@ p_transform_and_add_layer( gint32 comp_image_id
, local_stepcount
, tmp_image_id
, layer_id
+ , frn_elem->mask_anchor
);
/* apply the mask (necessary because the following copy with this layer
* as source would ignore the layer mask)
@@ -4499,8 +4658,8 @@ p_transform_and_add_layer( gint32 comp_image_id
}
-
- /* copy selected clipped source rectangle from tmp_image to composite image
+
+ /* copy selected clipped source rectangle from tmp_image to composite image
* as floating selection attached to l_new_layer_id (visble part at source size)
*/
{
@@ -4508,21 +4667,21 @@ p_transform_and_add_layer( gint32 comp_image_id
gdouble sy;
gdouble swidth;
gdouble sheight;
-
+
sx = 0;
if (calculated->x_offs < 0)
{
- sx = (0 - calculated->x_offs) *
+ sx = (0 - calculated->x_offs) *
((gdouble)gimp_image_width(tmp_image_id) / MAX((gdouble)calculated->width, 1.0));
}
-
+
sy = 0;
if (calculated->y_offs < 0)
{
- sy = (0 - calculated->y_offs) *
+ sy = (0 - calculated->y_offs) *
((gdouble)gimp_image_height(tmp_image_id) / MAX((gdouble)calculated->height, 1.0));
}
-
+
swidth = calculated->visible_width * gimp_image_width(tmp_image_id) / MAX(calculated->width, 1);
sheight = calculated->visible_height * gimp_image_height(tmp_image_id) / MAX(calculated->height, 1);
@@ -4569,10 +4728,14 @@ p_transform_and_add_layer( gint32 comp_image_id
gimp_floating_sel_anchor(l_fsel_layer_id);
-
+
}
+// if((rotate > 0.05) || (rotate < -0.05))
+// {
+// gap_story_transform_rotate_layer(comp_image_id, l_new_layer_id, rotate);
+// }
@@ -4584,6 +4747,7 @@ p_transform_and_add_layer( gint32 comp_image_id
, local_stepcount
, comp_image_id
, l_new_layer_id
+ , frn_elem->mask_anchor
);
}
@@ -4717,13 +4881,13 @@ p_limit_open_videohandles(GapStoryRenderVidHandle *vidhand
, 100
);
l_count_open_videohandles = currently_open_videohandles;
-
+
if (l_count_open_videohandles <= l_max_open_videohandles)
{
/* we are below the limit, nothing left to do in that case */
return;
}
-
+
for (frn_elem = vidhand->frn_list; frn_elem != NULL; frn_elem = (GapStoryRenderFrameRangeElem *)frn_elem->next)
{
if((frn_elem->last_master_frame_access < master_frame_nr)
@@ -4747,7 +4911,7 @@ p_limit_open_videohandles(GapStoryRenderVidHandle *vidhand
{
return;
}
-
+
}
}
#endif
@@ -5038,8 +5202,8 @@ gap_story_render_fetch_composite_image(GapStoryRenderVidHandle *vidhand
/* ------------------------------------------------
* p_split_delace_value
* ------------------------------------------------
- * split the specified delace value:
- * integer part is deinterlace mode,
+ * split the specified delace value:
+ * integer part is deinterlace mode,
* (0 NO,
* 1 Odd,
* 2 Even,
@@ -5053,7 +5217,7 @@ gap_story_render_fetch_composite_image(GapStoryRenderVidHandle *vidhand
* between 2 framenumbers, where the value 0.5 is the middle.
* An Interlaced frame contains 2 half-frames where one half-frame is represented by
* the even, the other half-frame by the odd lines.
- *
+ *
* therfore localframe_tween_rest values >= 0.5 selects the other half-frame,
* in case the enable_interlace_tween_pick option is enabled.
*
@@ -5102,7 +5266,7 @@ p_split_delace_value(gdouble delace, gdouble localframe_tween_rest, gint32 *dein
/* -------------------------------------------------------------------
- * p_conditional_delace_drawable
+ * p_conditional_delace_drawable
* -------------------------------------------------------------------
* general deinterlace handling for frames, images an animimages
* (except cliptype movie)
@@ -5155,12 +5319,22 @@ p_stb_render_image_or_animimage(GapStbFetchData *gfd
, gfd->framename /* full filename of the image */
, TRUE /* enable caching */
);
-
+
gimp_selection_none(l_orig_image_id);
if(gfd->frn_type == GAP_FRN_IMAGE)
{
- gfd->layer_id = p_prepare_RGB_image(l_orig_image_id);
gfd->tmp_image_id = gimp_image_duplicate(l_orig_image_id);
+ gfd->layer_id = p_prepare_RGB_image(gfd->tmp_image_id);
+ gap_frame_fetch_remove_parasite(gfd->tmp_image_id);
+ if(gap_debug)
+ {
+ printf("IMAGE fetch master_frame_nr:%d (dup)tmp_image_id:%d layer_id:%d l_orig_image_id:%d\n"
+ ,(int)master_frame_nr
+ ,(int)gfd->tmp_image_id
+ ,(int)gfd->layer_id
+ ,(int)l_orig_image_id
+ );
+ }
}
else
{
@@ -5213,14 +5387,14 @@ p_stb_render_image_or_animimage(GapStbFetchData *gfd
g_free (l_layers_list);
}
}
-
+
} /* end p_stb_render_image_or_animimage */
/* -------------------------------------------
* p_stb_render_movie (GAP_FRN_MOVIE)
* -------------------------------------------
- * fetch frame from a videofile (gfd->framename contains the videofile name)
+ * fetch frame from a videofile (gfd->framename contains the videofile name)
*/
static void
p_stb_render_movie(GapStbFetchData *gfd
@@ -5417,7 +5591,7 @@ p_stb_render_section(GapStbFetchData *gfd
* p_stb_render_frame_images (GAP_FRN_FRAMES)
* -------------------------------------------
* gfd->framename is one single imagefile out of a series of numbered imagefiles.
- * (note that the gfd->framename is already full qualified
+ * (note that the gfd->framename is already full qualified
* and includes path name, numberpart and extension)
*/
static void
@@ -5440,7 +5614,7 @@ p_stb_render_frame_images(GapStbFetchData *gfd, gint32 master_frame_nr)
* p_stb_render_composite_image_postprocessing
* -------------------------------------------
* perform postprocessing on the composite frame image.
- * this includes
+ * this includes
* - convert to gray (only when fetching masks)
* - optional applying the global filtermacro
* - check size and scale (if filtermacro has changed size of the composite image)
@@ -5474,17 +5648,6 @@ p_stb_render_composite_image_postprocessing(GapStbFetchData *gfd
);
}
- if(vidhand->is_mask_handle == TRUE)
- {
- /* we are running as mask fetcher,
- * therefore convert to GRAY image
- */
- if(gimp_image_base_type(gfd->comp_image_id) != GIMP_GRAY)
- {
- gimp_image_convert_grayscale(gfd->comp_image_id);
- }
- }
-
/* debug: disabled code to display a copy of the image */
if(1==0)
{
@@ -5600,7 +5763,7 @@ p_stb_render_result_monitoring(GapStbFetchData *gfd, gint32 master_frame_nr)
/* ------------------------
* p_paste_logo_pattern
- * ------------------------
+ * ------------------------
* replace logo area with the specified logo pattern
*/
static void
@@ -5646,7 +5809,7 @@ p_paste_logo_pattern(gint32 drawable_id
gimp_layer_set_offsets(l_fsel_layer_id
, offsetX + l_src_offset_x
, offsetY + l_src_offset_y);
-
+
gimp_floating_sel_anchor(l_fsel_layer_id);
} /* end p_copy_and_paste_replacement_pattern */
@@ -5656,6 +5819,7 @@ p_paste_logo_pattern(gint32 drawable_id
/* -------------------------------------
* p_do_insert_area_processing
* -------------------------------------
+ * add logo area to video clip
*/
static void
p_do_insert_area_processing(GapStbFetchData *gfd
@@ -5663,10 +5827,10 @@ p_do_insert_area_processing(GapStbFetchData *gfd
{
char *logo_imagename;
char *videofilename_without_path;
-
-
- videofilename_without_path = gap_story_build_basename(gfd->framename);
-
+
+
+ videofilename_without_path = gap_lib_build_basename_without_ext(gfd->framename);
+
if (vidhand->master_insert_area_format_has_framenumber)
{
if (vidhand->master_insert_area_format_has_videobasename)
@@ -5714,7 +5878,7 @@ p_do_insert_area_processing(GapStbFetchData *gfd
{
gint32 logo_image_id;
gint32 logo_layer_id;
-
+
if (vidhand->master_insert_area_format_has_framenumber)
{
logo_image_id = gap_lib_load_image(logo_imagename);
@@ -5727,14 +5891,14 @@ p_do_insert_area_processing(GapStbFetchData *gfd
, TRUE /* enable caching */
);
}
-
+
if(logo_image_id < 0)
{
printf("p_do_insert_area_processing: ERROR could not load logo_imagename:%s\n", logo_imagename);
return;
}
-
-
+
+
gimp_selection_none(logo_image_id);
logo_layer_id = p_prepare_RGB_image(logo_image_id);
@@ -5752,6 +5916,202 @@ p_do_insert_area_processing(GapStbFetchData *gfd
} /* end p_do_insert_area_processing */
+
+
+
+/* ----------------------------------------------------
+ * p_prepare_GRAY_image
+ * ----------------------------------------------------
+ * prepare image to be applied as transparency channel to a frame.
+ * - clear undo stack
+ * - convert to GREY
+ * - merge all visible layer to one layer that
+ * fits the image size.
+ *
+ * return the resulting layer_id.
+ */
+static gint32
+p_prepare_GRAY_image(gint32 image_id)
+{
+ gint l_nlayers;
+ gint32 *l_layers_list;
+ gint32 l_layer_id;
+
+ l_layer_id = -1;
+ /* dont waste time and memory for undo in noninteracive processing
+ * of the frames
+ */
+ /* gimp_image_undo_enable(image_id); */ /* clear undo stack */
+ /* no more gimp_image_undo_enable, because this results in Warnings since gimp-2.1.6
+ * Gimp-Core-CRITICAL **: file gimpimage.c: line 1708 (gimp_image_undo_thaw): assertion `gimage->undo_freeze_count > 0' failed
+ */
+ gimp_image_undo_disable(image_id); /* NO Undo */
+
+ l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+ if(l_layers_list != NULL)
+ {
+ l_layer_id = l_layers_list[0];
+ g_free (l_layers_list);
+ }
+
+ if((l_nlayers > 1 ) || (gimp_layer_get_mask (l_layer_id) >= 0))
+ {
+ if(gap_debug) printf("DEBUG: p_prepare_image merge layers tmp image\n");
+
+ /* merge visible layers (reduce to single layer) */
+ l_layer_id = gap_image_merge_visible_layers(image_id, GIMP_CLIP_TO_IMAGE);
+ }
+
+ /* convert TO GREY if needed */
+ if(gimp_image_base_type(image_id) != GIMP_GRAY)
+ {
+ gimp_image_convert_grayscale(image_id);
+ }
+
+ if(l_layer_id >= 0)
+ {
+ gimp_layer_resize_to_image_size(l_layer_id);
+ }
+
+ return(l_layer_id);
+} /* end p_prepare_GRAY_image */
+
+
+
+
+/* -------------------------------------
+ * p_do_insert_alpha_processing
+ * -------------------------------------
+ * adds alpha channel for videoframes
+ * based on an image or series of frames
+ * matching the configured format string.
+ * (VID_MASTER_INSERT_ALPHA)
+ *
+ */
+static void
+p_do_insert_alpha_processing(GapStbFetchData *gfd
+ , GapStoryRenderVidHandle *vidhand)
+{
+ char *alpha_imagename;
+ char *videofilename_without_path;
+
+
+ videofilename_without_path = gap_lib_build_basename_without_ext(gfd->framename);
+
+ if (vidhand->master_insert_alpha_format_has_framenumber)
+ {
+ if (vidhand->master_insert_alpha_format_has_videobasename)
+ {
+ alpha_imagename =
+ g_strdup_printf(vidhand->master_insert_alpha_format
+ , videofilename_without_path
+ , gfd->localframe_index /* videoFrameNr */
+ );
+ }
+ else
+ {
+ alpha_imagename =
+ g_strdup_printf(vidhand->master_insert_alpha_format
+ , gfd->localframe_index /* videoFrameNr */
+ );
+ }
+ }
+ else
+ {
+ if (vidhand->master_insert_alpha_format_has_videobasename)
+ {
+ alpha_imagename =
+ g_strdup_printf(vidhand->master_insert_alpha_format
+ , videofilename_without_path
+ );
+ }
+ else
+ {
+ alpha_imagename = g_strdup(vidhand->master_insert_alpha_format);
+ }
+ }
+
+ if(gap_debug)
+ {
+ printf("p_do_insert_alpha_processing: format:%s\n video:%s\n alpha_imagename:%s\n"
+ , vidhand->master_insert_alpha_format
+ , videofilename_without_path
+ , alpha_imagename
+ );
+ }
+
+
+ if(g_file_test(alpha_imagename, G_FILE_TEST_EXISTS))
+ {
+ gint32 alpha_image_id;
+ gint32 alpha_layer_id;
+ gint vid_width;
+ gint vid_height;
+
+ if (vidhand->master_insert_alpha_format_has_framenumber)
+ {
+ alpha_image_id = gap_lib_load_image(alpha_imagename);
+ }
+ else
+ {
+ /* use framefetcher cache in case all frames shall get alpha from the same image */
+ alpha_image_id = gap_frame_fetch_orig_image(vidhand->ffetch_user_id
+ , alpha_imagename
+ , TRUE /* enable caching */
+ );
+ }
+
+ if(alpha_image_id < 0)
+ {
+ printf("p_do_insert_alpha_processing: ERROR could not load alpha_imagename:%s\n"
+ , alpha_imagename
+ );
+ return;
+ }
+
+
+ gimp_selection_none(alpha_image_id);
+ alpha_layer_id = p_prepare_GRAY_image(alpha_image_id);
+
+ vid_width = gimp_image_width(gfd->tmp_image_id);
+ vid_height = gimp_image_height(gfd->tmp_image_id);
+
+ /* scale alpha image to Videosize (if not already equal) */
+ if ((gimp_image_width(alpha_image_id) != vid_width)
+ || (gimp_image_height(alpha_image_id) != vid_height) )
+ {
+ if(gap_debug)
+ {
+ printf("DEBUG: p_do_insert_alpha_processing scaling alpha image\n");
+ }
+ gimp_image_scale(gfd->comp_image_id, vid_width, vid_height);
+ }
+
+ if(! gimp_drawable_has_alpha(gfd->layer_id))
+ {
+ /* have to add alpha channel */
+ gimp_layer_add_alpha(gfd->layer_id);
+ }
+
+
+ /* copy alpha_layer_id into the alpha channel of the current frame */
+ gap_layer_copy_picked_channel(gfd->layer_id, 3 /* dst_pick is the alpha channel */
+ ,alpha_layer_id, 0 /* gray value */
+ ,FALSE /* shadow */
+ );
+
+ if (vidhand->master_insert_alpha_format_has_framenumber)
+ {
+ /* do not keep individual per frame alpha images cached
+ */
+ gap_image_delete_immediate(alpha_image_id);
+ }
+ }
+
+} /* end p_do_insert_alpha_processing */
+
+
+
/* --------------------------------------------
* p_story_render_fetch_composite_image_private
* --------------------------------------------
@@ -5790,6 +6150,8 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
gfd->layer_id = -1;
*layer_id = -1;
+
+
if(gap_debug)
{
printf("p_story_render_fetch_composite_image_private START master_frame_nr:%d %dx%d vidhand:%d\n"
@@ -5814,7 +6176,6 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
{
if((vidhand->is_mask_handle == FALSE)
&& (master_frame_nr == 1)
- // && (section_name == NULL)
)
{
printf("\n###\n###\nSTART rendering at master_frame_nr 1 with this list of elements:\n");
@@ -5845,6 +6206,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
, &l_green_f
, &l_blue_f
, &l_alpha_f
+ , &gfd->rotate /* output rotateion in degree */
, &gfd->opacity /* output opacity 0.0 upto 1.0 */
, &gfd->scale_x /* output 0.0 upto 10.0 where 1.0 is 1:1 */
, &gfd->scale_y /* output 0.0 upto 10.0 where 1.0 is 1:1 */
@@ -5908,11 +6270,19 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
return -1;
}
gfd->layer_id = p_prepare_RGB_image(gfd->tmp_image_id);
- if((gfd->frn_type == GAP_FRN_MOVIE) && (vidhand->master_insert_area_format))
+
+ if(gfd->frn_type == GAP_FRN_MOVIE)
{
- p_do_insert_area_processing(gfd, vidhand);
+ if(vidhand->master_insert_alpha_format)
+ {
+ p_do_insert_alpha_processing(gfd, vidhand);
+ }
+ if(vidhand->master_insert_area_format)
+ {
+ p_do_insert_area_processing(gfd, vidhand);
+ }
}
-
+
p_conditional_delace_drawable(gfd, gfd->layer_id);
g_free(gfd->framename);
}
@@ -5921,15 +6291,18 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
if(gap_debug)
{
- printf("p_prepare_RGB_image returned layer_id: %d, tmp_image_id:%d\n"
+ printf("p_prepare_RGB_image returned layer_id: %d, tmp_image_id:%d (master_frame_nr:%d, comp_image_id:%d)\n"
, (int)gfd->layer_id
, (int)gfd->tmp_image_id
+ , (int)master_frame_nr
+ , (int)gfd->comp_image_id
);
- }
+ }
if(gfd->comp_image_id < 0)
{
if((gfd->opacity == 1.0)
+ && (gfd->rotate == 0.0)
&& (gfd->scale_x == 1.0)
&& (gfd->scale_y == 1.0)
&& (gfd->move_x == 0.0)
@@ -5978,6 +6351,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
,gfd->keep_proportions
,gfd->fit_width
,gfd->fit_height
+ ,gfd->rotate
,gfd->opacity
,gfd->scale_x
,gfd->scale_y
@@ -6029,7 +6403,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
/* -------------------------------------------------------------------
* gap_story_render_fetch_composite_image_or_chunk (see included file)
* -------------------------------------------------------------------
- *
+ *
*/
#include "gap_story_render_lossless.c"
diff --git a/gap/gap_story_render_types.h b/gap/gap_story_render_types.h
index 80d6fa6..f208374 100644
--- a/gap/gap_story_render_types.h
+++ b/gap/gap_story_render_types.h
@@ -60,7 +60,8 @@ typedef gpointer t_GVA_Handle;
typedef enum
{
GAP_MSK_ANCHOR_CLIP
- ,GAP_MSK_ANCHOR_MASTER
+ ,GAP_MSK_ANCHOR_MASTER
+ ,GAP_MSK_ANCHOR_XCOLOR /* apply to clip as colormask */
} GapStoryMaskAnchormode;
typedef enum
@@ -137,7 +138,7 @@ typedef struct GapStoryRenderFrameRangeElem /* nick: frn_elem */
char *filtermacro_file_to; /* additional macro with 2nd parameterset(s) for varying apply */
gint32 fmac_total_steps; /* total steps for varying filtermacro apply */
gint32 fmac_accel; /* acceleration characteristic for filtermacro apply with varying values */
-
+
gdouble frame_from; /* internal frame number that is 1.st of range (float due to internal clip splitting) */
gdouble frame_to; /* internal frame number that is the last handled frame of the range */
gint32 frames_to_handle;
@@ -157,11 +158,19 @@ typedef struct GapStoryRenderFrameRangeElem /* nick: frn_elem */
char *mask_name; /* optional reference to a layer mask */
gdouble mask_stepsize;
GapStoryMaskAnchormode mask_anchor;
+ char *colormask_file; /* optional reference to a colormask parameter file
+ * relevant for ancor mode GAP_MSK_ANCHOR_CLIPCOLOR
+ * where mask is applied as colormask
+ */
gdouble wait_untiltime_sec;
gint32 wait_untilframes;
+ gdouble rotate_from; /* -36000.0 upto 3600.0 degree */
+ gdouble rotate_to; /* -36000.0 upto 3600.0 degree */
+ gint32 rotate_dur; /* number of frames to change from -> to value */
+
gdouble opacity_from; /* 0.0 upto 1.0 */
gdouble opacity_to; /* 0.0 upto 1.0 */
gint32 opacity_dur; /* number of frames to change from -> to value */
@@ -183,7 +192,7 @@ typedef struct GapStoryRenderFrameRangeElem /* nick: frn_elem */
gint32 move_y_dur; /* number of frames to change from -> to value */
gint opacity_accel; /* acceleration characteristic for opacity transformation */
- gint32 opacity_frames_done; /* already processed frames since begin of transition */
+ gint32 opacity_frames_done; /* already processed frames since begin of transition */
gint move_x_accel;
gint32 move_x_frames_done;
gint move_y_accel;
@@ -192,6 +201,8 @@ typedef struct GapStoryRenderFrameRangeElem /* nick: frn_elem */
gint32 scale_x_frames_done;
gint scale_y_accel;
gint32 scale_y_frames_done;
+ gint rotate_accel; /* acceleration characteristic for opacity transformation */
+ gint32 rotate_frames_done; /* already processed frames since begin of transition */
void *next;
@@ -251,6 +262,10 @@ typedef struct GapStoryRenderVTrackAttrElem
gboolean fit_width;
gboolean fit_height;
+ gdouble rotate_from; /* rotation in degree */
+ gdouble rotate_to; /* rotation in degree */
+ gint32 rotate_dur; /* number of frames to change from -> to value */
+
gdouble opacity_from; /* 0.0 upto 1.0 */
gdouble opacity_to; /* 0.0 upto 1.0 */
gint32 opacity_dur; /* number of frames to change from -> to value */
@@ -271,8 +286,10 @@ typedef struct GapStoryRenderVTrackAttrElem
gdouble move_y_to; /* -1.0 upto 1.0 where 0 is center and -1.0 up outside */
gint32 move_y_dur; /* number of frames to change from -> to value */
+ gint rotate_accel; /* acceleration characteristic for opacity transformation */
+ gint32 rotate_frames_done; /* already processed frames since begin of transition */
gint opacity_accel; /* acceleration characteristic for opacity transformation */
- gint32 opacity_frames_done; /* already processed frames since begin of transition */
+ gint32 opacity_frames_done; /* already processed frames since begin of transition */
gint move_x_accel;
gint32 move_x_frames_done;
gint move_y_accel;
@@ -323,6 +340,11 @@ typedef struct GapStoryRenderVidHandle
GapStoryRenderAudioRangeElem *aud_list;
GapStoryRenderErrors *sterr;
char *preferred_decoder;
+
+ char *master_insert_alpha_format; /* Format for alpha channel inserting */
+ gboolean master_insert_alpha_format_has_videobasename;
+ gboolean master_insert_alpha_format_has_framenumber;
+
char *master_insert_area_format; /* Format for logo replacement */
gboolean master_insert_area_format_has_videobasename;
gboolean master_insert_area_format_has_framenumber;
@@ -372,7 +394,7 @@ typedef struct GapStoryRenderMaskDefElem /* nick: maskdef_elem */
GapStoryRenderVidHandle *mask_vidhand;
-
+
struct GapStoryRenderMaskDefElem *next;
} GapStoryRenderMaskDefElem;
diff --git a/gap/gap_story_section_properties.c b/gap/gap_story_section_properties.c
index b51740c..4e97272 100644
--- a/gap/gap_story_section_properties.c
+++ b/gap/gap_story_section_properties.c
@@ -396,7 +396,7 @@ p_switch_to_section(GapStbSecpropWidget *spw, GapStorySection *target_section)
/* make the section the active section
* make the section the active section, refresh the sections combo box
- * (this implicite triggers thumbnail rendering)
+ * (this implicitly triggers thumbnail rendering)
*/
gap_story_dlg_spw_section_refresh(spw, target_section);
diff --git a/gap/gap_story_syntax.c b/gap/gap_story_syntax.c
index 2936b02..d1d5312 100644
--- a/gap/gap_story_syntax.c
+++ b/gap/gap_story_syntax.c
@@ -280,6 +280,10 @@ p_create_syntax_list(void)
,"decoder"
,NULL
);
+ p_add_keyword(GAP_STBKEY_VID_MASTER_INSERT_ALPHA
+ ,"format"
+ ,NULL
+ );
p_add_keyword(GAP_STBKEY_VID_MASTER_INSERT_AREA
,"format"
,NULL
@@ -307,6 +311,7 @@ p_create_syntax_list(void)
,"mask_disable"
,"macsteps"
,"macaccel"
+ ,"colormask_file"
,NULL
);
p_add_keyword(GAP_STBKEY_VID_PLAY_BLACKSECTION
@@ -325,6 +330,7 @@ p_create_syntax_list(void)
,"mask_disable"
,"macsteps"
,"macaccel"
+ ,"colormask_file"
,NULL
);
p_add_keyword(GAP_STBKEY_VID_PLAY_MOVIE
@@ -347,6 +353,7 @@ p_create_syntax_list(void)
,"mask_disable"
,"macsteps"
,"macaccel"
+ ,"colormask_file"
,NULL
);
p_add_keyword(GAP_STBKEY_VID_PLAY_FRAMES
@@ -366,6 +373,7 @@ p_create_syntax_list(void)
,"mask_disable"
,"macsteps"
,"macaccel"
+ ,"colormask_file"
,NULL
);
p_add_keyword(GAP_STBKEY_VID_PLAY_ANIMIMAGE
@@ -384,6 +392,7 @@ p_create_syntax_list(void)
,"mask_disable"
,"macsteps"
,"macaccel"
+ ,"colormask_file"
,NULL
);
@@ -399,6 +408,7 @@ p_create_syntax_list(void)
,"mask_disable"
,"macsteps"
,"macaccel"
+ ,"colormask_file"
,NULL
);
p_add_keyword(GAP_STBKEY_VID_PLAY_COLOR
@@ -416,6 +426,7 @@ p_create_syntax_list(void)
,"mask_disable"
,"macsteps"
,"macaccel"
+ ,"colormask_file"
,NULL
);
p_add_keyword(GAP_STBKEY_VID_SILENCE
@@ -424,6 +435,14 @@ p_create_syntax_list(void)
,"wait_until_sec"
,NULL
);
+ p_add_keyword(GAP_STBKEY_VID_ROTATE
+ ,"track"
+ ,"rotate_from"
+ ,"rotate_to"
+ ,"nframes"
+ ,"accel"
+ ,NULL
+ );
p_add_keyword(GAP_STBKEY_VID_OPACITY
,"track"
,"opacity_from"
diff --git a/gap/gap_story_syntax.h b/gap/gap_story_syntax.h
index 6d72074..44a630d 100644
--- a/gap/gap_story_syntax.h
+++ b/gap/gap_story_syntax.h
@@ -43,6 +43,7 @@
#define GAP_STBKEY_VID_MASTER_FRAME_ASPECT "VID_MASTER_FRAME_ASPECT"
#define GAP_STBKEY_VID_MASTER_LAYERSTACK "VID_MASTER_LAYERSTACK"
#define GAP_STBKEY_VID_PREFERRED_DECODER "VID_PREFERRED_DECODER"
+#define GAP_STBKEY_VID_MASTER_INSERT_ALPHA "VID_MASTER_INSERT_ALPHA"
#define GAP_STBKEY_VID_MASTER_INSERT_AREA "VID_MASTER_INSERT_AREA"
#define GAP_STBKEY_MAIN_SECTION "MAIN_SECTION"
@@ -56,6 +57,7 @@
#define GAP_STBKEY_VID_PLAY_IMAGE "VID_PLAY_IMAGE"
#define GAP_STBKEY_VID_PLAY_COLOR "VID_PLAY_COLOR"
#define GAP_STBKEY_VID_SILENCE "VID_SILENCE"
+#define GAP_STBKEY_VID_ROTATE "VID_ROTATE"
#define GAP_STBKEY_VID_OPACITY "VID_OPACITY"
#define GAP_STBKEY_VID_ZOOM_X "VID_ZOOM_X"
#define GAP_STBKEY_VID_ZOOM_Y "VID_ZOOM_Y"
diff --git a/gap/gap_story_vthumb.c b/gap/gap_story_vthumb.c
index 58b1d0a..ef9a24f 100644
--- a/gap/gap_story_vthumb.c
+++ b/gap/gap_story_vthumb.c
@@ -839,7 +839,7 @@ p_story_vthumb_elem_fetch(GapStbMainGlobalParams *sgpp
{
if(sgpp->vthumb_prefetch_in_progress != GAP_VTHUMB_PREFETCH_NOT_ACTIVE)
{
- /* at this point an implicite cancel of video thumbnail prefetch
+ /* at this point an implicit cancel of video thumbnail prefetch
* is detected.
*/
if(gap_debug)
@@ -902,7 +902,7 @@ p_story_vthumb_elem_fetch(GapStbMainGlobalParams *sgpp
return(NULL);
}
- /* Videothumbnail not known yet,
+ /* Video thumbnail not known yet,
* we try to create it now
*/
switch(velem->vt_type)
@@ -1001,7 +1001,7 @@ gap_story_vthumb_elem_fetch(GapStbMainGlobalParams *sgpp
/* ------------------------------
* gap_story_vthumb_fetch_thdata
* ------------------------------
- * RETURN a copy of the videothumbnail data
+ * RETURN a copy of the video thumbnail data
* or NULL if fetch was not successful
* the caller is responsible to g_free the returned data
* after usage.
@@ -1052,7 +1052,7 @@ gap_story_vthumb_fetch_thdata(GapStbMainGlobalParams *sgpp
/* --------------------------------------
* gap_story_vthumb_fetch_thdata_no_store
* --------------------------------------
- * RETURN a pointer of the videothumbnail data
+ * RETURN a pointer of the video thumbnail data
* or thumbnail data read from file (indicated by file_read_flag = TRUE)
* the caller must g_free the returned data if file_read_flag = TRUE
* but MUST NOT g_free the returned data if file_read_flag = FALSE
@@ -1124,20 +1124,20 @@ gap_story_vthumb_fetch_thdata_no_store(GapStbMainGlobalParams *sgpp
if(sgpp->vthumb_prefetch_in_progress != GAP_VTHUMB_PREFETCH_NOT_ACTIVE)
{
- /* at this point an implicite cancel of video thumbnail prefetch
+ /* at this point an implicit cancel of video thumbnail prefetch
* is detected.
- * - one option is to render default icon, and restert the prefetch
+ * - one option is to render default icon, and restart the prefetch
* via GAP_VTHUMB_PREFETCH_RESTART_REQUEST
* (because the storyboard may have changed since prefetch was started
* note that prefetch will be very quick for all clips where vthumb is already present
* from the cancelled previous prefetch cycle)
- * - an other (not implemented) option is to cancel prefetch and implicite turn off auto_vthumb mode
+ * - an other (not implemented) option is to cancel prefetch and implicitly turn off auto_vthumb mode
*/
sgpp->vthumb_prefetch_in_progress = GAP_VTHUMB_PREFETCH_RESTART_REQUEST;
return (NULL);
}
- /* Videothumbnail not known yet,
+ /* Video thumbnail not known yet,
* we try to create it now
* (but dont add it tho global vthumb list)
*/
diff --git a/gap/gap_thumbnail.c b/gap/gap_thumbnail.c
index 4e0f905..4f28efe 100644
--- a/gap/gap_thumbnail.c
+++ b/gap/gap_thumbnail.c
@@ -163,13 +163,13 @@ p_gap_filename_to_uri(const char *filename)
* and save the pixbuf for the new filename_dst.
*
* The following Information Tags in the resulting copy are updated
- * to match filename_dst. (this is done implicite by the gimp_thumbnail_* procedures)
+ * to match filename_dst. (this is done implicitly by the gimp_thumbnail_* procedures)
* tEXt::Thumb::URI
* tEXt::Thumb::MTime
* tEXt::Description
* tEXt::Thumb::Size
*
- * The following Tags are copied explicite from the source thumbnail:
+ * The following Tags are copied explicit from the source thumbnail:
* tEXt::Thumb::Image::Width
* tEXt::Thumb::Image::Height
* tEXt::Thumb::X-GIMP::Type
diff --git a/gap/gap_vex_dialog.c b/gap/gap_vex_dialog.c
index 4924e21..d2674c9 100644
--- a/gap/gap_vex_dialog.c
+++ b/gap/gap_vex_dialog.c
@@ -2533,7 +2533,7 @@ gap_vex_dlg_create_mw__main_window (GapVexMainGlobalParams *gpp)
gtk_box_pack_start (GTK_BOX (hbox2), checkbutton, TRUE, TRUE, 0);
gimp_help_set_help_data (checkbutton
, _("On: extract grayscale mask (generated by bluebox)\n"
- "Off: extract color frames 1.1")
+ "Off: extract color frames 1:1")
, NULL);
g_signal_connect (G_OBJECT (checkbutton), "toggled",
G_CALLBACK (on_mw__checkbutton_graymask_toggled),
diff --git a/gap/gap_video_index_creator.c b/gap/gap_video_index_creator.c
index ac0cdb3..92b01db 100644
--- a/gap/gap_video_index_creator.c
+++ b/gap/gap_video_index_creator.c
@@ -932,7 +932,7 @@ p_vindex_dialog(VindexValues *val_ptr)
"and no critical timecode steps are detected in the probereads so far.\n"
"\nWARNING: positioning via native seek may not work exact in case critical "
"timecode steps were not detected in the probereads.")
- , N_("Create video index. Requires unconditional full scann of all frames."
+ , N_("Create video index. Requires unconditional full scan of all frames."
"Native seek is enabled only in case all timecodes are OK.")
};
@@ -991,7 +991,7 @@ p_vindex_dialog(VindexValues *val_ptr)
ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_FLT); ii_percentage_smart_mode = ii;
argv[ii].constraint = TRUE;
argv[ii].label_txt = _("Percentage:");
- argv[ii].help_txt = _("stop scann after percentage reached and no unplausible timecode was detected so far (only relevant in smart mode)");
+ argv[ii].help_txt = _("stop scan after percentage reached and no unplausible timecode was detected so far (only relevant in smart mode)");
argv[ii].flt_min = 1.0;
argv[ii].flt_max = (gint)100.0;
argv[ii].flt_step = 1.0;
@@ -1444,7 +1444,7 @@ p_create_progress_window(GapVideoIndexCreatorProgressParams *vipp)
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
gimp_help_set_help_data (button
- , _("Cancel video access if in progress and disable automatic videothumbnails")
+ , _("Cancel video access if in progress and disable automatic video thumbnails")
, NULL);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (p_cancel_button_cb),
diff --git a/gap/gap_wr_color_balance.c b/gap/gap_wr_color_balance.c
new file mode 100644
index 0000000..5fea509
--- /dev/null
+++ b/gap/gap_wr_color_balance.c
@@ -0,0 +1,749 @@
+/* gap_wr_color_balance.c
+ * 2010.08.23 hof (Wolfgang Hofer)
+ *
+ * Wrapper Plugin for GIMP Hue Saturation tool
+ *
+ * Warning: This is just a QUICK HACK to enable
+ * Animated Filterapply in conjunction with the
+ * GIMP Hue Saturation Tool.
+ *
+ * It provides a primitive Dialog Interface where you
+ * can call the Hue Saturation Tool
+ *
+ * Further it has an Interface to 'Run_with_last_values'
+ * and an Iterator Procedure.
+ * (This enables the 'Animated Filter Call' from
+ * the GAP's Menu Filters->Filter all Layers)
+ *
+ */
+/* 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
+ * (2004/01/15) v1.3.24a hof: adapted for gimp-1.3.x and gtk+2.2 API
+ * (2002/10/27) v1.03 hof: - appear in menu (for filtermacro)
+ * (2002/01/01) v1.02 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"
+
+/* Defines */
+#define PLUG_IN_NAME "plug-in-wr-colorbalance"
+#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 "Wrapper call for GIMP Color Balance Tool"
+
+#define PLUG_IN_ITER_NAME "plug-in-wr-colorbalance-Iterator"
+#define PLUG_IN_DATA_ITER_FROM "plug-in-wr-colorbalance-ITER-FROM"
+#define PLUG_IN_DATA_ITER_TO "plug-in-wr-colorbalance-ITER-TO"
+#define PLUG_IN_HELP_ID "plug-in-wr-colorbalance"
+
+
+
+typedef struct
+{
+ gint32 transfer_mode;
+ gboolean preserve_lum;
+ gdouble cyan_red;
+ gdouble magenta_green;
+ gdouble yellow_blue;
+} wr_color_balance_val_t;
+
+
+typedef struct _WrDialog WrDialog;
+
+struct _WrDialog
+{
+ gint run;
+ gint show_progress;
+ GtkWidget *shell;
+ GtkWidget *radio_highlights;
+ GtkWidget *radio_midtones;
+ GtkWidget *radio_shadows;
+ wr_color_balance_val_t *vals;
+};
+
+
+WrDialog *do_dialog (wr_color_balance_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
+ * --------------
+ */
+
+
+
+/*
+ * Delta Calculations for the iterator
+ */
+static void
+p_delta_gdouble (gdouble *val,
+ gdouble val_from,
+ gdouble val_to,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ gdouble delta;
+
+ if(total_steps < 1) return;
+
+ delta = ((gdouble)(val_to - val_from) / (gdouble)total_steps) * ((gdouble)total_steps - current_step);
+ *val = val_from + delta;
+}
+
+static void
+p_delta_gint32 (gint32 *val,
+ gint32 val_from,
+ gint32 val_to,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ gdouble delta;
+
+ if(total_steps < 1) return;
+
+ delta = ((gdouble)(val_to - val_from) / (gdouble)total_steps) * ((gdouble)total_steps - current_step);
+ *val = val_from + delta;
+}
+
+
+static void
+p_run_color_balance_tool(gint32 drawable_id, wr_color_balance_val_t *cuvals)
+{
+ gboolean success;
+
+ if(gap_debug)
+ {
+ printf("p_run_color_balance_tool: drawable_id :%d\n", (int)drawable_id);
+ printf("p_run_color_balance_tool: transfer_mode:%d\n", (int)cuvals->transfer_mode);
+ printf("p_run_color_balance_tool: preserve_lum:%d\n", (int)cuvals->preserve_lum);
+ printf("p_run_color_balance_tool: cyan_red:%f\n", (float)cuvals->cyan_red);
+ printf("p_run_color_balance_tool: magenta_green:%f\n", (float)cuvals->magenta_green);
+ printf("p_run_color_balance_tool: yellow_blue:%f\n", (float)cuvals->yellow_blue);
+ }
+
+ success = gimp_color_balance(drawable_id
+ , cuvals->transfer_mode /* GimpTransferMode */
+ , cuvals->preserve_lum
+ , cuvals->cyan_red
+ , cuvals->magenta_green
+ , cuvals->yellow_blue
+ );
+
+
+}
+
+MAIN ()
+
+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_INT32, "transfer_mode", "Transfer mode { SHADOWS (0), MIDTONES (1), HIGHLIGHTS (2) }"},
+ { GIMP_PDB_INT32, "preserve_lum", "Preserve luminosity values at each pixel (TRUE or FALSE)"},
+ { GIMP_PDB_FLOAT, "cyan_red", "Cyan-Red color balance (-100 <= cyan-red <= 100)"},
+ { GIMP_PDB_FLOAT, "magenta_green", "Magenta-Green color balance (-100 <= magenta-green <= 100)"},
+ { GIMP_PDB_FLOAT, "yellow_blue", "Yellow-Blue color balance (-100 <= yellow-blue <= 100)"},
+ };
+ static int nargs = sizeof(args) / sizeof(args[0]);
+
+ static GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_DRAWABLE, "the_drawable", "the handled drawable" }
+ };
+ static int nreturn_vals = sizeof(return_vals) / sizeof(return_vals[0]);
+
+
+ static GimpParamDef args_iter[] =
+ {
+ {GIMP_PDB_INT32, "run_mode", "non-interactive"},
+ {GIMP_PDB_INT32, "total_steps", "total number of steps (# of layers-1 to apply the related plug-in)"},
+ {GIMP_PDB_FLOAT, "current_step", "current (for linear iterations this is the layerstack position, otherwise some value inbetween)"},
+ {GIMP_PDB_INT32, "len_struct", "length of stored data structure with id is equal to the plug_in proc_name"},
+ };
+ static int nargs_iter = sizeof(args_iter) / sizeof(args_iter[0]);
+
+ static GimpParamDef *return_iter = NULL;
+ static int nreturn_iter = 0;
+
+ gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+ /* the actual installation of the bend plugin */
+ gimp_install_procedure (PLUG_IN_NAME,
+ PLUG_IN_DESCRIPTION,
+ "This Plugin is a wrapper to call the GIMP Color Balance Tool (gimp_color_balance)"
+ " it has a simplified Dialog (without preview) where you can enter the parameters"
+ " this wrapper is useful for animated filtercalls and provides "
+ " a PDB interface that runs in GIMP_RUN_WITH_LAST_VALUES mode"
+ " and also provides an Iterator Procedure for animated calls"
+ ,
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ GAP_VERSION_WITH_DATE,
+ N_("Color Balance..."),
+ PLUG_IN_IMAGE_TYPES,
+ GIMP_PLUGIN,
+ nargs,
+ nreturn_vals,
+ args,
+ return_vals);
+
+
+ /* the installation of the Iterator extension for the bend plugin */
+ gimp_install_procedure (PLUG_IN_ITER_NAME,
+ "This extension calculates the modified values for one iterationstep for the call of gimp_color_balance",
+ "",
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ GAP_VERSION_WITH_DATE,
+ NULL, /* do not appear in menus */
+ NULL,
+ GIMP_PLUGIN,
+ nargs_iter, nreturn_iter,
+ args_iter, return_iter);
+
+ {
+ /* Menu names */
+ const char *menupath_image_video_layer_colors = N_("<Image>/Video/Layer/Colors/");
+
+ gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_video_layer_colors);
+ }
+}
+
+
+
+static void
+run(const gchar *name
+ , gint nparams
+ , const GimpParam *param
+ , gint *nreturn_vals
+ , GimpParam **return_vals)
+{
+ wr_color_balance_val_t l_cuvals;
+ WrDialog *wcd = NULL;
+
+ 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;
+
+ /* 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[2];
+
+
+ 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;
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ /* the Iterator Stuff */
+ if (strcmp (name, PLUG_IN_ITER_NAME) == 0)
+ {
+ gint32 len_struct;
+ gint32 total_steps;
+ gdouble current_step;
+ wr_color_balance_val_t cval; /* current values while iterating */
+ wr_color_balance_val_t cval_from, cval_to; /* start and end values */
+
+ /* Iterator procedure for animated calls is usually called from
+ * "plug_in_gap_layers_run_animfilter"
+ * (always run noninteractive)
+ */
+ if ((run_mode == GIMP_RUN_NONINTERACTIVE) && (nparams == 4))
+ {
+ total_steps = param[1].data.d_int32;
+ current_step = param[2].data.d_float;
+ len_struct = param[3].data.d_int32;
+
+ if(len_struct == sizeof(cval))
+ {
+ /* get _FROM and _TO data,
+ * This data was stored by plug_in_gap_layers_run_animfilter
+ */
+ gimp_get_data(PLUG_IN_DATA_ITER_FROM, &cval_from);
+ gimp_get_data(PLUG_IN_DATA_ITER_TO, &cval_to);
+ memcpy(&cval, &cval_from, sizeof(cval));
+
+ p_delta_gint32 (&cval.transfer_mode, cval_from.transfer_mode, cval_to.transfer_mode, total_steps, current_step);
+ p_delta_gdouble(&cval.cyan_red, cval_from.cyan_red, cval_to.cyan_red, total_steps, current_step);
+ p_delta_gdouble(&cval.magenta_green, cval_from.magenta_green, cval_to.magenta_green, total_steps, current_step);
+ p_delta_gdouble(&cval.yellow_blue, cval_from.yellow_blue, cval_to.yellow_blue, total_steps, current_step);
+
+ gimp_set_data(PLUG_IN_NAME, &cval, sizeof(cval));
+ }
+ else status = GIMP_PDB_CALLING_ERROR;
+ }
+ else status = GIMP_PDB_CALLING_ERROR;
+
+ values[0].data.d_status = status;
+ return;
+ }
+
+
+
+ /* get image and drawable */
+ l_image_id = param[1].data.d_int32;
+ l_drawable_id = param[2].data.d_drawable;
+
+ if(status == GIMP_PDB_SUCCESS)
+ {
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Initial values */
+ l_cuvals.transfer_mode = 0;
+ l_cuvals.preserve_lum = FALSE;
+ l_cuvals.cyan_red = 0;
+ l_cuvals.magenta_green = 0;
+ l_cuvals.yellow_blue = 0;
+
+ /* Get information from the dialog */
+ wcd = do_dialog(&l_cuvals);
+ wcd->show_progress = TRUE;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* check to see if invoked with the correct number of parameters */
+ if (nparams >= 7)
+ {
+ wcd = g_malloc (sizeof (WrDialog));
+ wcd->run = TRUE;
+ wcd->show_progress = FALSE;
+
+ l_cuvals.transfer_mode = param[3].data.d_int32;
+ l_cuvals.preserve_lum = param[4].data.d_int32;
+ l_cuvals.cyan_red = param[5].data.d_float;
+ l_cuvals.magenta_green = param[6].data.d_float;
+ l_cuvals.yellow_blue = param[7].data.d_float;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ wcd = g_malloc (sizeof (WrDialog));
+ wcd->run = TRUE;
+ wcd->show_progress = TRUE;
+ /* 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)
+ {
+ gimp_image_undo_group_start (l_image_id);
+ p_run_color_balance_tool(l_drawable_id, &l_cuvals);
+ l_handled_drawable_id = l_drawable_id;
+ gimp_image_undo_group_end (l_image_id);
+
+ /* 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 */
+ }
+
+ /* 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 = l_handled_drawable_id; /* return the id of handled layer */
+
+} /* end run */
+
+
+/*
+ * DIALOG and callback stuff
+ */
+
+
+static void
+radio_callback(GtkWidget *wgt, gpointer user_data)
+{
+ WrDialog *wcd;
+
+ if(gap_debug) printf("radio_callback: START\n");
+ wcd = (WrDialog*)user_data;
+ if(wcd != NULL)
+ {
+ if(wcd->vals != NULL)
+ {
+ if(wgt == wcd->radio_highlights) { wcd->vals->transfer_mode = 2; }
+ if(wgt == wcd->radio_midtones) { wcd->vals->transfer_mode = 1; }
+ if(wgt == wcd->radio_shadows) { wcd->vals->transfer_mode = 0; }
+
+ }
+ }
+}
+
+
+/* --------------------------------------
+ * on_gboolean_button_update
+ * --------------------------------------
+ */
+static void
+on_gboolean_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ 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));
+}
+
+
+/* ---------------------------------
+ * wr_color_balance_response
+ * ---------------------------------
+ */
+static void
+wr_color_balance_response (GtkWidget *widget,
+ gint response_id,
+ WrDialog *wcd)
+{
+ GtkWidget *dialog;
+
+ switch (response_id)
+ {
+ 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 wr_color_balance_response */
+
+
+WrDialog *
+do_dialog (wr_color_balance_val_t *cuvals)
+{
+ WrDialog *wcd;
+ GtkWidget *vbox;
+
+ GtkWidget *dialog1;
+ GtkWidget *dialog_vbox1;
+ GtkWidget *frame1;
+ GtkWidget *hbox1;
+ GtkWidget *vbox1;
+ GtkWidget *label1;
+ GSList *vbox1_group = NULL;
+ GtkWidget *radiobutton1;
+ GtkWidget *radiobutton2;
+ GtkWidget *radiobutton3;
+ GtkWidget *table1;
+ GtkWidget *label2;
+ GtkWidget *label3;
+ GtkWidget *label4;
+ GtkObject *spinbutton_cyan_red_adj;
+ GtkWidget *spinbutton_cyan_red;
+ GtkObject *spinbutton_magenta_green_adj;
+ GtkWidget *spinbutton_magenta_green;
+ GtkObject *spinbutton_yellow_blue_adj;
+ GtkWidget *spinbutton_yellow_blue;
+ GtkWidget *dialog_action_area1;
+ GtkWidget *checkbutton;
+ gint row;
+
+
+ /* Init UI */
+ gimp_ui_init ("wr_color_balance", FALSE);
+
+
+ /* The dialog1 */
+ wcd = g_malloc (sizeof (WrDialog));
+ wcd->run = FALSE;
+ wcd->vals = cuvals;
+
+ /* The dialog1 and main vbox */
+ dialog1 = gimp_dialog_new (_("Color-Balance"), "color_balance_wrapper",
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_HELP_ID,
+
+ 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 (wr_color_balance_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 (_("Select Range to Adjust"));
+
+ 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);
+
+ hbox1 = gtk_hbox_new (FALSE, 0);
+
+ gtk_widget_show (hbox1);
+ gtk_container_add (GTK_CONTAINER (frame1), hbox1);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox1), 4);
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+
+ gtk_widget_show (vbox1);
+ gtk_box_pack_start (GTK_BOX (hbox1), vbox1, TRUE, TRUE, 0);
+
+
+ /* Transfer Mode the radio buttons */
+ radiobutton1 = gtk_radio_button_new_with_label (vbox1_group, _("Shadows"));
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1));
+ gtk_widget_show (radiobutton1);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton1, FALSE, FALSE, 0);
+
+ radiobutton2 = gtk_radio_button_new_with_label (vbox1_group, _("Midtones"));
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2));
+ gtk_widget_show (radiobutton2);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton2, FALSE, FALSE, 0);
+
+ radiobutton3 = gtk_radio_button_new_with_label (vbox1_group, _("Highlights"));
+ vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3));
+ gtk_widget_show (radiobutton3);
+ gtk_box_pack_start (GTK_BOX (vbox1), radiobutton3, FALSE, FALSE, 0);
+
+
+
+ /* the frame */
+ frame1 = gimp_frame_new (_("Adjust Color Levels"));
+
+ 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);
+
+
+
+ /* table1 for spinbuttons */
+
+ row = 0;
+
+ label2 = gtk_label_new (_("Cyan/Red:"));
+ gtk_widget_show (label2);
+ gtk_table_attach (GTK_TABLE (table1), label2, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5);
+ spinbutton_cyan_red_adj = gtk_adjustment_new (0, -100, 100, 1, 10, 0);
+ spinbutton_cyan_red = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_cyan_red_adj), 1, 0);
+ gtk_widget_show (spinbutton_cyan_red);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton_cyan_red, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ row++;
+
+ label3 = gtk_label_new (_("Magenta/Green:"));
+ gtk_widget_show (label3);
+ gtk_table_attach (GTK_TABLE (table1), label3, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label3), 0, 0.5);
+
+
+ spinbutton_magenta_green_adj = gtk_adjustment_new (0, -100, 100, 1, 10, 0);
+ spinbutton_magenta_green = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_magenta_green_adj), 1, 0);
+ gtk_widget_show (spinbutton_magenta_green);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton_magenta_green, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+
+ row++;
+
+ label4 = gtk_label_new (_("Yellow/Blue:"));
+ gtk_widget_show (label4);
+ gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
+
+
+ spinbutton_yellow_blue_adj = gtk_adjustment_new (0, -100, 100, 1, 10, 0);
+ spinbutton_yellow_blue = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_yellow_blue_adj), 1, 0);
+ gtk_widget_show (spinbutton_yellow_blue);
+ gtk_table_attach (GTK_TABLE (table1), spinbutton_yellow_blue, 1, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+
+ row++;
+
+ checkbutton = gtk_check_button_new_with_label ( _("Preserve luminosity"));
+ gtk_widget_show (checkbutton);
+ gtk_table_attach( GTK_TABLE(table1), checkbutton, 0, 2, row, row+1,
+ GTK_FILL, 0, 0, 0 );
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->preserve_lum);
+ g_signal_connect (checkbutton, "toggled",
+ G_CALLBACK (on_gboolean_button_update),
+ &cuvals->preserve_lum);
+
+
+
+
+ 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);
+
+
+
+
+
+ wcd->radio_shadows = radiobutton1;
+ wcd->radio_midtones = radiobutton2;
+ wcd->radio_highlights = radiobutton3;
+
+
+ /* signals */
+ g_signal_connect (G_OBJECT (wcd->radio_shadows), "clicked", G_CALLBACK (radio_callback), wcd);
+ g_signal_connect (G_OBJECT (wcd->radio_midtones), "clicked", G_CALLBACK (radio_callback), wcd);
+ g_signal_connect (G_OBJECT (wcd->radio_highlights), "clicked", G_CALLBACK (radio_callback), wcd);
+
+ g_signal_connect (G_OBJECT (spinbutton_cyan_red_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->cyan_red);
+ g_signal_connect (G_OBJECT (spinbutton_magenta_green_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->magenta_green);
+ g_signal_connect (G_OBJECT (spinbutton_yellow_blue_adj), "value_changed", G_CALLBACK (gimp_double_adjustment_update), &cuvals->yellow_blue);
+
+
+ gtk_widget_show (dialog1);
+
+ gtk_main ();
+ gdk_flush ();
+
+ return wcd;
+}
diff --git a/libgapbase/gap_val_file.c b/libgapbase/gap_val_file.c
index c3e3a2b..811fdbd 100644
--- a/libgapbase/gap_val_file.c
+++ b/libgapbase/gap_val_file.c
@@ -238,6 +238,19 @@ p_write_keylist_value(FILE *fp, GapValKeyList *keyptr, const char *term_str)
switch(keyptr->dataype)
{
+ case GAP_VAL_GINT:
+ {
+ gint *val_ptr;
+
+ val_ptr = (gint *)keyptr->val_ptr;
+ fprintf(fp, "%s%d%s %s\n"
+ , keyptr->keyword /* "(keyword " */
+ , (int)*val_ptr /* value */
+ , term_ptr
+ , keyptr->comment
+ );
+ }
+ break;
case GAP_VAL_GINT32:
{
gint32 *val_ptr;
@@ -527,6 +540,14 @@ gap_val_scann_filevalues(GapValKeyList *keylist, const char *filename)
l_cnt_keys++;
switch(keyptr->dataype)
{
+ case GAP_VAL_GINT:
+ {
+ gint *val_ptr;
+
+ val_ptr = (gint *)keyptr->val_ptr;
+ *val_ptr = atol(&txf_ptr->line[l_len]);
+ }
+ break;
case GAP_VAL_GINT32:
{
gint32 *val_ptr;
@@ -641,7 +662,7 @@ gap_val_scann_filevalues(GapValKeyList *keylist, const char *filename)
break;
}
} /* end for keylist loop */
- } /* end for text lines scann loop */
+ } /* end for text lines scan loop */
if(txf_ptr_root)
{
gap_val_free_textfile_lines(txf_ptr_root);
diff --git a/libgapbase/gap_val_file.h b/libgapbase/gap_val_file.h
index e21a2bd..cb2d3e1 100644
--- a/libgapbase/gap_val_file.h
+++ b/libgapbase/gap_val_file.h
@@ -46,6 +46,7 @@ typedef enum
, GAP_VAL_STRING
, GAP_VAL_GBOOLEAN
, GAP_VAL_G32BOOLEAN
+, GAP_VAL_GINT
} GapValDataType;
diff --git a/libgapvidapi/gap_vid_api_ffmpeg.c b/libgapvidapi/gap_vid_api_ffmpeg.c
index b408a27..d38fe96 100644
--- a/libgapvidapi/gap_vid_api_ffmpeg.c
+++ b/libgapvidapi/gap_vid_api_ffmpeg.c
@@ -3,6 +3,7 @@
* GAP Video read API implementation of libavformat/lbavcodec (also known as FFMPEG)
* based wrappers to read various videofile formats
*
+ * 2010.07.31 update to support both ffmpeg-0.5 and ffmpeg-0.6
* 2007.11.04 update to ffmpeg svn snapshot 2007.10.31
* bugfix: selftest sometimes did not detect variable timecodes.
* 2007.04.04 update to ffmpeg svn snapshot 2007.04.04,
@@ -11,7 +12,7 @@
* 2007.03.12 update to ffmpeg svn snapshot 2007.03.12,
* 2005.02.05 update to ffmpeg-0.4.9 (basically works now)
* 2004.10.24 workaround initial frameread and reopen for detection of yuv_buff_pix_fmt
- * of the active codec. (works only after the 1.st frame was read)
+ * of the active codec. (works only after the 1.st frame was read)
* 2004.04.12 vindex bugfix seek high framnumbers sometimes used wrong (last index)
* 2004.03.07 vindex
* 2004.02.29 hof fptr_progress_callback
@@ -27,12 +28,37 @@
*/
#ifdef ENABLE_GVA_LIBAVFORMAT
#include "avformat.h"
+#include "swscale.h"
#include "stdlib.h"
#include "gap_val_file.h"
#include "gap_base.h"
#include "audioconvert.h"
#include "imgconvert.h"
+
+/* start ffmpeg 0.5 / 0.6 support */
+#if LIBAVCODEC_VERSION_MAJOR < 52
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#if LIBAVCODEC_VERSION_MAJOR == 52
+#if LIBAVCODEC_VERSION_MINOR <= 20
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#endif
+
+
+#ifdef GAP_USES_OLD_FFMPEG_0_5
+/* defines to use older ffmpeg-0.5 compatible types */
+#define AVMEDIA_TYPE_UNKNOWN CODEC_TYPE_UNKNOWN
+#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
+#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO
+#define AV_PKT_FLAG_KEY PKT_FLAG_KEY
+#endif
+
+/* end ffmpeg 0.5 / 0.6 support */
+
+
+
#define READSTEPS_PROBE_TIMECODE 32
#define DEFAULT_NAT_SEEK_GOPSIZE 24
@@ -155,10 +181,12 @@ typedef struct t_GVA_ffmpeg
gboolean prefere_native_seek; /* prefere native seek if both vindex and native seek available */
gboolean all_timecodes_verified;
gboolean critical_timecodesteps_found;
-
+
unsigned char * chunk_ptr;
gint32 chunk_len;
-
+
+ struct SwsContext *img_convert_ctx;
+
} t_GVA_ffmpeg;
@@ -355,14 +383,15 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
handle->key_frame_detection_works = FALSE; /* assume a Codec with non working detection */
handle->chunk_len = 0;
handle->chunk_ptr = NULL;
-
-
+ handle->img_convert_ctx = NULL; /* context for img_convert via sws_scale */
+
+
p_reset_proberead_results(handle);
handle->prefere_native_seek = FALSE;
handle->all_timecodes_verified = FALSE;
handle->critical_timecodesteps_found = FALSE;
-
+
/* open for the VIDEO part */
if(FALSE == p_ff_open_input(filename, gvahand, handle, TRUE))
{
@@ -473,7 +502,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
);
- /*
+ /*
* vid_codec_context->pix_fmt may change later, and is sometimes
* not recognized until the 1.st frame has been decoded.
* therefore the follwing avpicture_fill is just a 1.st guess
@@ -484,7 +513,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
* - Quicktime codec (codec_id == 10) uses PIX_FMT_YUV422P ( Olympus C4040Z Digicam MJPEG .mov files)
*/
handle->yuv_buff_pix_fmt = handle->vid_codec_context->pix_fmt; /* PIX_FMT_YUV420P */
-
+
avpicture_fill(handle->picture_yuv
,handle->yuv_buffer
,handle->yuv_buff_pix_fmt
@@ -497,9 +526,9 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
gvahand->decoder_handle = (void *)handle;
- /* workaround: initial frameread and reopen for detection of yuv_buff_pix_fmt */
+ /* workaround: initial frameread and reopen for detection of yuv_buff_pix_fmt */
{
- if(gap_debug)
+ if(gap_debug)
{
printf("INITIAL call p_wrapper_ffmpeg_get_next_frame\n");
}
@@ -510,7 +539,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
p_ffmpeg_vid_reopen_read(handle, gvahand);
gvahand->current_frame_nr = 0;
gvahand->current_seek_nr = 1;
-
+
}
@@ -546,7 +575,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
}
}
- if(gap_debug) //####XXXXXXXXXXXXXx
+ if(gap_debug)
{
if(gvahand->vindex)
{
@@ -567,7 +596,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
{
printf("IDX: p_wrapper_ffmpeg_open_read: vindex is NULL\n");
}
-
+
printf("p_wrapper_ffmpeg_open_read: END, OK\n");
}
@@ -623,6 +652,12 @@ p_wrapper_ffmpeg_close(t_GVA_Handle *gvahand)
/* ??????? do not attempt to free handle->aud_codec_context (it points to &handle->aud_stream.codec) */
}
+ if (handle->img_convert_ctx != NULL)
+ {
+ sws_freeContext(handle->img_convert_ctx);
+ handle->img_convert_ctx = NULL;
+ }
+
/* Close a media file (but not its codecs) */
if(gap_debug) printf("p_wrapper_ffmpeg_close: av_close_input_file\n");
if(handle->vid_input_context) av_close_input_file(handle->vid_input_context);
@@ -670,7 +705,7 @@ p_wrapper_ffmpeg_get_codec_name(t_GVA_Handle *gvahand
return(g_strdup(handle->vcodec->name));
}
}
-
+
if (codec_type == GVA_AUDIO_CODEC)
{
if(handle->acodec)
@@ -715,7 +750,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle *gvahand
/* illegal frame_nr (first frame starts at Nr 1 */
return(GVA_RET_ERROR);
}
-
+
handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
if(handle == NULL)
{
@@ -726,7 +761,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle *gvahand
if (frame_nr != gvahand->current_seek_nr)
{
gdouble pos;
-
+
pos = frame_nr;
l_rc = p_wrapper_ffmpeg_seek_frame(gvahand, pos, GVA_UPOS_FRAMES);
if (l_rc != GVA_RET_OK)
@@ -735,7 +770,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle *gvahand
}
}
-
+
l_rc = p_private_ffmpeg_get_next_frame(gvahand, TRUE);
if(handle->chunk_len > max_size)
{
@@ -750,11 +785,11 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle *gvahand
printf("CALLING ERROR p_wrapper_ffmpeg_get_video_chunk fetch raw frame failed, chunk_ptr is NULL\n");
return(GVA_RET_ERROR);
}
-
+
if(gap_debug)
{
char *vcodec_name;
-
+
vcodec_name = "NULL";
if(handle->vcodec)
{
@@ -771,7 +806,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle *gvahand
,vcodec_name
);
}
-
+
*size = handle->chunk_len;
memcpy(chunk, handle->chunk_ptr, handle->chunk_len);
return (l_rc);
@@ -839,7 +874,7 @@ p_url_ftell(ByteIOContext *s)
,(int)s->buf_end
);
}
-
+
/* ignore inplausible staart offsets */
bufferStartOffsetInStream = 0;
@@ -913,7 +948,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
l_curr_url_offset = -1;
l_url_seek_nr = gvahand->current_seek_nr;
l_key_frame_detected = FALSE;
-
+
while(l_got_picture == 0)
{
int l_pktlen;
@@ -950,7 +985,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
);
}
l_record_url_offset = -1;
-
+
if (!gvahand->all_frames_counted)
{
gvahand->total_frames = gvahand->current_frame_nr;
@@ -963,13 +998,13 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
*/
gvahand->total_aud_samples = (gint32)GVA_frame_2_samples(gvahand->framerate, gvahand->samplerate, gvahand->total_frames);
}
-
+
p_clear_inbuf_and_vid_packet(handle);
l_rc = 1;
break;
}
-
+
/* if (gap_debug) printf("vid_stream:%d pkt.stream_index #%d, pkt.size: %d\n", handle->vid_stream_index, handle->vid_pkt.stream_index, handle->vid_pkt.size); */
@@ -989,7 +1024,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
l_pkt_size = handle->vid_pkt.size;
handle->frame_len += l_pkt_size;
-
+
if(gap_debug)
{ printf("using Packet data:%d size:%d handle->frame_len:%d dts:%lld pts:%lld AV_NOPTS_VALUE:%lld\n"
,(int)handle->vid_pkt.data
@@ -999,7 +1034,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
, handle->vid_pkt.pts
, AV_NOPTS_VALUE
);
- }
+ }
}
if (gap_debug)
@@ -1046,12 +1081,22 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
* bytes used. If no frame could be decompressed, *got_picture_ptr is
* zero. Otherwise, it is non zero.
*/
+#ifdef GAP_USES_OLD_FFMPEG_0_5
+ // printf("USING avcodec_decode_video FFMPEG-0.5\n");
l_len = avcodec_decode_video(handle->vid_codec_context /* AVCodecContext * */
,&handle->big_picture_yuv
,&l_got_picture
,handle->inbuf_ptr
,handle->inbuf_len
);
+#else
+ // printf("USING avcodec_decode_video2 FFMPEG-0.6\n");
+ l_len = avcodec_decode_video2(handle->vid_codec_context /* AVCodecContext * */
+ ,&handle->big_picture_yuv
+ ,&l_got_picture
+ ,&handle->vid_pkt
+ );
+#endif
/*if (gap_debug) printf("after avcodec_decode_video: l_len:%d got_pic:%d\n", (int)l_len, (int)l_got_picture);*/
@@ -1074,7 +1119,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
,gvahand->width
,gvahand->height
);
- }
+ }
if(l_len < 0)
{
@@ -1090,8 +1135,8 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
l_frame_len = (l_len & 0xffff);
handle->frame_len = (l_len - l_pkt_size);
l_record_url_offset = l_curr_url_offset;
- l_key_frame_detected = ((handle->vid_pkt.flags & PKT_FLAG_KEY) != 0);
-
+ l_key_frame_detected = ((handle->vid_pkt.flags & AV_PKT_FLAG_KEY) != 0);
+
if(gap_debug)
{
/* log information that could be relevant for redesign of VINDEX creation */
@@ -1099,7 +1144,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
, (int)gvahand->current_seek_nr
, handle->prev_url_offset[MAX_PREV_OFFSET -1]
, l_record_url_offset
- , (handle->vid_pkt.flags & PKT_FLAG_KEY)
+ , (handle->vid_pkt.flags & AV_PKT_FLAG_KEY)
, handle->vid_pkt.dts
,(int)l_frame_len
,(int)l_len
@@ -1125,9 +1170,9 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
/* 1.st frame_len may contain headers (size may be too large) */
handle->max_frame_len = MAX(handle->max_frame_len, l_len);
}
-
+
handle->got_frame_length16 = l_frame_len;
-
+
if(gap_debug)
{
printf("GOT PIC: current_seek_nr:%06d l_frame_len:%d got_pic:%d key:%d\n"
@@ -1140,20 +1185,43 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
if(handle->dummy_read == FALSE)
{
- if (gap_debug) printf("p_wrapper_ffmpeg_get_next_frame: before img_convert\n");
-
- /* avcodec.img_convert convert among pixel formats */
+ /* reuse the img_convert_ctx or create a new one (in case ctx is NULL or params have changed)
+ *
+ */
+ handle->img_convert_ctx = sws_getCachedContext(handle->img_convert_ctx
+ , gvahand->width
+ , gvahand->height
+ , handle->yuv_buff_pix_fmt /* src pixelformat */
+ , gvahand->width
+ , gvahand->height
+ , PIX_FMT_RGB24 /* dest pixelformat */
+ , SWS_BICUBIC /* int sws_flags */
+ , NULL, NULL, NULL
+ );
+ if (handle->img_convert_ctx == NULL)
+ {
+ printf("Cannot initialize the conversion context (sws_getCachedContext delivered NULL pointer)\n");
+ exit(1);
+ }
+ if (gap_debug)
+ {
+ printf("p_wrapper_ffmpeg_get_next_frame: before img_convert via sws_scale\n");
+ }
+ sws_scale(handle->img_convert_ctx
+ , handle->picture_yuv->data /* srcSlice */
+ , handle->picture_yuv->linesize /* srcStride the array containing the strides for each plane */
+ , 0 /* srcSliceY starting at 0 */
+ , gvahand->height /* srcSliceH the height of the source slice */
+ , handle->picture_rgb->data /* dst */
+ , handle->picture_rgb->linesize /* dstStride the array containing the strides for each plane */
+ );
- l_rc = img_convert(handle->picture_rgb
- , PIX_FMT_RGB24 /* dst */
- ,handle->picture_yuv
- ,handle->yuv_buff_pix_fmt /* src */
- ,gvahand->width
- ,gvahand->height
- );
+ if (gap_debug)
+ {
+ printf("p_wrapper_ffmpeg_get_next_frame: after img_convert via sws_scale\n");
+ }
- /* if (gap_debug) printf("p_wrapper_ffmpeg_get_next_frame: after img_convert l_rc:%d\n", (int)l_rc); */
}
#define GVA_LOW_GOP_LIMIT 24
@@ -1188,10 +1256,10 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
if(gap_debug)
{
- if( (gvahand->vindex)
+ if( (gvahand->vindex)
&& (handle->capture_offset)
)
- {
+ {
printf("KeyFrame: %d works:%d\n"
,(int)handle->big_picture_yuv.key_frame
,(int)handle->key_frame_detection_works
@@ -1199,7 +1267,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
}
}
- if((gvahand->vindex)
+ if((gvahand->vindex)
&& (handle->capture_offset)
&& (l_potential_index_frame)
)
@@ -1220,13 +1288,13 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
/* printf("GUESS_GOP: prev_key_seek_nr:%d l_url_seek_nr:%d\n"
* , (int)handle->prev_key_seek_nr
- * , (int)l_url_seek_nr);
+ * , (int)l_url_seek_nr);
*/
/* try to guess the GOP size (but use at least GOPSIZE of 4 frames) */
if(l_url_seek_nr > handle->prev_key_seek_nr)
{
gdouble l_gopsize;
-
+
l_gopsize = l_url_seek_nr - handle->prev_key_seek_nr;
if(handle->guess_gop_size == 0)
{
@@ -1268,10 +1336,10 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
}
handle->prev_url_offset[0] = l_record_url_offset;
}
-
+
gvahand->current_frame_nr = gvahand->current_seek_nr;
gvahand->current_seek_nr++;
-
+
if (gap_debug) printf("p_wrapper_ffmpeg_get_next_frame: current_frame_nr :%d\n"
, (int)gvahand->current_frame_nr);
@@ -1306,7 +1374,7 @@ t_GVA_RetCode
p_wrapper_ffmpeg_seek_frame(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
{
t_GVA_ffmpeg* master_handle;
-
+
master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
if (master_handle->timecode_proberead_done != TRUE)
@@ -1354,7 +1422,7 @@ static void p_reset_proberead_results(t_GVA_ffmpeg* handle)
handle->eof_timecode = AV_NOPTS_VALUE;
handle->video_libavformat_seek_gopsize
= gap_base_get_gimprc_int_value("video-libavformat-seek-gopsize", DEFAULT_NAT_SEEK_GOPSIZE, 0, MAX_NAT_SEEK_GOPSIZE);
-} /* end p_reset_proberead_results */
+} /* end p_reset_proberead_results */
/* -------------------------------------
@@ -1379,8 +1447,8 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
t_GVA_ffmpeg* master_handle;
int gap_debug_local;
gboolean persitent_analyse_available;
-
-
+
+
gap_debug_local = gap_debug;
#ifdef GAP_DEBUG_FF_NATIVE_SEEK
@@ -1396,7 +1464,7 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
persitent_analyse_available =
p_get_video_analyse_results(gvahand);
}
-
+
if(gap_debug_local)
{
printf("\n\n\n\n\n\n\n\n\n\n\n\n");
@@ -1413,10 +1481,10 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
);
}
}
-
+
if(persitent_analyse_available != TRUE)
{
- bck_total_frames = gvahand->total_frames;
+ bck_total_frames = gvahand->total_frames;
gvahand->cancel_operation = FALSE; /* reset cancel flag */
p_ffmpeg_vid_reopen_read(master_handle, gvahand);
@@ -1425,8 +1493,8 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
gvahand->current_seek_nr = 1;
p_clear_inbuf_and_vid_packet(master_handle);
- /* limit native seek attempts to only one loop while running the selftest
- */
+ /* limit native seek attempts to only one loop while running the selftest
+ */
master_handle->max_tries_native_seek = 1;
@@ -1546,7 +1614,7 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
* and furthermore native timecode based seek operations are not reliable
* and must be disabled.
*
- * it seems that libavformat sets the current timecode
+ * it seems that libavformat sets the current timecode
* to timecode after last frame
* when seeking to positions after EOF (and in case of errors after native seek).
* This (undocumented ?) effect enables quick detection of the toatal_frames.
@@ -1564,18 +1632,18 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
t_GVA_RetCode l_rc;
t_GVA_ffmpeg* master_handle;
int gap_debug_local;
-
+
gap_debug_local = gap_debug;
#ifdef GAP_DEBUG_FF_NATIVE_SEEK
gap_debug_local = 1;
#endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-
+
master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
master_handle->eof_timecode = AV_NOPTS_VALUE;
totalGuess = p_guess_total_frames(gvahand);
lastFrameNr = totalGuess;
-
+
totalPlausible = MAX((totalGuess / 4), 256);
if(gap_debug_local)
{
@@ -1585,7 +1653,7 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
}
/* first loop trys to seek after EOF until seek fails
- * seek to 150 % until seek fails
+ * seek to 150 % until seek fails
*/
totalDetected = 0;
frameNrLo = 1;
@@ -1620,7 +1688,7 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
while(1==1)
{
gdouble delta;
-
+
delta = (gdouble)(frameNrHi - frameNrLo) / 2.0;
frameNrMed = frameNrLo + delta;
if(gap_debug_local)
@@ -1656,7 +1724,7 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
}
}
-
+
if(totalDetected >= totalPlausible)
{
l_rc = p_seek_native_timcode_based(gvahand, totalDetected);
@@ -1681,8 +1749,8 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
gvahand->total_frames = lastFrameNr;
gvahand->all_frames_counted = FALSE;
}
-
-
+
+
if(gap_debug_local)
{
printf("END: p_detect_total_frames_via_native_seek plausiblity limit:%d detected:%d total_frames:%d \n"
@@ -1703,8 +1771,8 @@ static void
p_inc_native_timecode_seek_failcount(t_GVA_Handle *gvahand)
{
t_GVA_ffmpeg* master_handle;
-
- master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
+
+ master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
if (master_handle->native_timecode_seek_failcount <= 0)
{
printf("\n##############################\n");
@@ -1714,7 +1782,7 @@ p_inc_native_timecode_seek_failcount(t_GVA_Handle *gvahand)
printf("# positioning to exact framenumber is not guaranteed\n");
printf("# you may create a videoindex to enable positioning to exact frame numbers\n");
printf("##############################\n");
-
+
}
master_handle->native_timecode_seek_failcount++;
} /* end p_inc_native_timecode_seek_failcount */
@@ -1766,17 +1834,17 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
* The outer Loop (l_nloops) repeats the sync loop attempt by using a lower seek offset
* from the previous video index entry and uses more read operations (l_synctries)
* trying to find the target frame. (This is necessary because byte oriented
- * seek operations do not work exact and may position the video stream
+ * seek operations do not work exact and may position the video stream
* AFTER the target frame.
- *
+ *
* The postioning to last Group of pictures is done
* by sequential read (l_readsteps). it should contain at least one keyframe (I-frame)
* that enables clean decoding of the following P and B frames
*
- * video index table inner (synctries) and outer loop
+ * video index table inner (synctries) and outer loop
*
* |-------------------------------------------|
- * | [10] seek_nr:000100 offset: len16: DTS: |
+ * | [10] seek_nr:000100 offset: len16: DTS: |
* |-------------------------------------------|
*
*
@@ -1784,7 +1852,7 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
* to seek the video stream
* to exact target position)
* |-------------------------------------------| l_nloops = 2
- * | [11] seek_nr:000110 offset: len16: DTS: | | 1 l_synctries
+ * | [11] seek_nr:000110 offset: len16: DTS: | | 1 l_synctries
* |-------------------------------------------| | 2
* | 3
* | 4
@@ -1793,19 +1861,19 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
* | 7
* | 8
* | 9
- * |-------------------------------------------| l_nloops = 1 | 10
+ * |-------------------------------------------| l_nloops = 1 | 10
* l_idx_target ------> | [12] seek_nr:000120 offset: len16: DTS: | | 1 l_synctries | 11
* |-------------------------------------------| | 2 | 12
- * | 3 | 13
- * | 4 | 14
- * | 5 | 15
- * | 6 | 16
- * | 7 | 17
- * | 8 | 18
+ * | 3 | 13
+ * | 4 | 14
+ * | 5 | 15
+ * | 6 | 16
+ * | 7 | 17
+ * | 8 | 18
* |-------------------------------------------| | 9 | 19
- * l_idx_too_far -----> | [13] seek_nr:000130 offset: len16: DTS: | V 10 V 20
+ * l_idx_too_far -----> | [13] seek_nr:000130 offset: len16: DTS: | V 10 V 20
* |-------------------------------------------|
- *
+ *
*
* - the fallback method emulates
* seek by pos-1 sequential read ops.
@@ -1814,7 +1882,7 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
*
* seek operation to the start (the first few frames) always use sequential read
*
- *
+ *
*/
#define GVA_IDX_SYNC_STEPSIZE 1
#define GVA_FRAME_NEAR_START 24
@@ -1833,7 +1901,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
vindex = gvahand->vindex;
-
+
gvahand->cancel_operation = FALSE; /* reset cancel flag */
switch(pos_unit)
@@ -1852,7 +1920,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
l_frame_pos = (gint32)pos;
break;
}
-
+
if(gvahand->all_frames_counted)
{
/* in case total number of frames is exactly known
@@ -1860,7 +1928,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
*/
l_frame_pos = MIN(l_frame_pos, gvahand->total_frames);
}
-
+
gvahand->percentage_done = 0.0;
if(gap_debug)
@@ -1874,7 +1942,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
, gvahand->filename
);
}
-
+
handle->dummy_read = TRUE;
l_url_frame_pos = 0;
@@ -1920,7 +1988,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
}
}
-
+
/* check conditions and try native timecode based seek if conditions permit native seek.
*/
if((l_vindex_is_usable != TRUE) || (handle->prefere_native_seek))
@@ -1941,9 +2009,9 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
}
return (l_rc_rd);
}
- }
- }
-
+ }
+ }
+
}
/* use video index where possible */
@@ -1960,8 +2028,8 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
, (int)vindex->tabsize_used
);
}
-
-
+
+
if(vindex->tabsize_used < 1)
{
printf("SEEK: index is >>> EMPTY <<<: vindex->tabsize_used: %d cannot use index!\n", (int)vindex->tabsize_used);
@@ -1987,13 +2055,13 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
l_idx = vindex->tabsize_used -1;
}
- /* lower index adjustment (dont start seek with positions much smaller than l_frame_pos) */
+ /* lower index adjustment (dont start seek with positions much smaller than l_frame_pos) */
if(l_idx > 0)
{
gint32 l_pos_min;
-
+
l_pos_min = l_frame_pos - (MAX(64, (2* vindex->stepsize)));
-
+
while(vindex->ofs_tab[l_idx].seek_nr < l_pos_min)
{
l_idx++;
@@ -2016,8 +2084,8 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
}
}
}
-
- /* upper index adjustment (dont start seek with positions already graeter than l_frame_pos)*/
+
+ /* upper index adjustment (dont start seek with positions already graeter than l_frame_pos)*/
if(l_idx > 0)
{
while(vindex->ofs_tab[l_idx].seek_nr >= l_frame_pos)
@@ -2038,7 +2106,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
}
}
}
-
+
if(l_idx > 0)
{
gint32 l_idx_target;
@@ -2055,12 +2123,12 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
{
l_idx_too_far = -1; /* mark as invalid */
}
-
+
l_readsteps = l_frame_pos - gvahand->current_seek_nr;
if((l_readsteps > vindex->stepsize)
|| (l_readsteps < 0))
{
- gint32 l_nloops; /* number of seek attempts with different recorded videoindex entries
+ gint32 l_nloops; /* number of seek attempts with different recorded videoindex entries
* (outer loop)
*/
gint32 l_synctries; /* number of frame read attempts until
@@ -2073,7 +2141,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
while((l_idx >= 0) && (l_nloops < 12))
{
gboolean l_dts_timecode_usable;
-
+
l_dts_timecode_usable = FALSE;
if((vindex->ofs_tab[l_idx].timecode_dts != AV_NOPTS_VALUE)
@@ -2083,7 +2151,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
{
l_dts_timecode_usable = TRUE;
}
-
+
if(gap_debug)
{
printf("SEEK: USING_INDEX: ofs_tab[%d]: ofs64: %lld seek_nr:%d flen:%d chk:%d dts:%lld DTS_USABLE:%d NLOOPS:%d\n"
@@ -2097,7 +2165,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
, (int)l_nloops
);
}
-
+
l_synctries = 4 + MAX_PREV_OFFSET + (vindex->stepsize * GVA_IDX_SYNC_STEPSIZE * l_nloops);
/* SYNC READ loop
@@ -2130,7 +2198,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
{
printf("USING DTS timecode based av_seek_frame\n");
}
-
+
/* timecode based seek */
ret_av_seek_frame = av_seek_frame(handle->vid_input_context, handle->vid_stream_index
, vindex->ofs_tab[l_idx].timecode_dts, AVSEEK_FLAG_BACKWARD);
@@ -2145,7 +2213,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
/* byte position based seek AVSEEK_FLAG_BACKWARD AVSEEK_FLAG_ANY*/
ret_av_seek_frame = av_seek_frame(handle->vid_input_context, handle->vid_stream_index
,seek_pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_BYTE);
-
+
}
p_clear_inbuf_and_vid_packet(handle);
@@ -2154,7 +2222,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
while(l_synctries > 0)
{
gboolean l_potentialCanditate;
-
+
l_potentialCanditate = FALSE;
l_rc_rd = p_wrapper_ffmpeg_get_next_frame(gvahand);
l_summary_read_ops++;
@@ -2173,10 +2241,10 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
// printf("SEEK_SYNC_LOOP: idx_frame_len:%d got_frame_length16:%d Key:%d dts:%lld\n"
// , (int)vindex->ofs_tab[l_idx_target].frame_length
// , (int)handle->got_frame_length16
-// , (int)handle->big_picture_yuv.key_frame
+// , (int)handle->big_picture_yuv.key_frame
// , handle->vid_pkt.dts
// );
-
+
if(l_dts_timecode_usable == TRUE)
{
/* handling for index format with usable timecode
@@ -2198,7 +2266,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
l_potentialCanditate = TRUE;
}
}
-
+
if (l_potentialCanditate == TRUE)
{
guint16 l_checksum;
@@ -2275,11 +2343,11 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
}
}
}
-
+
l_synctries--;
} /* end while */
-
-
+
+
if(l_target_found != TRUE)
{
/* index sync search failed, try with previous index in the table */
@@ -2310,7 +2378,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
{
printf("SEEK: sync loop success remaining l_readsteps: %d\n", (int)l_readsteps);
}
-
+
break; /* OK, escape from outer loop, we are now at read position of the wanted keyframe */
}
@@ -2331,7 +2399,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
}
l_nloops++;
} /* end outer loop (l_nloops) */
-
+
if (l_target_found != TRUE)
{
p_ffmpeg_vid_reopen_read(handle, gvahand);
@@ -2341,22 +2409,22 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
{
printf("SEEK: target frame not found, fallback to slow sequential read from begin\n");
}
-
+
}
-
+
}
}
}
}
-
-
+
+
/* fallback to sequential read */
if(l_url_frame_pos == 0)
{
/* emulate seek with N steps of read ops */
if((l_frame_pos < gvahand->current_seek_nr) /* position backwards ? */
- || (gvahand->current_seek_nr < 0)) /* inplausible current position ? */
+ || (gvahand->current_seek_nr < 0)) /* inplausible current position ? */
{
/* reopen and read from start */
p_ffmpeg_vid_reopen_read(handle, gvahand);
@@ -2427,11 +2495,11 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
* p_seek_native_timcode_based
* ------------------------------
* positioning of the video stream before target_frame position.
- *
+ *
* the native timecode based seek operates on
* timecode steps sample pattern that will be measured
* by some initial probereads (p_probe_timecode_offset).
- *
+ *
* Most Videofiles have either constant timing (stepsize equal for all frames)
* or use a repeating pattern of individual stepsizes within a group of pictures.
* In both cases this procedure can calculate the correct timecode that
@@ -2475,13 +2543,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
#define NATIVE_SEEK_EXTRA_PREREADS_NEAR_END 22
handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
-
+
l_retcode = GVA_RET_ERROR;
if (handle->video_libavformat_seek_gopsize <= 0)
{
return(l_retcode);
}
-
+
l_retry = TRUE;
for(l_tries=0; l_tries < handle->max_tries_native_seek; l_tries++)
{
@@ -2502,10 +2570,10 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
,(int)l_pre_read_frames
,(int)gvahand->total_frames
,(int)target_frame
- );
+ );
}
#endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-
+
if (target_frame > l_pre_read_frames)
@@ -2515,8 +2583,8 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
int64_t l_seek_timecode;
gint32 seek_fnr;
gint32 seek_fnr2;
-
-
+
+
ret_av_seek_frame = -1;
p_probe_timecode_offset(gvahand);
@@ -2535,7 +2603,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
*/
seek_fnr = target_frame - l_pre_read_frames;
l_seek_timecode = p_frame_nr_to_timecode(handle, seek_fnr);
-
+
seek_fnr2 = seek_fnr;
for(jj=0; jj < 2; jj++)
{
@@ -2552,7 +2620,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
/* SEEK to preread position */
gvahand->current_frame_nr = 0;
gvahand->current_seek_nr = 1;
-
+
if(1==1 /*l_tries == 0*/)
{
/* seek variant based on stream specific dts timecode
@@ -2560,7 +2628,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
/* seek is done some (configurable number) frames less than wanted frame position.
* it shall deliver position of nearest key frame
* after seek, have to read/decode some few frames until
- * wanted frame is reached
+ * wanted frame is reached
* in some rare cases (some older mpeg files) libavformat positioning may take us
* to non-keyframes (even with AVSEEK_FLAG_BACKWARD set).
* therefore read/decode some frames (l_pre_read_frames) shall ensure that
@@ -2575,8 +2643,8 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
*/
int64_t target_pts;
gdouble secs;
-
- target_pts =
+
+ target_pts =
av_rescale_q(l_seek_timecode, handle->vid_stream->time_base, AV_TIME_BASE_Q);
ret_av_seek_frame = av_seek_frame(handle->vid_input_context, -1
@@ -2591,7 +2659,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
#endif /* GAP_DEBUG_FF_NATIVE_SEEK */
}
-
+
#ifdef GAP_DEBUG_FF_NATIVE_SEEK
if(1==0)
{
@@ -2601,13 +2669,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
gdouble secs;
gdouble dframes;
gint32 seek_fnr;
-
+
seek_fnr = target_frame - l_pre_read_frames;
secs = (gdouble)seek_fnr / gvahand->framerate;
target_pts = secs * AV_TIME_BASE;
microsecs_start = handle->vid_input_context->start_time * handle->vid_stream->time_base.num
/ handle->vid_stream->time_base.den;
-
+
seek_target= av_rescale_q(target_pts, AV_TIME_BASE_Q, handle->vid_stream->time_base);
dframes = handle->vid_input_context->duration / (gdouble)AV_TIME_BASE * gvahand->framerate;
@@ -2619,7 +2687,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
, l_seek_timecode
, (int)seek_fnr
);
-
+
}
@@ -2634,12 +2702,12 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
);
}
#endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-
- /* clear inbuf and packet
+
+ /* clear inbuf and packet
*(must remove old content after seek to enable clean reads)
*/
p_clear_inbuf_and_vid_packet(handle);
-
+
if (ret_av_seek_frame < 0)
{
printf("** ERROR AVFORMAT: av_seek_frame failed retcode: %d \n"
@@ -2657,7 +2725,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
int64_t l_prev_timecode;
int64_t l_pprev_timecode;
gchar l_debug_msg[500];
-
+
l_wanted_timecode = p_frame_nr_to_timecode(handle, target_frame -1);
l_want_2_timecode = p_frame_nr_to_timecode(handle, target_frame -2);
l_want_3_timecode = p_frame_nr_to_timecode(handle, target_frame -3);
@@ -2669,10 +2737,10 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
l_prev_timecode = AV_NOPTS_VALUE;
l_pprev_timecode = AV_NOPTS_VALUE;
l_read_err_count = 0;
-
+
l_progress_step = 1.0 / MAX((gdouble)l_pre_read_frames, 1.0);
gvahand->percentage_done = 0.0;
- /* native seek has (hopefully) set stream position to the nearest keyframe
+ /* native seek has (hopefully) set stream position to the nearest keyframe
* (before the wanted frame)
* ??? For some video formats the native seek may set position AFTER the wanted
* frame in such a case it is worth to start a retry with larger
@@ -2748,7 +2816,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
fflush(stdout);
}
#endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-
+
l_rc_timeread = p_wrapper_ffmpeg_get_next_frame(gvahand);
l_curr_timecode = handle->vid_pkt.dts;
@@ -2771,13 +2839,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
if(l_rc_timeread != GVA_RET_OK)
{
gint32 l_fnr;
-
+
l_fnr = p_timecode_to_frame_nr(handle, l_curr_timecode);
if(handle->eof_timecode == AV_NOPTS_VALUE)
{
handle->eof_timecode = l_curr_timecode;
}
-
+
l_read_err_count++;
#ifdef GAP_DEBUG_FF_NATIVE_SEEK
@@ -2793,7 +2861,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
, (int)gvahand->all_frames_counted
);
#endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-
+
if (1==1) /* (l_read_err_count > 1) */
{
l_retry = FALSE;
@@ -2804,7 +2872,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
l_prev_timecode = AV_NOPTS_VALUE;
}
break;
-
+
}
l_debug_msg[0] = "\n";
@@ -2834,19 +2902,19 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
int64_t l_prev_absdiff;
int64_t l_pprev_absdiff;
-
+
match_count = 0;
must_match = 2;
debug_match_string[0] = '-';
debug_match_string[1] = '-';
debug_match_string[2] = '-';
debug_match_string[3] = '\0';
-
+
l_curr_absdiff = llabs(l_curr_timecode - l_wanted_timecode);
l_prev_absdiff = llabs(l_prev_timecode - l_want_2_timecode);
l_pprev_absdiff = llabs(l_pprev_timecode - l_want_3_timecode);
-
-
+
+
if (l_curr_absdiff <= l_very_small_absdiff)
{
debug_match_string[0] = 'x';
@@ -2871,13 +2939,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
fflush(stdout);
}
#endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-
+
if(match_count >= must_match)
{
/* set attributes of gvahand to refelect wanted seek position reached. */
gvahand->current_seek_nr = target_frame;
gvahand->current_frame_nr = target_frame -1;
-
+
/* if this is not the first try ( l_tries > 0)
* increase video_libavformat_seek_gopsize
* to the value where we had success with.
@@ -2908,7 +2976,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
return (GVA_RET_OK);
}
} /* END of tolerant check if wanted timecode is reached */
-
+
/* timecode overflow check */
if ((l_curr_timecode != AV_NOPTS_VALUE)
@@ -2943,15 +3011,15 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
l_retcode = GVA_RET_ERROR;
break;
}
-
+
l_pprev_timecode = l_prev_timecode;
l_prev_timecode = l_curr_timecode;
-
-
+
+
}
}
-
+
if (l_retry != TRUE)
{
printf("** AVFORMAT: native seek failed (seek behind EOF, Seek Error, or Read error)\n");
@@ -2973,7 +3041,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
} /* end for tries loop */
return (l_retcode);
-
+
} /* end p_seek_native_timcode_based */
@@ -3144,7 +3212,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
{
gvahand->vindex = GVA_load_videoindex(gvahand->filename, gvahand->vid_track, GVA_FFMPEG_DECODER_NAME);
}
-
+
if(gvahand->vindex)
{
if((gvahand->vindex->total_frames > 0)
@@ -3207,7 +3275,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
/* TIMCODE LOG INIT */
FILE *fp_timecode_log;
fp_timecode_log = p_init_timecode_log(gvahand);
-
+
if(vindex)
{
handle->capture_offset = TRUE;
@@ -3242,8 +3310,8 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
}
gvahand->frame_counter++;
l_total_frames = MAX(l_total_frames, gvahand->frame_counter);
-
-
+
+
/* TIMCODE check and LOG WHILE COUNTING */
p_timecode_check_and_log(fp_timecode_log, gvahand->frame_counter, handle, master_handle, gvahand);
@@ -3281,15 +3349,15 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
/* close the extra handle (that was opened for counting only) */
p_wrapper_ffmpeg_close(copy_gvahand);
-
-
+
+
/* TIMCODE LOG FINALLY */
if (fp_timecode_log)
{
fclose(fp_timecode_log);
}
-
-
+
+
}
g_free(copy_gvahand);
@@ -3307,7 +3375,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
if(gvahand->cancel_operation)
{
/* because of cancel the total_frames is still unknown
- * (vindex->total_frames == 0 is the indicator for incomplete index)
+ * (vindex->total_frames == 0 is the indicator for incomplete index)
*/
vindex->total_frames = 0;
}
@@ -3317,7 +3385,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
printf("p_wrapper_ffmpeg_count_frames: before GVA_save_videoindex\n");
}
GVA_save_videoindex(vindex, gvahand->filename, GVA_FFMPEG_DECODER_NAME);
-
+
if(gap_debug)
{
@@ -3329,14 +3397,14 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
{
gvahand->total_frames = gvahand->frame_counter;
gvahand->all_frames_counted = TRUE;
-
+
master_handle->all_timecodes_verified = TRUE;
}
else
{
gvahand->total_frames = MAX(gvahand->total_frames, gvahand->frame_counter);
gvahand->cancel_operation = FALSE; /* reset cancel flag */
-
+
if(gap_debug)
{
printf("p_wrapper_ffmpeg_count_frames: RETURN ERROR\n");
@@ -3405,7 +3473,7 @@ t_GVA_SeekSupport
p_wrapper_ffmpeg_seek_support(t_GVA_Handle *gvahand)
{
t_GVA_ffmpeg* master_handle;
-
+
master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
if ((master_handle->timecode_proberead_done != TRUE)
@@ -3418,14 +3486,14 @@ p_wrapper_ffmpeg_seek_support(t_GVA_Handle *gvahand)
{
return(GVA_SEEKSUPP_NONE);
}
-
+
if (master_handle->video_libavformat_seek_gopsize > 0)
{
return(GVA_SEEKSUPP_NATIVE);
}
return(GVA_SEEKSUPP_VINDEX);
-
+
} /* end p_wrapper_ffmpeg_seek_support */
@@ -3591,7 +3659,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
switch(acc->codec_type)
{
- case CODEC_TYPE_AUDIO:
+ case AVMEDIA_TYPE_AUDIO:
gvahand->atracks++; /* count all audiostraems as audiotrack */
if(gap_debug) printf("\nInput Audio channels: %d\n", acc->channels);
if((gvahand->atracks == gvahand->aud_track)
@@ -3609,14 +3677,14 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
gvahand->samplerate = acc->sample_rate;
}
break;
- case CODEC_TYPE_VIDEO:
+ case AVMEDIA_TYPE_VIDEO:
gvahand->vtracks++; /* count all videostraems as videotrack */
if((gvahand->vtracks == gvahand->vid_track)
|| (gvahand->vtracks == 1))
{
gdouble containerFramerate;
gdouble codecFramerate;
-
+
if(vid_open)
{
handle->vid_stream_index = ii;
@@ -3630,7 +3698,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
/* Aspect Ratio handling */
p_set_aspect_ratio(gvahand, handle);
-
+
rfps = ic->streams[ii]->r_frame_rate.num;
rfps_base = ic->streams[ii]->r_frame_rate.den;
@@ -3642,7 +3710,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
acc->idct_algo= 0;
/*
* if(acc->codec->capabilities & CODEC_CAP_TRUNCATED)
- * acc->flags|= CODEC_FLAG_TRUNCATED;
+ * acc->flags|= CODEC_FLAG_TRUNCATED;
*/
if(/*acc->codec_id==CODEC_ID_MPEG4 || */acc->codec_id==CODEC_ID_MPEG1VIDEO)
{
@@ -3650,7 +3718,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
}
- /* attempt to get the framerate */
+ /* attempt to get the framerate */
containerFramerate = 1.0;
codecFramerate = 1.0;
@@ -3658,7 +3726,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
if (acc->time_base.num != 0)
{
codecFramerate = (gdouble)acc->time_base.den / (gdouble)acc->time_base.num;
-
+
if (acc->ticks_per_frame > 1)
{
/* videos with interlaced frames typically deliver framerates at double speed
@@ -3680,13 +3748,13 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
/* in case the framerate values of the codec and container are different
* pick the smaller value (but only if it is greater than plausibility threshold of 10 fps)
*/
-
+
if((containerFramerate > 10.0)
&& (containerFramerate < codecFramerate))
{
gvahand->framerate = containerFramerate;
}
-
+
printf("\nSeems stream %d codec frame rate differs from container frame rate: %2.2f (%d/%d) -> %2.2f (%d/%d) ticksPerFrame:%d\n"
,ii
, (float)acc->time_base.den / (float)acc->time_base.num
@@ -3795,9 +3863,9 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle, gb
* -------------------------
* set the gvahand->aspect_ratio variable to aspect ratio
* typical values are
- * 1.777777 For 16:9 video
+ * 1.777777 For 16:9 video
* 1.333333 For 4:3 video
- *
+ *
* Note that the gvahand->aspect_ratio variable describes the ratio
* for the full image (and not the ratio of a single pixel)
*
@@ -3807,7 +3875,7 @@ static void
p_set_aspect_ratio(t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle)
{
AVStream *video_st;
-
+
video_st = handle->vid_stream;
gvahand->aspect_ratio = 0.0;
@@ -3824,7 +3892,7 @@ p_set_aspect_ratio(t_GVA_Handle *gvahand, t_GVA_ffmpeg* handle)
{
gvahand->aspect_ratio = 1.0;
}
-
+
if(gvahand->aspect_ratio != 0.0)
{
gvahand->aspect_ratio *= (gdouble)video_st->codec->width / video_st->codec->height;
@@ -3998,7 +4066,7 @@ p_ffmpeg_aud_reopen_read(t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand)
* p_audio_convert_to_s16
* ------------------------------
*
- * convert audio samples in specified data_length at handle->output_samples_ptr
+ * convert audio samples in specified data_length at handle->output_samples_ptr
* to signed 16 bit little endian audio format.
* The original content of handle->output_samples_ptr is replaced by the conversion result
* that may also change data length.
@@ -4020,7 +4088,7 @@ p_audio_convert_to_s16(t_GVA_ffmpeg *handle
{
handle->reformat_ctx = av_audio_convert_alloc(SAMPLE_FMT_S16, 1,
dec->sample_fmt, 1, NULL, 0);
- if (!handle->reformat_ctx)
+ if (!handle->reformat_ctx)
{
printf("ERROR: Cannot convert %s sample format to %s sample format\n",
avcodec_get_sample_fmt_name(dec->sample_fmt),
@@ -4028,15 +4096,15 @@ p_audio_convert_to_s16(t_GVA_ffmpeg *handle
return (converted_data_size);
}
}
-
+
audio_convert_in_buffer = g_malloc(data_size);
- if (audio_convert_in_buffer != NULL)
+ if (audio_convert_in_buffer != NULL)
{
/* copy samples in a new buffer to be used as input for the conversion */
memcpy(audio_convert_in_buffer, handle->output_samples_ptr, data_size);
-
- /* convert and write converted samples back to handle->output_samples_ptr */
- if (handle->reformat_ctx)
+
+ /* convert and write converted samples back to handle->output_samples_ptr */
+ if (handle->reformat_ctx)
{
const void *ibuf[6]= {audio_convert_in_buffer};
void *obuf[6]= {handle->output_samples_ptr};
@@ -4336,12 +4404,21 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
* zero or negative.
*/
data_size = handle->samples_buffer_size;
+#ifdef GAP_USES_OLD_FFMPEG_0_5
l_len = avcodec_decode_audio2(handle->aud_codec_context /* AVCodecContext * */
,(int16_t *)handle->output_samples_ptr
,&data_size
,handle->abuf_ptr
,handle->abuf_len
);
+#else
+ l_len = avcodec_decode_audio3(handle->aud_codec_context /* AVCodecContext * */
+ ,(int16_t *)handle->output_samples_ptr
+ ,&data_size
+ ,&handle->aud_pkt
+ );
+#endif
+
if (gap_debug)
{
@@ -4484,7 +4561,7 @@ p_vindex_add_url_offest(t_GVA_Handle *gvahand
)
{
static gint64 s_last_seek_nr;
-
+
if(vindex->tabsize_used > 0)
{
if(seek_nr <= s_last_seek_nr)
@@ -4493,7 +4570,7 @@ p_vindex_add_url_offest(t_GVA_Handle *gvahand
}
}
s_last_seek_nr = seek_nr;
-
+
if(vindex->tabsize_used >= vindex->tabsize_allocated -1)
{
t_GVA_IndexElem *redim_ofs_tab;
@@ -4564,7 +4641,7 @@ p_gva_checksum(AVPicture *picture_yuv, gint height)
{
guint16 l_checksum;
gint ii;
-
+
l_checksum = 0;
for(ii=0;ii<4;ii++)
{
@@ -4572,7 +4649,7 @@ p_gva_checksum(AVPicture *picture_yuv, gint height)
{
guchar *buf;
gint row;
-
+
buf = picture_yuv->data[ii];
buf += picture_yuv->linesize[ii] / 2;
for(row=0; row < height; row+=2)
@@ -4601,7 +4678,7 @@ p_init_timecode_log(t_GVA_Handle *gvahand)
FILE *fp_timecode_log;
gint32 gimprc_timecode_log;
gchar *timecode_logfile_name;
-
+
gimprc_timecode_log
= gap_base_get_gimprc_int_value("video-libavformat-timecodelog", 0, 0, 1);
fp_timecode_log = NULL;
@@ -4634,7 +4711,7 @@ p_init_timecode_log(t_GVA_Handle *gvahand)
{
t_GVA_ffmpeg *master_handle;
master_handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
-
+
fprintf(fp_timecode_log, "# TIMECODE log for video: %s\n"
, gvahand->filename
);
@@ -4652,7 +4729,7 @@ p_init_timecode_log(t_GVA_Handle *gvahand)
}
g_free(timecode_logfile_name);
}
-
+
return (fp_timecode_log);
} /* end p_init_timecode_log */
@@ -4662,7 +4739,7 @@ p_init_timecode_log(t_GVA_Handle *gvahand)
* p_timecode_check_and_log
* ----------------------------------
* write log entry for current frame to timecode log file.
- * the master_handle is used read only
+ * the master_handle is used read only
* (for converting framenr to timecode based on
* proberead statistical data)
*/
@@ -4679,7 +4756,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
int64_t diff_dts;
expected_dts = p_frame_nr_to_timecode(master_handle, framenr);
-
+
if (abs(expected_dts - handle->vid_pkt.dts) > 10)
{
master_handle->critical_timecodesteps_found = TRUE;
@@ -4691,7 +4768,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
{
remark_ptr = ok_string;
}
-
+
if (fp_timecode_log == NULL)
{
return;
@@ -4714,7 +4791,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
, expected_dts - handle->vid_pkt.pts
, handle->vid_pkt.pts - handle->vid_pkt.dts
);
-
+
if (old_pts != AV_NOPTS_VALUE)
{
fprintf(fp_timecode_log, "; pts-oldpts:%lld"
@@ -4727,7 +4804,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
{
fprintf(fp_timecode_log, "; pts:NOPTS_VAL"
);
-
+
}
fprintf(fp_timecode_log, "%s\n"
@@ -4793,7 +4870,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
master_handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
analysefile_name = p_create_analysefile_name(gvahand);
-
+
/* current videofile timestamp */
master_handle->timestamp = gap_file_get_mtime(gvahand->filename);
master_handle->readsteps_probe_timecode = READSTEPS_PROBE_TIMECODE;
@@ -4808,7 +4885,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
if (master_handle->self_test_detected_seek_bug == TRUE)
{
fprintf(fp_analyse, "NONE");
- }
+ }
else if (master_handle->video_libavformat_seek_gopsize > 0)
{
fprintf(fp_analyse, "NATIVE");
@@ -4829,7 +4906,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
duration = master_handle->vid_input_context->duration;
duration3 =
av_rescale_q(duration, AV_TIME_BASE_Q, master_handle->vid_stream->time_base);
-
+
stt = master_handle->vid_input_context->start_time;
/* scale stat_time from global AV_TIME_BASE to stream specific timecode */
stt =
@@ -4856,7 +4933,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
fclose(fp_analyse);
keylist = gap_val_new_keylist();
-
+
/* setup key/value descriptions */
p_set_analysefile_master_keywords(keylist, gvahand, master_handle->count_timecode_steps);
@@ -4894,13 +4971,13 @@ p_get_video_analyse_results(t_GVA_Handle *gvahand)
gint32 curr_mtime;
gint ii;
gboolean ret;
-
-
+
+
analysefile_name = p_create_analysefile_name(gvahand);
master_handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
keylist = gap_val_new_keylist();
p_set_analysefile_master_keywords(keylist, gvahand, READSTEPS_PROBE_TIMECODE);
-
+
/* init structures with some non-plausible values
* (will be overwritten on sucessful fetch)
*/
@@ -4908,10 +4985,10 @@ p_get_video_analyse_results(t_GVA_Handle *gvahand)
{
master_handle->timecode_steps[ii] = UNDEFINED_TIMECODE_STEP_VALUE;
}
-
+
min_expected_items = 10;
master_handle->count_timecode_steps = -1;
-
+
scanned_items = gap_val_scann_filevalues(keylist, analysefile_name);
gap_val_free_keylist(keylist);
curr_mtime = gap_file_get_mtime(gvahand->filename);
@@ -4947,8 +5024,8 @@ p_get_video_analyse_results(t_GVA_Handle *gvahand)
,(int)scanned_items
,(int)master_handle->timestamp
,(int)curr_mtime
- );
- }
+ );
+ }
g_free(analysefile_name);
return (ret);
@@ -4965,7 +5042,7 @@ static char*
p_create_analysefile_name(t_GVA_Handle *gvahand)
{
char *vindex_name;
-
+
vindex_name = p_build_gvaidx_filename(gvahand->filename
, gvahand->vid_track
, GVA_FFMPEG_DECODER_NAME
@@ -5078,7 +5155,7 @@ p_frame_nr_to_timecode(t_GVA_ffmpeg *handle, gint32 frame_nr)
, (int)handle->timecode_steps_sum
);
}
-
+
return(timecode);
} /* end p_frame_nr_to_timecode */
@@ -5099,7 +5176,7 @@ p_analyze_stepsize_pattern(gint max_idx, t_GVA_Handle *gvahand)
gint jj;
t_GVA_ffmpeg *master_handle;
int gap_debug_local;
-
+
gap_debug_local = gap_debug;
#ifdef GAP_DEBUG_FF_NATIVE_SEEK
@@ -5185,7 +5262,7 @@ p_analyze_stepsize_pattern(gint max_idx, t_GVA_Handle *gvahand)
* Note that duration of frames may not be constant for all videofiles.
* for videos with very individual frame durations it is not possible
* to calculate correct timecode for a given frame number.
- * Typically most (DVD) videos use individual frame timing
+ * Typically most (DVD) videos use individual frame timing
* in cyclic pattern manner. This probe read analyzes the type of pattern
* and enables correct frame to timestamp conversion
* based on the measured resulting array timecode_steps[]
@@ -5217,7 +5294,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
l_countValidTimecodes = 0;
master_handle->timecode_proberead_done = TRUE;
master_handle->timecode_step_abs_min = 99999999;
-
+
/* calculate average timecode step per frame via framerate */
avg_fstepsize = 100000.0 / master_gvahand->framerate;
master_handle->timecode_steps[0] = avg_fstepsize;
@@ -5228,7 +5305,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
, READSTEPS_PROBE_TIMECODE
);
}
-
+
/* open an extra handle for the probe read */
copy_gvahand = g_malloc0(sizeof(t_GVA_Handle));
p_wrapper_ffmpeg_open_read(master_gvahand->filename
@@ -5262,7 +5339,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
MIN(abs(master_handle->timecode_steps[l_readsteps])
, master_handle->timecode_step_abs_min);
l_readsteps++;
- master_handle->timecode_step_avg =
+ master_handle->timecode_step_avg =
(copy_handle->vid_pkt.dts - master_handle->timecode_offset_frame1) / l_readsteps;
if(gap_debug)
@@ -5280,14 +5357,14 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
/* close the extra handle (that was opened for counting only) */
p_wrapper_ffmpeg_close(copy_gvahand);
-
+
if (l_countValidTimecodes > 0)
{
p_analyze_stepsize_pattern(l_readsteps, master_gvahand);
}
else
{
- /* some older ffmpeg versions did always deliver valid dts timecodes,
+ /* some older ffmpeg versions did always deliver valid dts timecodes,
* even if not present in the video.
* but unfortunately recent ffmpeg snapshots deliver AV_NOPTS_VALUE as dts
* for such videos. In this case native seek must be disabled.
@@ -5296,7 +5373,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
master_handle->count_timecode_steps = 1;
master_handle->video_libavformat_seek_gopsize = 0; /* DISABLE natvie seek */
master_handle->prefere_native_seek = FALSE;
-
+
printf("WARNING: p_probe_timecode_offset no valid timecode found in video:%s\n"
, master_gvahand->filename
);
@@ -5305,7 +5382,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
}
}
-
+
} /* end p_probe_timecode_offset */
diff --git a/libgapvidapi/gap_vid_api_mpeg3.c b/libgapvidapi/gap_vid_api_mpeg3.c
index 1a5c155..4f900e8 100644
--- a/libgapvidapi/gap_vid_api_mpeg3.c
+++ b/libgapvidapi/gap_vid_api_mpeg3.c
@@ -334,7 +334,7 @@ p_wrapper_mpeg3_open_read(char *in_filename, t_GVA_Handle *gvahand)
gvahand->height = mpeg3_video_height(handle->main_handle, (int)gvahand->vid_track);
gvahand->aspect_ratio = mpeg3_aspect_ratio(handle->main_handle, (int)gvahand->vid_track);
- /* libmpeg3-1.5.4 has a bug and did always deliver apect ratio value 0.0
+ /* libmpeg3-1.5.4 has a bug and did always deliver aspect ratio value 0.0
* the aspect was not recognized when it was there
* tested with some DVD and MPEG1 videofiles
*/
@@ -853,7 +853,7 @@ p_wrapper_mpeg3_count_frames(t_GVA_Handle *gvahand)
if(gvahand->cancel_operation)
{
- /* delete uncomplete TOC file
+ /* delete incomplete TOC file
* (dont know if libmpeg3 can use unfinished TOC files)
*/
g_remove(vindex->tocfile);
@@ -883,7 +883,7 @@ p_wrapper_mpeg3_count_frames(t_GVA_Handle *gvahand)
/* because of cancel the total_frames is still unknown
* (vindex->total_frames == 0 is the indicator for incomplete index)
* For libmpeg3: videoindex is just used to store total_frames info
- * so we dont save uncomplete videoindex file
+ * so we dont save incomplete videoindex file
*/
vindex->total_frames = 0;
}
@@ -1117,7 +1117,7 @@ p_mpeg3_new_dec_elem(void)
/* -----------------------
* p_mpeg3_emulate_seek
* -----------------------
- * procedure to completly emulate frame seek by dummy reads
+ * procedure to completely emulate frame seek by dummy reads
* (is very slow but sets position to exact frame position
* this is needed if we have no TOC file)
*/
@@ -1183,7 +1183,7 @@ p_mpeg3_emulate_seek(mpeg3_t* handle, gint32 seekto_frame, t_GVA_Handle *gvahan
if(l_clean_reads > 0)
{
/* after (re)open we must start with a clean read
- * (never start with a dirty read (because ist does not advance position as expected)
+ * (never start with a dirty read (because it does not advance position as expected)
*/
l_clean_reads--;
@@ -1226,7 +1226,7 @@ p_mpeg3_emulate_seek(mpeg3_t* handle, gint32 seekto_frame, t_GVA_Handle *gvahan
for(l_ii = 0; l_ii < l_dirty_reads; l_ii++)
{
- /* read one frame (we use the comressed chunk
+ /* read one frame (we use the compressed chunk
* because this is a dummy read to skip frames only)
*/
l_rc = mpeg3_read_video_chunk(seek_handle,
@@ -1354,8 +1354,8 @@ p_mpeg3_gopseek(mpeg3_t* handle, gint32 seekto_frame, t_GVA_Handle *gvahand)
|| ((gvahand->current_seek_nr + GVA_GOPSEEKSIZE) <= seekto_frame) )
{
if(gap_debug) printf("p_mpeg3_gopseek(F): (GOPSEEK NEEDED)\n");
- /* perform (faster) seek if we have to do backstep
- * or if we have to do a bigstep forward (greater than GVA_GOPSEEKSIZE)
+ /* perform (faster) seek if we have to do step back
+ * or if we have to do a big step forward (greater than GVA_GOPSEEKSIZE)
*/
l_rc = mpeg3_set_frame(seek_handle, l_gopseek +1, (int)gvahand->vid_track);
gvahand->percentage_done += l_progress_step;
@@ -1426,9 +1426,9 @@ p_mpeg3_gopseek(mpeg3_t* handle, gint32 seekto_frame, t_GVA_Handle *gvahand)
* the name of the toc file must then be passed to the
* the mpeg3_open procedure (rather than the original videofile)
*
- * toc files can be created explicite with the commandline tool mpeg3toc
+ * toc files can be created explicitly with the commandline tool mpeg3toc
* (that is part of the libmpeg3 distribution)
- * or implicite by the GVA video Api procedures.
+ * or implicitly by the GVA video Api procedures.
*/
gboolean
p_check_libmpeg3_toc_file(const char *filename)
diff --git a/libgapvidapi/gap_vid_api_mpeg3toc.c b/libgapvidapi/gap_vid_api_mpeg3toc.c
index 15e4fc4..7e23517 100644
--- a/libgapvidapi/gap_vid_api_mpeg3toc.c
+++ b/libgapvidapi/gap_vid_api_mpeg3toc.c
@@ -1,14 +1,14 @@
-/* this file is a modified variante
+/* this file is a modified variant
* of the meg3toc.c Code that came with libmpeg3-1.8
*
* it is used to build the libmpeg3
- * compatible "Table of content" files implicite
+ * compatible "Table of content" files implicitly
* when using the GIMP-GAP specific GVA video API.
* .TOC files are the libmpeg3 specific pendant to gvaindexes
* and enable fast and exact positioning in the MPEG videofile
* (but need one initial full scan to create)
*
- * This variante of the toc file genration is used in GIMP-GAP
+ * This variant of the toc file generation is used in GIMP-GAP
* GUI Modules and provides Progress Feedback for the calling GUI
* and has the option to STOP (cancel) TOC creation on GUI request.
*
diff --git a/libgapvidutil/gap_gve_raw.c b/libgapvidutil/gap_gve_raw.c
index ea604d2..bf2a686 100644
--- a/libgapvidutil/gap_gve_raw.c
+++ b/libgapvidutil/gap_gve_raw.c
@@ -190,9 +190,9 @@ gap_gve_raw_RGB_drawable_encode(GimpDrawable *drawable, gint32 *RAW_size, gboole
, 1);
for(l_idx=0;l_idx < l_rowstride; l_idx += drawable->bpp)
{
- *(RAW_ptr++) = pixelrow_data[l_idx + l_blue];
- *(RAW_ptr++) = pixelrow_data[l_idx + l_green];
*(RAW_ptr++) = pixelrow_data[l_idx + l_red];
+ *(RAW_ptr++) = pixelrow_data[l_idx + l_green];
+ *(RAW_ptr++) = pixelrow_data[l_idx + l_blue];
}
}
g_free(pixelrow_data);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 406a70c..6a0a80c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,9 @@ gap/gap_audio_wav.c
gap/gap_base_ops.c
gap/gap_bluebox.c
gap/gap_bluebox_main.c
+gap/gap_colormask_dialog.c
+gap/gap_colormask_file.c
+gap/gap_colormask_main.c
gap/gap_dbbrowser_utils.c
gap/gap_decode_mplayer_main.c
gap/gap_decode_mplayer.c
@@ -24,6 +27,7 @@ gap/gap_mod_layer_dialog.c
gap/gap_morph_dialog.c
gap/gap_morph_exec.c
gap/gap_morph_main.c
+gap/gap_morph_shape.c
gap/gap_morph_tween_dialog.c
gap/gap_mov_dialog.c
gap/gap_mov_exec.c
@@ -54,6 +58,7 @@ gap/gap_vex_dialog.c
gap/gap_vex_exec.c
gap/gap_video_index_creator.c
gap/gap_vex_main.c
+gap/gap_wr_color_balance.c
gap/gap_wr_color_curve.c
gap/gap_wr_color_huesat.c
gap/gap_wr_color_levels.c
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
index aa75df3..88928d9 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
@@ -284,7 +284,7 @@ p_replace_combo_vid_codec(GapGveFFMpegGlobalParams *gpp)
{
char *menu_name;
char *object_data;
- if ((avcodec->encode) && (avcodec->type == CODEC_TYPE_VIDEO))
+ if ((avcodec->encode) && (avcodec->type == AVMEDIA_TYPE_VIDEO))
{
object_data = (char *)avcodec->name;
@@ -358,7 +358,7 @@ p_replace_combo_aud_codec(GapGveFFMpegGlobalParams *gpp)
{
char *menu_name;
char *object_data;
- if ((avcodec->encode) && (avcodec->type == CODEC_TYPE_AUDIO))
+ if ((avcodec->encode) && (avcodec->type == AVMEDIA_TYPE_AUDIO))
{
object_data = (char *)avcodec->name;
@@ -3736,7 +3736,7 @@ p_create_ffmpeg_dialog_shell (GapGveFFMpegGlobalParams *gpp)
gpp->ff_codec_FLAG_GRAY_checkbutton = NULL;
gpp->ff_codec_FLAG_EMU_EDGE_checkbutton = NULL;
gpp->ff_codec_FLAG_TRUNCATED_checkbutton = NULL;
-
+
gpp->ff_codec_FLAG2_FAST_checkbutton = NULL;
gpp->ff_codec_FLAG2_LOCAL_HEADER_checkbutton = NULL;
gpp->ff_codec_FLAG2_BPYRAMID_checkbutton = NULL;
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
index c95c256..eadbec1 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
@@ -76,7 +76,8 @@
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
-#include "imgconvert.h"
+//#include "imgconvert.h"
+#include "swscale.h"
#include "gap-intl.h"
@@ -204,6 +205,8 @@ typedef struct t_ffmpeg_handle
gdouble pts_stepsize_per_frame;
gint32 encode_frame_nr; /* number of frame to encode always starts at 1 and inc by one (base for monotone timecode calculation) */
+ struct SwsContext *img_convert_ctx;
+
} t_ffmpeg_handle;
@@ -230,6 +233,7 @@ static void run(const gchar *name
static void p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext);
+static int p_av_metadata_set(AVMetadata **pm, const char *key, const char *value, int flags);
static void p_set_flag(gint32 value_bool32, int *flag_ptr, int maskbit);
@@ -699,7 +703,7 @@ static void
p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext)
{
printf("AVCodecContext Settings START\n");
-
+
printf("(av_class (ptr AVClass*) %d)\n", (int) codecContext->av_class);
printf("(bit_rate %d)\n", (int) codecContext->bit_rate);
printf("(bit_rate_tolerance %d)\n", (int) codecContext->bit_rate_tolerance);
@@ -901,12 +905,49 @@ p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext)
printf("(rc_max_available_vbv_use %f)\n", (float) codecContext->rc_max_available_vbv_use);
printf("(rc_min_vbv_overflow_use %f)\n", (float) codecContext->rc_min_vbv_overflow_use);
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+
+ printf("(hwaccel_context %d)\n", (int) codecContext->hwaccel_context);
+ printf("(color_primaries %d)\n", (int) codecContext->color_primaries);
+ printf("(color_trc %d)\n", (int) codecContext->color_trc);
+ printf("(colorspace %d)\n", (int) codecContext->colorspace);
+ printf("(color_range %d)\n", (int) codecContext->color_range);
+ printf("(chroma_sample_location %d)\n", (int) codecContext->chroma_sample_location);
+ /* *func */
+ printf("(weighted_p_pred %d)\n", (int) codecContext->weighted_p_pred);
+ printf("(aq_mode %d)\n", (int) codecContext->aq_mode);
+ printf("(aq_strength %f)\n", (float) codecContext->aq_strength);
+ printf("(psy_rd %f)\n", (float) codecContext->psy_rd);
+ printf("(psy_trellis %f)\n", (float) codecContext->psy_trellis);
+ printf("(rc_lookahead %d)\n", (int) codecContext->rc_lookahead);
+
+#endif
+
printf("AVCodecContext Settings END\n");
} /* end p_debug_print_dump_AVCodecContext */
/* --------------------------------
+ * p_av_metadata_set
+ * --------------------------------
+ */
+static int
+p_av_metadata_set(AVMetadata **pm, const char *key, const char *value, int flags)
+{
+ int ret;
+
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+ ret = av_metadata_set2(pm, key, value, flags);
+#else
+ ret = av_metadata_set(pm, key, value);
+#endif
+
+ return (ret);
+} /* end p_av_metadata_set */
+
+
+/* --------------------------------
* p_set_flag
* --------------------------------
*/
@@ -1155,7 +1196,7 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
/* new parms 2009.01.31 */
-
+
epp->b_frame_strategy = 0;
epp->workaround_bugs = FF_BUG_AUTODETECT;
epp->error_recognition = FF_ER_CAREFUL;
@@ -1177,7 +1218,7 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
epp->cqp = -1;
epp->keyint_min = 25; /* minimum GOP size */
epp->refs = 1;
- epp->chromaoffset = 0;
+ epp->chromaoffset = 0;
epp->bframebias = 0;
epp->trellis = 0;
epp->complexityblur = 20.0;
@@ -1201,7 +1242,20 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
epp->channel_layout = 0;
epp->rc_max_available_vbv_use = 1.0 / 3.0;
epp->rc_min_vbv_overflow_use = 3.0;
-
+
+ /* new params of ffmpeg-0.6 2010.07.31 */
+ epp->color_primaries = 0; // TODO enum AVColorPrimaries color_primaries;
+ epp->color_trc = 0; // enum AVColorTransferCharacteristic color_trc;
+ epp->colorspace = 0; // enum AVColorSpace colorspace;
+ epp->color_range = 0; // enum AVColorRange color_range;
+ epp->chroma_sample_location = 0; // enum AVChromaLocation chroma_sample_location;
+ epp->weighted_p_pred = 0;
+ epp->aq_mode = 0;
+ epp->aq_strength = 0.0;
+ epp->psy_rd = 0.0;
+ epp->psy_trellis = 0.0;
+ epp->rc_lookahead = 0;
+
/* new flags/flags2 2009.01.31 */
epp->codec_FLAG_GMC = 0; /* 0: FALSE */
epp->codec_FLAG_INPUT_PRESERVED = 0; /* 0: FALSE */
@@ -1226,6 +1280,11 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
epp->codec_FLAG2_NON_LINEAR_QUANT = 0; /* 0: FALSE */
epp->codec_FLAG2_BIT_RESERVOIR = 0; /* 0: FALSE */
+ /* new flags/flags2 of ffmpeg-0.6 2010.07.31 */
+ epp->codec_FLAG2_MBTREE = 0; /* 0: FALSE */
+ epp->codec_FLAG2_PSY = 0; /* 0: FALSE */
+ epp->codec_FLAG2_SSIM = 0; /* 0: FALSE */
+
} /* end gap_enc_ffmpeg_main_init_preset_params */
@@ -1621,7 +1680,7 @@ p_calculate_current_timecode(t_ffmpeg_handle *ffh)
{
gdouble dblTimecode;
gint64 timecode64;
-
+
/*
* gdouble seconds;
* seconds = (ffh->encode_frame_nr / gpp->val.framerate);
@@ -1629,7 +1688,7 @@ p_calculate_current_timecode(t_ffmpeg_handle *ffh)
dblTimecode = ffh->encode_frame_nr * ffh->pts_stepsize_per_frame;
timecode64 = dblTimecode;
-
+
return (timecode64);
}
@@ -1669,19 +1728,19 @@ p_set_timebase_from_framerate(AVRational *time_base, gdouble framerate)
{
time_base->num = 1001;
time_base->den = 60000;
-
+
}
if ((framerate > 29.90) && (framerate < 29.99))
{
time_base->num = 1001;
time_base->den = 30000;
-
+
}
if ((framerate > 23.90) && (framerate < 23.99))
{
time_base->num = 1001;
time_base->den = 24000;
-
+
}
} /* end p_set_timebase_from_framerate */
@@ -1729,10 +1788,10 @@ p_ffmpeg_open_init(t_ffmpeg_handle *ffh, GapGveFFMpegGlobalParams *gpp)
ffh->ast[ii].audio_buffer = NULL;
ffh->ast[ii].audio_buffer_size = 0;
}
-
+
{
AVRational l_time_base;
-
+
p_set_timebase_from_framerate(&l_time_base, gpp->val.framerate);
ffh->pts_stepsize_per_frame = l_time_base.num;
@@ -1786,7 +1845,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
g_free(ffh);
return(FALSE); /* error */
}
- if(ffh->vst[ii].vid_codec->type != CODEC_TYPE_VIDEO)
+ if(ffh->vst[ii].vid_codec->type != AVMEDIA_TYPE_VIDEO)
{
printf("CODEC: %s is no VIDEO CODEC!\n", epp->vcodec_name);
g_free(ffh);
@@ -1825,14 +1884,14 @@ p_init_video_codec(t_ffmpeg_handle *ffh
video_enc = ffh->vst[ii].vid_codec_context;
- video_enc->codec_type = CODEC_TYPE_VIDEO;
+ video_enc->codec_type = AVMEDIA_TYPE_VIDEO;
video_enc->codec_id = ffh->vst[ii].vid_codec->id;
video_enc->bit_rate = epp->video_bitrate * 1000;
video_enc->bit_rate_tolerance = epp->bitrate_tol * 1000;
p_set_timebase_from_framerate(&video_enc->time_base, gpp->val.framerate);
-
+
video_enc->width = gpp->val.vid_width;
video_enc->height = gpp->val.vid_height;
@@ -2005,6 +2064,12 @@ p_init_video_codec(t_ffmpeg_handle *ffh
p_set_flag(epp->codec_FLAG2_NON_LINEAR_QUANT, &video_enc->flags2, CODEC_FLAG2_NON_LINEAR_QUANT);
p_set_flag(epp->codec_FLAG2_BIT_RESERVOIR, &video_enc->flags2, CODEC_FLAG2_BIT_RESERVOIR);
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+ p_set_flag(epp->codec_FLAG2_MBTREE, &video_enc->flags2, CODEC_FLAG2_MBTREE);
+ p_set_flag(epp->codec_FLAG2_PSY, &video_enc->flags2, CODEC_FLAG2_PSY);
+ p_set_flag(epp->codec_FLAG2_SSIM, &video_enc->flags2, CODEC_FLAG2_SSIM);
+#endif
+
if ((epp->b_frames > 0) && (!epp->intra))
{
@@ -2036,7 +2101,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
break;
}
}
-
+
video_enc->qmin = epp->qmin;
video_enc->qmax = epp->qmax;
video_enc->max_qdiff = epp->qdiff;
@@ -2069,8 +2134,8 @@ p_init_video_codec(t_ffmpeg_handle *ffh
video_enc->workaround_bugs = epp->workaround_bugs;
video_enc->error_recognition = epp->error_recognition;
video_enc->mpeg_quant = epp->mpeg_quant;
-
-
+
+
video_enc->debug = 0;
video_enc->mb_qmin = epp->mb_qmin;
video_enc->mb_qmax = epp->mb_qmax;
@@ -2081,8 +2146,8 @@ p_init_video_codec(t_ffmpeg_handle *ffh
video_enc->rc_qsquish = epp->rc_qsquish;
video_enc->rc_qmod_amp = epp->rc_qmod_amp;
video_enc->rc_qmod_freq = epp->rc_qmod_freq;
-
-
+
+
video_enc->luma_elim_threshold = epp->video_lelim;
video_enc->chroma_elim_threshold = epp->video_celim;
@@ -2108,7 +2173,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
video_enc->nsse_weight = epp->nsse_weight;
video_enc->me_subpel_quality = epp->subpel_quality;
-
+
video_enc->dia_size = epp->dia_size;
video_enc->last_predictor_count = epp->last_predictor_count;
video_enc->pre_dia_size = epp->pre_dia_size;
@@ -2130,7 +2195,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
video_enc->cqp = epp->cqp;
video_enc->keyint_min = epp->keyint_min;
video_enc->refs = epp->refs;
- video_enc->chromaoffset = epp->chromaoffset;
+ video_enc->chromaoffset = epp->chromaoffset;
video_enc->bframebias = epp->bframebias;
video_enc->trellis = epp->trellis;
video_enc->complexityblur = (float) epp->complexityblur;
@@ -2153,6 +2218,21 @@ p_init_video_codec(t_ffmpeg_handle *ffh
video_enc->rc_max_available_vbv_use = (float)epp->rc_max_available_vbv_use;
video_enc->rc_min_vbv_overflow_use = (float)epp->rc_min_vbv_overflow_use;
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+ //video_enc->hwaccel_context == NULL;
+ video_enc->color_primaries = epp->color_primaries;
+ video_enc->color_trc = epp->color_trc;
+ video_enc->colorspace = epp->colorspace;
+ video_enc->color_range = epp->color_range;
+ video_enc->chroma_sample_location = epp->chroma_sample_location;
+ //video_enc->func = NULL;
+ video_enc->weighted_p_pred = (int)epp->weighted_p_pred;
+ video_enc->aq_mode = (int)epp->aq_mode;
+ video_enc->aq_strength = (float)epp->aq_strength;
+ video_enc->psy_rd = (float)epp->psy_rd;
+ video_enc->psy_trellis = (float)epp->psy_trellis;
+ video_enc->rc_lookahead = (int)epp->rc_lookahead;
+#endif
if(epp->packet_size)
@@ -2167,19 +2247,19 @@ p_init_video_codec(t_ffmpeg_handle *ffh
if (epp->title[0] != '\0')
{
- av_metadata_set(&ffh->output_context->metadata, "title", &epp->title[0]);
+ p_av_metadata_set(&ffh->output_context->metadata, "title", &epp->title[0], 0);
}
if (epp->author[0] != '\0')
{
- av_metadata_set(&ffh->output_context->metadata, "author", &epp->author[0]);
+ p_av_metadata_set(&ffh->output_context->metadata, "author", &epp->author[0], 0);
}
if (epp->copyright[0] != '\0')
{
- av_metadata_set(&ffh->output_context->metadata, "copyright", &epp->copyright[0]);
+ p_av_metadata_set(&ffh->output_context->metadata, "copyright", &epp->copyright[0], 0);
}
if (epp->comment[0] != '\0')
{
- av_metadata_set(&ffh->output_context->metadata, "comment", &epp->comment[0]);
+ p_av_metadata_set(&ffh->output_context->metadata, "comment", &epp->comment[0], 0);
}
@@ -2265,7 +2345,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
return (TRUE); /* OK */
-
+
} /* end p_init_video_codec */
@@ -2291,7 +2371,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
audioOK = TRUE;
msg = NULL;
-
+
/* ------------ Start Audio CODEC init ------- */
if(audio_channels > 0)
{
@@ -2305,7 +2385,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
}
else
{
- if(ffh->ast[ii].aud_codec->type != CODEC_TYPE_AUDIO)
+ if(ffh->ast[ii].aud_codec->type != AVMEDIA_TYPE_AUDIO)
{
audioOK = FALSE;
msg = g_strdup_printf(_("CODEC: %s is no AUDIO CODEC!"), epp->acodec_name);
@@ -2326,7 +2406,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
/* ffh->ast[ii].aud_codec_context = avcodec_alloc_context();*/
audio_enc = ffh->ast[ii].aud_codec_context;
- audio_enc->codec_type = CODEC_TYPE_AUDIO;
+ audio_enc->codec_type = AVMEDIA_TYPE_AUDIO;
audio_enc->codec_id = ffh->ast[ii].aud_codec->id;
audio_enc->bit_rate = epp->audio_bitrate * 1000;
@@ -2340,7 +2420,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
audio_enc->workaround_bugs = epp->workaround_bugs;
audio_enc->error_recognition = epp->error_recognition;
audio_enc->cutoff = epp->cutoff;
-
+
if (bits == 16)
{
audio_enc->sample_fmt = SAMPLE_FMT_S16;
@@ -2349,8 +2429,8 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
{
audio_enc->sample_fmt = SAMPLE_FMT_U8;
}
-
-
+
+
switch (audio_channels)
{
case 1:
@@ -2362,14 +2442,14 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
default:
audio_enc->channel_layout = epp->channel_layout;
break;
-
+
}
/* open audio codec */
if (avcodec_open(ffh->ast[ii].aud_codec_context, ffh->ast[ii].aud_codec) < 0)
{
audioOK = FALSE;
-
+
msg = g_strdup_printf(_("could not open audio codec: %s\n"
"at audio_samplerate:%d channels:%d bits per channel:%d\n"
"(try to convert to 48 KHz, 44.1KHz or 32 kHz samplerate\n"
@@ -2381,7 +2461,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
);
ffh->ast[ii].aud_codec = NULL;
}
-
+
}
}
}
@@ -2447,7 +2527,11 @@ p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
/* ------------ File Format ------- */
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+ ffh->file_oformat = av_guess_format(epp->format_name, gpp->val.videoname, NULL);
+#else
ffh->file_oformat = guess_format(epp->format_name, gpp->val.videoname, NULL);
+#endif
if (!ffh->file_oformat)
{
printf("Unknown output format: %s\n", epp->format_name);
@@ -2558,7 +2642,7 @@ p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
*/
ffh->ap->sample_rate = awp->awk[0].sample_rate;
ffh->ap->channels = awp->awk[0].channels;
-
+
p_set_timebase_from_framerate(&ffh->ap->time_base, gpp->val.framerate);
ffh->ap->width = gpp->val.vid_width;
@@ -2637,6 +2721,8 @@ p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
ffh->output_context->preload = (int)(epp->mux_preload*AV_TIME_BASE);
ffh->output_context->max_delay = (int)(epp->mux_max_delay*AV_TIME_BASE);
+ ffh->img_convert_ctx = NULL; /* will be allocated at first img conversion */
+
if(gap_debug)
{
printf("\np_ffmpeg_open END\n");
@@ -2740,7 +2826,7 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
chunk_frame_type = GVA_util_check_mpg_frame_type(ffh->vst[ii].video_buffer, encoded_size);
if(chunk_frame_type == 1) /* check for intra frame type */
{
- pkt.flags |= PKT_FLAG_KEY;
+ pkt.flags |= AV_PKT_FLAG_KEY;
}
@@ -2758,14 +2844,14 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
///// pkt.pts = ffh->vst[ii].vid_codec_context->coded_frame->pts; // OLD
{
AVCodecContext *c;
-
+
c = ffh->vst[ii].vid_codec_context;
if (c->coded_frame->pts != AV_NOPTS_VALUE)
{
pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, ffh->vst[ii].vid_stream->time_base);
}
}
-
+
pkt.dts = AV_NOPTS_VALUE; /* let av_write_frame calculate the decompression timestamp */
pkt.stream_index = ffh->vst[ii].video_stream_index;
pkt.data = ffh->vst[ii].video_buffer;
@@ -2784,7 +2870,7 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
* p_convert_colormodel
* -----------------------
* convert video frame specified in the rgb_buffer
- * from PIX_FMT_BGR24 to the colormodel that is required
+ * from PIX_FMT_RGB24 to the colormodel that is required
* by the video codec.
*
* conversion is done based on ffmpegs img_convert procedure.
@@ -2796,7 +2882,7 @@ p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb
AVPicture *picture_rgb;
uint8_t *l_convert_buffer;
int ii;
- int l_rc;
+ //int l_rc;
ii = ffh->vst[vid_track].video_stream_index;
@@ -2804,7 +2890,7 @@ p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb
big_picture_rgb = avcodec_alloc_frame();
picture_rgb = (AVPicture *)big_picture_rgb;
-
+
/* allocate buffer for image conversion large enough for for uncompressed RGBA32 colormodel */
l_convert_buffer = g_malloc(4 * ffh->frame_width * ffh->frame_height);
@@ -2844,16 +2930,45 @@ p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb
, (int)PIX_FMT_YUV420P);
}
- /* convert to pix_fmt needed by the codec */
- l_rc = img_convert(picture_codec, ffh->vst[ii].vid_codec_context->pix_fmt /* dst */
- ,picture_rgb, PIX_FMT_BGR24 /* src */
- ,ffh->frame_width
- ,ffh->frame_height
- );
+ /* reuse the img_convert_ctx or create a new one
+ * (in case ctx is NULL or params have changed)
+ */
+ ffh->img_convert_ctx = sws_getCachedContext(ffh->img_convert_ctx
+ , ffh->frame_width
+ , ffh->frame_height
+ , PIX_FMT_RGB24 /* src pixelformat */
+ , ffh->frame_width
+ , ffh->frame_height
+ , ffh->vst[ii].vid_codec_context->pix_fmt /* dest pixelformat */
+ , SWS_BICUBIC /* int sws_flags */
+ , NULL, NULL, NULL
+ );
+ if (ffh->img_convert_ctx == NULL)
+ {
+ printf("Cannot initialize the conversion context (sws_getCachedContext delivered NULL pointer)\n");
+ exit(1);
+ }
+
+ /* convert from RGB to pix_fmt needed by the codec */
+ sws_scale(ffh->img_convert_ctx
+ , picture_rgb->data /* srcSlice */
+ , picture_rgb->linesize /* srcStride the array containing the strides for each plane */
+ , 0 /* srcSliceY starting at 0 */
+ , ffh->frame_height /* srcSliceH the height of the source slice */
+ , picture_codec->data /* dst */
+ , picture_codec->linesize /* dstStride the array containing the strides for each plane */
+ );
+
+ // /* img_convert is no longer available since ffmpeg.0.6 */
+ //l_rc = img_convert(picture_codec, ffh->vst[ii].vid_codec_context->pix_fmt /* dst */
+ // ,picture_rgb, PIX_FMT_BGR24 /* src */
+ // ,ffh->frame_width
+ // ,ffh->frame_height
+ // );
if(gap_debug)
{
- printf("after img_convert: l_rc:%d\n", l_rc);
+ printf("after sws_scale:\n");
}
g_free(big_picture_rgb);
@@ -2964,7 +3079,7 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
{
l_convert_buffer = p_convert_colormodel(ffh, picture_codec, rgb_buffer, vid_track);
}
-
+
if(gap_debug)
{
@@ -3021,26 +3136,26 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
///// pkt.pts = ffh->vst[ii].vid_codec_context->coded_frame->pts; // OLD
-
+
if (c->coded_frame->pts != AV_NOPTS_VALUE)
{
pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, ffh->vst[ii].vid_stream->time_base);
}
-
+
// if ((pkt.pts == 0) || (pkt.pts == AV_NOPTS_VALUE))
// {
// /* WORKAROND calculate pts timecode for the current frame
-// * because the codec did not deliver a valid timecode
+// * because the codec did not deliver a valid timecode
// */
// pkt.pts = p_calculate_current_timecode(ffh);
// }
-
+
if(c->coded_frame->key_frame)
{
- pkt.flags |= PKT_FLAG_KEY;
+ pkt.flags |= AV_PKT_FLAG_KEY;
}
-
+
pkt.stream_index = ffh->vst[ii].video_stream_index;
pkt.data = ffh->vst[ii].video_buffer;
pkt.size = encoded_size;
@@ -3048,7 +3163,7 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
if(gap_debug)
{
AVStream *st;
-
+
st = ffh->output_context->streams[pkt.stream_index];
printf("before av_write_frame video encoded_size:%d\n"
@@ -3065,7 +3180,7 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
,st->pts.den
,st->pts.val
);
-
+
}
ret = av_write_frame(ffh->output_context, &pkt);
@@ -3130,7 +3245,7 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
// pkt.pts = ffh->ast[ii].aud_codec_context->coded_frame->pts; // OLD
-
+
if (c->coded_frame->pts != AV_NOPTS_VALUE)
{
pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, ffh->ast[ii].aud_stream->time_base);
@@ -3139,16 +3254,16 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
// if ((pkt.pts == 0) || (pkt.pts == AV_NOPTS_VALUE))
// {
// /* calculate pts timecode for the current frame
-// * because the codec did not deliver a valid timecode
+// * because the codec did not deliver a valid timecode
// */
// pkt.pts = p_calculate_current_timecode(ffh);
// }
-
-
+
+
pkt.stream_index = ffh->ast[ii].audio_stream_index;
pkt.data = ffh->ast[ii].audio_buffer;
pkt.size = encoded_size;
-
+
if(gap_debug)
{
printf("before av_write_frame audio encoded_size:%d pkt.pts:%lld dts:%lld\n"
@@ -3157,7 +3272,7 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
, pkt.dts
);
}
-
+
ret = av_write_frame(ffh->output_context, &pkt);
if(gap_debug)
@@ -3175,13 +3290,13 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
* my_url_fclose
* ---------------
* this procedure is a workaround that fixes a crash that happens on attempt to call the original
- * url_fclose procedure
+ * url_fclose procedure
* my private copy of (libavformat/aviobuf.c url_fclose)
* just skips the crashing step "av_free(s);" that frees up the ByteIOContext itself
* (free(): invalid pointer: 0x0859f3b0)
* The workaround is still required in ffmpeg snapshot from 2009.01.31
*/
-int
+int
my_url_fclose(ByteIOContext *s)
{
URLContext *h = s->opaque;
@@ -3312,7 +3427,7 @@ p_ffmpeg_close(t_ffmpeg_handle *ffh)
}
}
-
+
if(gap_debug)
{
printf("Closing AUDIO stuff\n");
@@ -3343,7 +3458,7 @@ p_ffmpeg_close(t_ffmpeg_handle *ffh)
printf("before url_fclose\n");
}
my_url_fclose(&ffh->output_context->pb);
-
+
if(gap_debug)
{
printf("after url_fclose\n");
@@ -3355,6 +3470,11 @@ p_ffmpeg_close(t_ffmpeg_handle *ffh)
}
}
+ if(ffh->img_convert_ctx != NULL)
+ {
+ sws_freeContext(ffh->img_convert_ctx);
+ ffh->img_convert_ctx = NULL;
+ }
} /* end p_ffmpeg_close */
@@ -3543,7 +3663,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
l_cnt_encoded_frames = 0;
l_cnt_reused_frames = 0;
p_init_audio_workdata(awp);
-
+
l_check_flags = GAP_VID_CHCHK_FLAG_SIZE;
l_vcodec_list = p_setup_check_flags(epp, &l_check_flags);
@@ -3594,7 +3714,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
}
/* TODO check for overwrite (in case we are called non-interactive)
- * overwrite check shall be done only if (current_pass < 2)
+ * overwrite check shall be done only if (current_pass < 2)
*/
if (gap_debug) printf("Creating ffmpeg file.\n");
@@ -3674,7 +3794,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
if(gap_debug)
{
printf("\nFFenc: after gap_story_render_fetch_composite_image_or_chunk image_id:%d layer_id:%d\n"
- , (int)l_tmp_image_id
+ , (int)l_tmp_image_id
, (int) l_layer_id
);
}
@@ -3793,7 +3913,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
printf("1:1 copied frames: %d\n", (int)l_cnt_reused_frames);
printf("total handled frames: %d\n", (int)l_cnt_encoded_frames + l_cnt_reused_frames);
}
-
+
return l_rc;
} /* end p_ffmpeg_encode_pass */
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
index 777f5c0..3a872c5 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
@@ -38,6 +38,28 @@
#include "avformat.h"
#include "avcodec.h"
+/// start ffmpeg 0.5 / 0.6 support
+#if LIBAVCODEC_VERSION_MAJOR < 52
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#if LIBAVCODEC_VERSION_MAJOR == 52
+#if LIBAVCODEC_VERSION_MINOR <= 20
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#endif
+
+
+
+#ifdef GAP_USES_OLD_FFMPEG_0_5
+/* defines to use older ffmpeg-0.5 compatible types */
+#define AVMEDIA_TYPE_UNKNOWN CODEC_TYPE_UNKNOWN
+#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
+#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO
+#define AV_PKT_FLAG_KEY PKT_FLAG_KEY
+#endif
+
+/// end ffmpeg 0.5 / 0.6 support
+
#define GAP_HELP_ID_FFMPEG_PARAMS "plug-in-gap-encpar-ffmpeg"
#define GAP_PLUGIN_NAME_FFMPEG_PARAMS "plug-in-gap-encpar-ffmpeg"
@@ -355,6 +377,20 @@ typedef struct {
gdouble rc_max_available_vbv_use;
gdouble rc_min_vbv_overflow_use;
+ gint32 color_primaries; // enum AVColorPrimaries color_primaries;
+ gint32 color_trc; // enum AVColorTransferCharacteristic color_trc;
+ gint32 colorspace; // enum AVColorSpace colorspace;
+ gint32 color_range; // enum AVColorRange color_range;
+ gint32 chroma_sample_location; // enum AVChromaLocation chroma_sample_location;
+ gint32 weighted_p_pred; // int weighted_p_pred;
+ gint32 aq_mode; // int aq_mode;
+ gdouble aq_strength; // float aq_strength;
+ gdouble psy_rd; // float psy_rd;
+ gdouble psy_trellis; // float psy_trellis;
+ gint32 rc_lookahead; // int rc_lookahead;
+
+
+
gint32 codec_FLAG_GMC;
gint32 codec_FLAG_INPUT_PRESERVED;
gint32 codec_FLAG_GRAY;
@@ -377,6 +413,10 @@ typedef struct {
gint32 codec_FLAG2_CHUNKS;
gint32 codec_FLAG2_NON_LINEAR_QUANT;
gint32 codec_FLAG2_BIT_RESERVOIR;
+ gint32 codec_FLAG2_MBTREE;
+ gint32 codec_FLAG2_PSY;
+ gint32 codec_FLAG2_SSIM;
+
} GapGveFFMpegValues;
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
index 2d38d3e..8855834 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
@@ -207,6 +207,18 @@ p_set_master_keywords(GapValKeyList *keylist, GapGveFFMpegValues *epp)
gap_val_set_keyword(keylist, "(rc_max_available_vbv_use ", &epp->rc_max_available_vbv_use, GAP_VAL_GDOUBLE, 0, "# Ratecontrol attempt to use, at maximum, <value> of what can be used without an underflow");
gap_val_set_keyword(keylist, "(rc_min_vbv_overflow_use ", &epp->rc_min_vbv_overflow_use, GAP_VAL_GDOUBLE, 0, "# Ratecontrol attempt to use, at least, <value> times the amount needed to prevent a vbv overflow");
+ /* new params (introduced with ffmpeg 0.6) */
+ gap_val_set_keyword(keylist, "(color_primaries ", &epp->color_primaries, GAP_VAL_GINT32, 0, "# Chromaticity coordinates of the source primaries.");
+ gap_val_set_keyword(keylist, "(color_trc ", &epp->color_trc, GAP_VAL_GINT32, 0, "# Color Transfer Characteristic.");
+ gap_val_set_keyword(keylist, "(colorspace ", &epp->colorspace, GAP_VAL_GINT32, 0, "# YUV colorspace type.");
+ gap_val_set_keyword(keylist, "(color_range ", &epp->color_range, GAP_VAL_GINT32, 0, "# MPEG vs JPEG YUV range.");
+ gap_val_set_keyword(keylist, "(chroma_sample_location ", &epp->chroma_sample_location, GAP_VAL_GINT32, 0, "# This defines the location of chroma samples.");
+ gap_val_set_keyword(keylist, "(weighted_p_pred ", &epp->weighted_p_pred, GAP_VAL_GINT32, 0, "# explicit P-frame weighted prediction analysis method 0:off, 1: fast blind weighting, 2:smart weighting (full fade detection analysis)");
+ gap_val_set_keyword(keylist, "(aq_mode ", &epp->aq_mode, GAP_VAL_GINT32, 0, "# AQ mode");
+ gap_val_set_keyword(keylist, "(aq_strength ", &epp->aq_strength, GAP_VAL_GDOUBLE, 0, "# AQ strength, Reduces blocking and blurring in flat and textured areas.");
+ gap_val_set_keyword(keylist, "(psy_rd ", &epp->psy_rd, GAP_VAL_GDOUBLE, 0, "# PSY RD Strength of psychovisual optimization ");
+ gap_val_set_keyword(keylist, "(psy_trellis ", &epp->psy_trellis, GAP_VAL_GDOUBLE, 0, "# PSY trellis Strength of psychovisual optimization");
+ gap_val_set_keyword(keylist, "(rc_lookahead ", &epp->rc_lookahead, GAP_VAL_GINT32, 0, "# Number of frames for frametype and ratecontrol lookahead");
/* codec flags */
@@ -254,6 +266,13 @@ p_set_master_keywords(GapValKeyList *keylist, GapGveFFMpegValues *epp)
p_set_keyword_bool32(keylist, "(use_non_linear_quant ", &epp->codec_FLAG2_NON_LINEAR_QUANT, "# CODEC_FLAG2_NON_LINEAR_QUANT Use MPEG-2 nonlinear quantizer");
p_set_keyword_bool32(keylist, "(use_bit_reservoir ", &epp->codec_FLAG2_BIT_RESERVOIR, "# CODEC_FLAG2_BIT_RESERVOIR Use a bit reservoir when encoding if possible");
+ /* codec flags new in ffmpeg-0.6 */
+
+ p_set_keyword_bool32(keylist, "(use_bit_reservoir ", &epp->codec_FLAG2_BIT_RESERVOIR, "# CODEC_FLAG2_BIT_RESERVOIR Use a bit reservoir when encoding if possible");
+ p_set_keyword_bool32(keylist, "(use_bit_mbtree ", &epp->codec_FLAG2_MBTREE, "# CODEC_FLAG2_MBTREE Use macroblock tree ratecontrol (x264 only)");
+ p_set_keyword_bool32(keylist, "(use_bit_psy ", &epp->codec_FLAG2_PSY, "# CODEC_FLAG2_PSY Use psycho visual optimizations.");
+ p_set_keyword_bool32(keylist, "(use_bit_ssim ", &epp->codec_FLAG2_SSIM, "# CODEC_FLAG2_SSIM Compute SSIM during encoding, error[] values are undefined");
+
} /* end p_set_master_keywords */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]