[vlc-devel] [PATCH 5/7] Puzzle: main filter update

Vianney Boyer vlcvboyer at gmail.com
Mon Apr 15 14:17:47 CEST 2013


From: Vianney BOYER <vlcvboyer at gmail.com>
Date: Sun, 14 Apr 2013 12:34:55 +0200
Subject: [PATCH 5/7] Puzzle: main filter update

---
  modules/video_filter/Modules.am |   12 +-
  modules/video_filter/puzzle.c   |  919 
++++++++++++++++++++++++++++-----------
  modules/video_filter/puzzle.h   |   78 ++++
  3 files changed, 748 insertions(+), 261 deletions(-)
  create mode 100644 modules/video_filter/puzzle.h

diff --git a/modules/video_filter/Modules.am 
b/modules/video_filter/Modules.am
index 9810c21..183009a 100644
--- a/modules/video_filter/Modules.am
+++ b/modules/video_filter/Modules.am
@@ -52,6 +52,16 @@ if HAVE_GCRYPT
  libvlc_LTLIBRARIES += libremoteosd_plugin.la
  endif

+libpuzzle_plugin_la_SOURCES = \
+    puzzle.c puzzle.h \
+    puzzle_bezier.c puzzle_bezier.h \
+    puzzle_lib.c puzzle_lib.h \
+    puzzle_mgt.c puzzle_mgt.h \
+    puzzle_pce.c puzzle_pce.h
+libpuzzle_plugin_la_CFLAGS = $(AM_CFLAGS)
+libpuzzle_plugin_la_LIBADD = $(AM_LIBADD)
+libvlc_LTLIBRARIES += libpuzzle_plugin.la
+
  SOURCES_magnify = magnify.c
  SOURCES_wave = wave.c
  SOURCES_ripple = ripple.c
@@ -75,7 +85,6 @@ SOURCES_rotate = \
      $(motion_extra) \
      $(NULL)

-SOURCES_puzzle = puzzle.c
  SOURCES_colorthres = colorthres.c
  SOURCES_extract = extract.c
  SOURCES_sharpen = sharpen.c
@@ -149,7 +158,6 @@ libvlc_LTLIBRARIES += \
      libmotiondetect_plugin.la \
      libposterize_plugin.la \
      libpsychedelic_plugin.la \
-    libpuzzle_plugin.la \
      libripple_plugin.la \
      librotate_plugin.la \
      librss_plugin.la \
diff --git a/modules/video_filter/puzzle.c b/modules/video_filter/puzzle.c
index 713d1f2..746e793 100644
--- a/modules/video_filter/puzzle.c
+++ b/modules/video_filter/puzzle.c
@@ -2,9 +2,11 @@
   * puzzle.c : Puzzle game
*****************************************************************************
   * Copyright (C) 2005-2009 VLC authors and VideoLAN
+ * Copyright (C) 2013      Vianney Boyer
   * $Id$
   *
   * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
+ *          Vianney Boyer <vlcvboyer -at- gmail -dot- com>
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as 
published by
@@ -37,6 +39,12 @@

  #include "filter_picture.h"

+#include "puzzle.h"
+#include "puzzle_bezier.h"
+#include "puzzle_lib.h"
+#include "puzzle_pce.h"
+#include "puzzle_mgt.h"
+
  /*****************************************************************************
   * Module descriptor
*****************************************************************************/
@@ -44,13 +52,32 @@
  #define ROWS_LONGTEXT N_("Number of puzzle rows")
  #define COLS_TEXT N_("Number of puzzle columns")
  #define COLS_LONGTEXT N_("Number of puzzle columns")
-#define BLACKSLOT_TEXT N_("Make one tile a black slot")
-#define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can 
only be swapped with the black slot.")
+#define MODE_TEXT N_("Game mode")
+#define MODE_LONGTEXT N_("Select game mode variation from jigsaw puzzle 
to sliding puzzle.")
+#define BORDER_TEXT N_("Border")
+#define BORDER_LONGTEXT N_("Unshuffled Border width.")
+#define PREVIEW_TEXT N_("Small preview")
+#define PREVIEW_LONGTEXT N_("Show small preview.")
+#define PREVIEWSIZE_TEXT N_("Small preview size")
+#define PREVIEWSIZE_LONGTEXT N_("Show small preview size (percent of 
source).")
+#define SHAPE_SIZE_TEXT N_("Piece edge shape size")
+#define SHAPE_SIZE_LONGTEXT N_("Size of the curve along the piece's edge")
+#define AUTO_SHUFFLE_TEXT N_("Auto shuffle")
+#define AUTO_SHUFFLE_LONGTEXT N_("Auto shuffle delay during game")
+#define AUTO_SOLVE_TEXT N_("Auto solve")
+#define AUTO_SOLVE_LONGTEXT N_("Auto solve delay during game")
+#define ROTATION_TEXT N_("Rotation")
+#define ROTATION_LONGTEXT N_("Rotation parameter: none;180;90-270;mirror")
+
+const int pi_mode_values[] = { (int) 0, (int) 1, (int) 2, (int) 3 };
+const char *const ppsz_mode_descriptions[] = { N_("jigsaw puzzle"), 
N_("sliding puzzle"), N_("swap puzzle"), N_("exchange puzzle") };
+const int pi_rotation_values[] = { (int) 0, (int) 1, (int) 2, (int) 3 };
+const char *const ppsz_rotation_descriptions[] = { N_("0"), 
N_("0/180"), N_("0/90/180/270"), N_("0/90/180/270/mirror") };

  #define CFG_PREFIX "puzzle-"

-static int  Open ( vlc_object_t * );
-static void Close( vlc_object_t * );
+int  Open ( vlc_object_t * );
+void Close( vlc_object_t * );

  vlc_module_begin()
      set_description( N_("Puzzle interactive game video filter") )
@@ -63,107 +90,108 @@ vlc_module_begin()
                              ROWS_TEXT, ROWS_LONGTEXT, false )
      add_integer_with_range( CFG_PREFIX "cols", 4, 2, 16,
                              COLS_TEXT, COLS_LONGTEXT, false )
-    add_bool( CFG_PREFIX "black-slot", false,
-              BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false )
+    add_integer_with_range( CFG_PREFIX "border", 3, 0, 40,
+              BORDER_TEXT, BORDER_LONGTEXT, false )
+    add_bool( CFG_PREFIX "preview", false,
+              PREVIEW_TEXT, PREVIEW_LONGTEXT, false )
+    add_integer_with_range( CFG_PREFIX "preview-size", 15, 0, 100,
+              PREVIEWSIZE_TEXT, PREVIEWSIZE_LONGTEXT, false )
+    add_integer_with_range( CFG_PREFIX "shape-size", 90, 0, 100,
+              SHAPE_SIZE_TEXT, SHAPE_SIZE_LONGTEXT, false )
+    add_integer_with_range( CFG_PREFIX "auto-shuffle", 0, 0, 30000,
+              AUTO_SHUFFLE_TEXT, AUTO_SHUFFLE_LONGTEXT, false )
+    add_integer_with_range( CFG_PREFIX "auto-solve", 0, 0, 30000,
+              AUTO_SOLVE_TEXT, AUTO_SOLVE_LONGTEXT, false )
+    add_integer( CFG_PREFIX "rotation", 0,
+              ROTATION_TEXT, ROTATION_LONGTEXT, false )
+        change_integer_list(pi_rotation_values, 
ppsz_rotation_descriptions )
+    add_integer( CFG_PREFIX "mode", 0,
+              MODE_TEXT, MODE_LONGTEXT, false )
+        change_integer_list(pi_mode_values, ppsz_mode_descriptions )

      set_callbacks( Open, Close )
  vlc_module_end()

-
  /*****************************************************************************
   * Local prototypes
*****************************************************************************/
-static const char *const ppsz_filter_options[] = {
-    "rows", "cols", "black-slot", NULL
-};

-static picture_t *Filter( filter_t *, picture_t * );
-static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t *, const 
vlc_mouse_t * );
-
-static bool IsFinished( filter_sys_t * );
-static void Shuffle( filter_sys_t * );
-static int PuzzleCallback( vlc_object_t *, char const *,
-                           vlc_value_t, vlc_value_t, void * );
-
-struct filter_sys_t
-{
-    /* */
-    int i_cols;
-    int i_rows;
-    bool b_blackslot;
-    int *pi_order;
-    int i_selected;
-    bool b_finished;
-
-    /* */
-    struct
-    {
-        atomic_flag b_uptodate;
-        atomic_bool b_blackslot;
-        atomic_uint i_cols;
-        atomic_uint i_rows;
-    } change;
+const char *const ppsz_filter_options[] = {
+    "rows", "cols","border", "preview", "preview-size", "mode", 
"shape-size", "auto-shuffle", "auto-solve",  "rotation", NULL
  };

-#define SHUFFLE_WIDTH 81
-#define SHUFFLE_HEIGHT 13
-static const char *shuffle_button[] =
-{
-".................................................................................",
-"..............  ............................   ........   ...... 
...............",
-"..............  ...........................  .........  ........ 
...............",
-"..............  ...........................  .........  ........ 
...............",
-"..     .......  .    .......  ....  ......     ......     ...... 
........    ...",
-".  .... ......   ...  ......  ....  .......  .........  ........ 
.......  ..  ..",
-".  ...........  ....  ......  ....  .......  .........  ........ 
......  ....  .",
-".      .......  ....  ......  ....  .......  .........  ........ 
......        .",
-"..      ......  ....  ......  ....  .......  .........  ........ 
......  .......",
-"......  ......  ....  ......  ....  .......  .........  ........ 
......  .......",
-". ....  ......  ....  ......  ...   .......  .........  ........ 
.......  .... .",
-"..     .......  ....  .......    .  .......  .........  ........ 
........     ..",
-"................................................................................."
-};
-
-
  /**
   * Open the filter
   */
-static int Open( vlc_object_t *p_this )
+int Open( vlc_object_t *p_this )
  {
      filter_t *p_filter = (filter_t *)p_this;
      filter_sys_t *p_sys;

-    /* */
-    if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
-    {
+    /* Assert video in match with video out */
+    if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
          msg_Err( p_filter, "Input and output format does not match" );
          return VLC_EGENERIC;
      }

      /* Allocate structure */
-    p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
+    p_filter->p_sys = p_sys = calloc(1, sizeof( *p_sys ) );
      if( !p_sys )
          return VLC_ENOMEM;

+    p_sys->b_shuffle_rqst     = true;
+    p_sys->b_change_param    = true;
+    p_sys->i_mouse_drag_pce  = NO_PCE;
+    p_sys->i_pointed_pce     = NO_PCE;
+    p_sys->i_magnet_accuracy = 3;
+
      config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                         p_filter->p_cfg );

-    p_sys->pi_order = NULL;
-
-    atomic_init( &p_sys->change.i_rows,
-                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX 
"rows" ) );
-    atomic_init( &p_sys->change.i_cols,
-                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX 
"cols" ) );
-    atomic_init( &p_sys->change.b_blackslot,
-               var_CreateGetBoolCommand( p_filter, CFG_PREFIX 
"black-slot" ) );
-    p_sys->change.b_uptodate = ATOMIC_FLAG_INIT;
-
-    var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
-    var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
-    var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, 
p_sys );
+    vlc_mutex_init( &p_sys->lock );
+    vlc_mutex_init( &p_sys->pce_lock );
+
+    p_sys->s_new_param.i_rows =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" );
+    p_sys->s_new_param.i_cols =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" );
+    p_sys->s_new_param.i_border =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "border" );
+    p_sys->s_new_param.b_preview =
+        var_CreateGetBoolCommand( p_filter, CFG_PREFIX "preview" );
+    p_sys->s_new_param.i_preview_size =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "preview-size" );
+    p_sys->s_new_param.i_shape_size =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "shape-size" );
+    p_sys->s_new_param.i_auto_shuffle_speed =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "auto-shuffle" );
+    p_sys->s_new_param.i_auto_solve_speed =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "auto-solve" );
+    p_sys->s_new_param.i_rotate =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rotation" );
+    p_sys->s_new_param.i_mode =
+        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "mode" );
+
+    var_AddCallback( p_filter, CFG_PREFIX "rows", puzzle_Callback, p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "cols", puzzle_Callback, p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "border", puzzle_Callback, 
p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "preview", puzzle_Callback, 
p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "preview-size", 
puzzle_Callback, p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "shape-size", 
puzzle_Callback, p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "auto-shuffle", 
puzzle_Callback, p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "auto-solve", 
puzzle_Callback, p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "rotation", puzzle_Callback, 
p_sys );
+    var_AddCallback( p_filter, CFG_PREFIX "mode", puzzle_Callback, p_sys );

      p_filter->pf_video_filter = Filter;
-    p_filter->pf_video_mouse = Mouse;
+    p_filter->pf_video_mouse = puzzle_mouse;
+
+    /* Generate values of random bezier shapes */
+    p_sys->ps_bezier_pts_H = calloc( SHAPES_QTY, sizeof( point_t *) );
+    if( !p_sys->ps_bezier_pts_H )
+        return VLC_ENOMEM;
+    for (int32_t i_shape = 0; i_shape<SHAPES_QTY; i_shape++)
+        p_sys->ps_bezier_pts_H[i_shape] = puzzle_rand_bezier(7);

      return VLC_SUCCESS;
  }
@@ -171,271 +199,644 @@ static int Open( vlc_object_t *p_this )
  /**
   * Close the filter
   */
-static void Close( vlc_object_t *p_this )
-{
+void Close( vlc_object_t *p_this ) {
      filter_t *p_filter = (filter_t *)p_this;
      filter_sys_t *p_sys = p_filter->p_sys;

-    var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
-    var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
-    var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, 
p_sys );
-
+    var_DelCallback( p_filter, CFG_PREFIX "rows", puzzle_Callback, p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "cols", puzzle_Callback, p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "border", puzzle_Callback, 
p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "preview", puzzle_Callback, 
p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "preview-size", 
puzzle_Callback, p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "shape-size", 
puzzle_Callback, p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "auto-shuffle", 
puzzle_Callback, p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "auto-solve", 
puzzle_Callback, p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "rotation", puzzle_Callback, 
p_sys );
+    var_DelCallback( p_filter, CFG_PREFIX "mode", puzzle_Callback, p_sys );
+
+    vlc_mutex_destroy( &p_sys->lock );
+    vlc_mutex_destroy( &p_sys->pce_lock );
+
+    /* Free allocated memory */
+    puzzle_free_ps_puzzle_array ( p_filter );
+    puzzle_free_ps_pieces_shapes ( p_filter);
+    puzzle_free_ps_pieces ( p_filter );
+    free(p_sys->ps_desk_planes);
+    free(p_sys->ps_pict_planes);
      free( p_sys->pi_order );

+    for (int32_t i_shape = 0; i_shape<SHAPES_QTY; i_shape++)
+        free(p_sys->ps_bezier_pts_H[i_shape]);
+    free(p_sys->ps_bezier_pts_H);
+
      free( p_sys );
  }

  /**
   * Filter a picture
   */
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
-{
+picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
+    if( !p_pic_in || !p_filter) return NULL;
+
+    const video_format_t  *p_fmt_in = &p_filter->fmt_in.video;
      filter_sys_t *p_sys = p_filter->p_sys;

-    picture_t *p_outpic = filter_NewPicture( p_filter );
-    if( !p_outpic )
-    {
-        picture_Release( p_pic );
+    picture_t *p_pic_out = filter_NewPicture( p_filter );
+    if( !p_pic_out ) {
+        picture_Release( p_pic_in );
          return NULL;
      }

-    /* */
-    if( !atomic_flag_test_and_set( &p_sys->change.b_uptodate ) )
-    {
-        p_sys->i_rows      = atomic_load( &p_sys->change.i_rows );
-        p_sys->i_cols      = atomic_load( &p_sys->change.i_cols );
-        p_sys->b_blackslot = atomic_load( &p_sys->change.b_blackslot );
-        Shuffle( p_sys );
+    int i_ret = 0;
+    p_sys->b_bake_request = false;
+
+    if ((p_sys->pi_order == NULL) || (p_sys->ps_desk_planes == NULL) || 
(p_sys->ps_pict_planes == NULL)  || (p_sys->ps_puzzle_array == NULL) || 
(p_sys->ps_pieces == NULL))
+        p_sys->b_init = false;
+
+    if ((p_sys->ps_pieces_shapes == NULL) && 
p_sys->s_current_param.b_advanced && 
(p_sys->s_current_param.i_shape_size != 0))
+        p_sys->b_init = false;
+
+    /* assert initialized & allocated data match with current frame 
characteristics */
+    if ( p_sys->s_allocated.i_planes != p_pic_out->i_planes)
+        p_sys->b_init = false;
+    p_sys->s_current_param.i_planes = p_pic_out->i_planes;
+    if (p_sys->ps_pict_planes != NULL) {
+        for (uint8_t i_plane = 0; i_plane < 
p_sys->s_allocated.i_planes; i_plane++) {
+            if ( (p_sys->ps_pict_planes[i_plane].i_lines != 
p_pic_in->p[i_plane].i_visible_lines)
+                    || (p_sys->ps_pict_planes[i_plane].i_width != 
p_pic_in->p[i_plane].i_visible_pitch / p_pic_in->p[i_plane].i_pixel_pitch)
+                    || (p_sys->ps_desk_planes[i_plane].i_lines != 
p_pic_out->p[i_plane].i_visible_lines)
+                    || (p_sys->ps_desk_planes[i_plane].i_width != 
p_pic_out->p[i_plane].i_visible_pitch / 
p_pic_out->p[i_plane].i_pixel_pitch) )
+                p_sys->b_init = false;
+        }
      }

-    /* */
-    const int i_rows = p_sys->i_rows;
-    const int i_cols = p_sys->i_cols;
+    p_sys->s_current_param.i_pict_width  = (int) 
p_pic_in->p[0].i_visible_pitch / p_pic_in->p[0].i_pixel_pitch;
+    p_sys->s_current_param.i_pict_height = (int) 
p_pic_in->p[0].i_visible_lines;
+    p_sys->s_current_param.i_desk_width  = (int) 
p_pic_out->p[0].i_visible_pitch / p_pic_out->p[0].i_pixel_pitch;
+    p_sys->s_current_param.i_desk_height = (int) 
p_pic_out->p[0].i_visible_lines;

-    /* Draw each piece of the puzzle at the right place */
-    for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
-    {
-        const plane_t *p_in = &p_pic->p[i_plane];
-        plane_t *p_out = &p_outpic->p[i_plane];
-
-        for( int i = 0; i < i_cols * i_rows; i++ )
-        {
-            int i_piece_height = p_out->i_visible_lines / i_rows;
-            int i_piece_width  = p_out->i_visible_pitch / i_cols;
+    /* assert no mismatch between sizes */
+    if (    p_sys->s_current_param.i_pict_width  != 
p_sys->s_current_param.i_desk_width
+         || p_sys->s_current_param.i_pict_height != 
p_sys->s_current_param.i_desk_height
+         || p_sys->s_current_param.i_pict_width  != (int) p_fmt_in->i_width
+         || p_sys->s_current_param.i_pict_height != (int) 
p_fmt_in->i_height )
+        return NULL;

-            int i_col = (i % i_cols) * i_piece_width;
-            int i_row = (i / i_cols) * i_piece_height;
-            int i_last_row = i_row + i_piece_height;
+    vlc_mutex_lock( &p_sys->lock );

-            int i_ocol = (p_sys->pi_order[i] % i_cols) * i_piece_width;
-            int i_orow = (p_sys->pi_order[i] / i_cols) * i_piece_height;
+    /* check if we have to compute initial data */
+    if ( p_sys->b_change_param || p_sys->b_bake_request || 
!p_sys->b_init ) {
+        if ( p_sys->s_allocated.i_rows != p_sys->s_new_param.i_rows
+                || p_sys->s_allocated.i_cols != p_sys->s_new_param.i_cols
+                || p_sys->s_allocated.i_rotate != 
p_sys->s_new_param.i_rotate
+                || p_sys->s_allocated.i_mode != p_sys->s_new_param.i_mode
+                || p_sys->b_bake_request  || !p_sys->b_init )
+        {
+            p_sys->b_bake_request = true;
+            p_sys->b_init = false;
+            p_sys->b_shuffle_rqst = true;
+            p_sys->b_shape_init = false;
+        }

-            if( p_sys->b_blackslot && !p_sys->b_finished && i == 
p_sys->i_selected )
-            {
-                uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
-                for( int r = i_row; r < i_last_row; r++ )
-                {
-                    memset( p_out->p_pixels + r * p_out->i_pitch + i_col,
-                            color, i_piece_width );
-                }
-            }
-            else
-            {
-                for( int r = i_row, or = i_orow; r < i_last_row; r++, 
or++ )
-                {
-                    memcpy( p_out->p_pixels + r * p_out->i_pitch + i_col,
-                            p_in->p_pixels + or * p_in->i_pitch  + i_ocol,
-                            i_piece_width );
-                }
-            }
+        if ( p_sys->s_current_param.i_border != p_sys->s_new_param.i_border
+                || p_sys->s_current_param.i_shape_size != 
p_sys->s_new_param.i_shape_size )
+        {
+            p_sys->b_bake_request = true;
+            p_sys->b_shape_init = false;
+        }

-            /* Draw the borders of the selected slot */
-            if( i_plane == 0 && !p_sys->b_blackslot && 
p_sys->i_selected == i )
-            {
-                memset( p_out->p_pixels + i_row * p_out->i_pitch + i_col,
-                        0xff, i_piece_width );
-                for( int r = i_row; r < i_last_row; r++ )
-                {
-                    p_out->p_pixels[r * p_out->i_pitch + i_col + 
0             + 0 ] = 0xff;
-                    p_out->p_pixels[r * p_out->i_pitch + i_col + 
i_piece_width - 1 ] = 0xff;
-                }
-                memset( p_out->p_pixels + (i_last_row - 1) * 
p_out->i_pitch + i_col,
-                        0xff, i_piece_width );
-            }
+        /* depending on the game selected, set associated internal flags */
+        switch ( p_sys->s_new_param.i_mode )
+        {
+          case 0:  /* jigsaw puzzle */
+            p_sys->s_new_param.b_advanced    = true;
+            p_sys->s_new_param.b_blackslot   = false;
+            p_sys->s_new_param.b_near        = false;
+            break;
+          case 1:  /* sliding puzzle */
+            p_sys->s_new_param.b_advanced    = false;
+            p_sys->s_new_param.b_blackslot   = true;
+            p_sys->s_new_param.b_near        = true;
+            break;
+          case 2:  /* swap puzzle */
+            p_sys->s_new_param.b_advanced    = false;
+            p_sys->s_new_param.b_blackslot   = false;
+            p_sys->s_new_param.b_near        = true;
+            break;
+          case 3:  /* exchange puzzle */
+            p_sys->s_new_param.b_advanced    = false;
+            p_sys->s_new_param.b_blackslot   = false;
+            p_sys->s_new_param.b_near        = false;
+            break;
+        }
+        p_sys->s_current_param.i_mode = p_sys->s_new_param.i_mode;
+
+        if ( p_sys->s_current_param.b_blackslot != 
p_sys->s_new_param.b_blackslot
+                && p_sys->i_selected == NO_PCE
+                && p_sys->s_current_param.b_blackslot )
+            p_sys->i_selected = 0;
+
+        if ( p_sys->s_current_param.i_auto_shuffle_speed != 
p_sys->s_new_param.i_auto_shuffle_speed )
+            p_sys->i_auto_shuffle_countdown_val = 
init_countdown(p_sys->s_new_param.i_auto_shuffle_speed);
+
+        if ( p_sys->s_current_param.i_auto_solve_speed != 
p_sys->s_new_param.i_auto_solve_speed )
+            p_sys->i_auto_solve_countdown_val = 
init_countdown(p_sys->s_current_param.i_auto_solve_speed);
+
+        p_sys->s_current_param.i_rows       = p_sys->s_new_param.i_rows;
+        p_sys->s_current_param.i_cols       = p_sys->s_new_param.i_cols;
+        p_sys->s_current_param.i_pieces_nbr = 
p_sys->s_current_param.i_rows * p_sys->s_current_param.i_cols;
+        p_sys->s_current_param.b_advanced   = 
p_sys->s_new_param.b_advanced;
+        if (!p_sys->s_new_param.b_advanced) {
+            p_sys->s_current_param.b_blackslot   = 
p_sys->s_new_param.b_blackslot;
+            p_sys->s_current_param.b_near        = 
p_sys->s_new_param.b_near || p_sys->s_new_param.b_blackslot;
+            p_sys->s_current_param.i_border      = 0;
+            p_sys->s_current_param.b_preview     = false;
+            p_sys->s_current_param.i_preview_size= 0;
+            p_sys->s_current_param.i_shape_size  = 0;
+            p_sys->s_current_param.i_auto_shuffle_speed  = 0;
+            p_sys->s_current_param.i_auto_solve_speed    = 0;
+            p_sys->s_current_param.i_rotate      = 0;
          }
+        else
+        {
+            p_sys->s_current_param.b_blackslot = false;
+            p_sys->s_current_param.b_near      = false;
+            p_sys->s_current_param.i_border    = 
p_sys->s_new_param.i_border;
+            p_sys->s_current_param.b_preview   = 
p_sys->s_new_param.b_preview;
+            p_sys->s_current_param.i_preview_size        = 
p_sys->s_new_param.i_preview_size;
+            p_sys->s_current_param.i_shape_size          = 
p_sys->s_new_param.i_shape_size;
+            p_sys->s_current_param.i_auto_shuffle_speed  = 
p_sys->s_new_param.i_auto_shuffle_speed;
+            p_sys->s_current_param.i_auto_solve_speed    = 
p_sys->s_new_param.i_auto_solve_speed;
+            p_sys->s_current_param.i_rotate     = 
p_sys->s_new_param.i_rotate;
+        }
+        p_sys->b_change_param = false;
      }

-    /* Draw the 'Shuffle' button if the puzzle is finished */
-    if( p_sys->b_finished )
-    {
-        plane_t *p_out = &p_outpic->p[Y_PLANE];
-        for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
-        {
-            for( int j = 0; j < SHUFFLE_WIDTH; j++ )
+    vlc_mutex_unlock( &p_sys->lock );
+
+    /* generate initial puzzle data when needed */
+    if ( p_sys->b_bake_request ) {
+        if (!p_sys->b_shuffle_rqst) {
+            /* here we have to keep the same position
+             * we have to save locations before generating new data
+             */
+            save_game_t *ps_save_game = puzzle_save(p_filter);
+            if (!ps_save_game)
+                return CopyInfoAndRelease( p_pic_out, p_pic_in );
+            i_ret = puzzle_bake( p_filter, p_pic_out, p_pic_in );
+            if ( i_ret != VLC_SUCCESS )
              {
-                if( shuffle_button[i][j] == '.' )
-                   p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
+                free(ps_save_game->ps_pieces);
+                free(ps_save_game);
+                return CopyInfoAndRelease( p_pic_out, p_pic_in );
              }
+            puzzle_load( p_filter, ps_save_game);
+            free(ps_save_game->ps_pieces);
+            free(ps_save_game);
+        }
+        else {
+            i_ret = puzzle_bake( p_filter, p_pic_out, p_pic_in );
+            if ( i_ret != VLC_SUCCESS )
+                return CopyInfoAndRelease( p_pic_out, p_pic_in );
          }
      }

-    return CopyInfoAndRelease( p_outpic, p_pic );
-}
+    /* shuffle the desk and generate pieces data  */
+    if ( p_sys->b_shuffle_rqst && p_sys->b_init ) {
+        i_ret = puzzle_bake_piece ( p_filter );
+        if (i_ret != VLC_SUCCESS)
+            return CopyInfoAndRelease( p_pic_out, p_pic_in );
+    }

-static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
-                  const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
-{
-    filter_sys_t *p_sys = p_filter->p_sys;
-    const video_format_t  *p_fmt = &p_filter->fmt_in.video;
+    /* preset output pic */
+    if ( !p_sys->b_bake_request && !p_sys->b_shuffle_rqst && 
p_sys->b_init && !p_sys->b_finished )
+        puzzle_preset_desk_background(p_pic_out, 0, 127, 127);
+    else {
+        /* copy src to dst during init & bake process */
+        for( uint8_t i_plane = 0; i_plane < p_pic_out->i_planes; 
i_plane++ )
+            memcpy( p_pic_out->p[i_plane].p_pixels, 
p_pic_in->p[i_plane].p_pixels,
+                p_pic_in->p[i_plane].i_pitch * (int32_t) 
p_pic_in->p[i_plane].i_visible_lines );
+    }

-    /* Only take events inside the puzzle erea */
-    if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
-        p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
-        return VLC_EGENERIC;
+    vlc_mutex_lock( &p_sys->pce_lock );

-    /* */
-    const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, 
MOUSE_BUTTON_LEFT );
+    /* manage the game, adjust locations, groups and regenerate some 
corrupted data if any */
+    for (uint32_t i = 0; i < __MAX( 4, p_sys->s_allocated.i_pieces_nbr 
/ 4 )
+                             && ( !p_sys->b_bake_request && 
!p_sys->b_mouse_drag
+                             && p_sys->b_init && 
p_sys->s_current_param.b_advanced ); i++)
+    {
+        puzzle_solve_pces_accuracy( p_filter );
+    }

-    /* If the puzzle is finished, shuffle it if needed */
-    if( p_sys->b_finished )
+    for (uint32_t i = 0; i < __MAX( 4, p_sys->s_allocated.i_pieces_nbr 
/ 4 )
+                             && ( !p_sys->b_bake_request && 
!p_sys->b_mouse_drag
+                             && p_sys->b_init && 
p_sys->s_current_param.b_advanced ); i++)
      {
-        if( b_clicked &&
-            p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
-        {
-            atomic_flag_clear( &p_sys->change.b_uptodate );
-            return VLC_EGENERIC;
-        }
-        else
-        {
-            /* This is the only case where we can forward the mouse */
-            *p_mouse = *p_new;
-            return VLC_SUCCESS;
-        }
+        puzzle_solve_pces_group( p_filter );
      }
-    if( !b_clicked )
-        return VLC_EGENERIC;

-    /* */
-    const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
-    const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
-    const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
+    if ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init
+            && p_sys->s_current_param.b_advanced )
+        puzzle_count_pce_group( p_filter);
+    if ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init
+            && p_sys->s_current_param.b_advanced ) {
+        i_ret = puzzle_sort_layers( p_filter);
+        if (i_ret != VLC_SUCCESS)
+            return CopyInfoAndRelease( p_pic_out, p_pic_in );
+    }

-    if( p_sys->i_selected == -1 )
+    for (uint32_t i = 0; i < __MAX( 4, p_sys->s_allocated.i_pieces_nbr 
/ 24 )
+                            && ( !p_sys->b_bake_request && 
!p_sys->b_mouse_drag
+                            && p_sys->b_init && 
p_sys->s_current_param.b_advanced ); i++)
      {
-        p_sys->i_selected = i_pos;
+        p_sys->i_calc_corn_loop++;
+        p_sys->i_calc_corn_loop %= p_sys->s_allocated.i_pieces_nbr;
+        puzzle_calculate_corners( p_filter, p_sys->i_calc_corn_loop );
      }
-    else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
+
+    /* computer moves some piece depending on auto_solve and 
auto_shuffle param */
+    if ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init
+             && p_sys->ps_puzzle_array != NULL && 
p_sys->s_current_param.b_advanced )
      {
-        p_sys->i_selected = -1;
+        puzzle_auto_shuffle( p_filter );
+        puzzle_auto_solve( p_filter );
      }
-    else if( ( p_sys->i_selected == i_pos + 1 && 
p_sys->i_selected%p_sys->i_cols != 0 )
-          || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols 
!= 0 )
-          || p_sys->i_selected == i_pos + p_sys->i_cols
-          || p_sys->i_selected == i_pos - p_sys->i_cols )
-    {
-        /* Swap two pieces */
-        int a = p_sys->pi_order[ p_sys->i_selected ];
-        p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ i_pos ];
-        p_sys->pi_order[ i_pos ] = a;

-        p_sys->i_selected = p_sys->b_blackslot ? i_pos : -1;
-        p_sys->b_finished = IsFinished( p_sys );
+    vlc_mutex_unlock( &p_sys->pce_lock );
+
+    /* draw output pic */
+    if ( !p_sys->b_bake_request && p_sys->b_init && 
p_sys->ps_puzzle_array != NULL ) {
+
+        puzzle_draw_borders(p_filter, p_pic_in, p_pic_out);
+
+        p_sys->i_pointed_pce = NO_PCE;
+        puzzle_draw_pieces(p_filter, p_pic_in, p_pic_out);
+
+        /* when puzzle_draw_pieces() has not updated p_sys->i_pointed_pce,
+         * use puzzle_find_piece to define the piece pointed by the mouse
+         */
+        if (p_sys->i_pointed_pce == NO_PCE)
+            p_sys->i_mouse_drag_pce = puzzle_find_piece( p_filter, 
p_sys->i_mouse_x, p_sys->i_mouse_y, -1);
+        else
+            p_sys->i_mouse_drag_pce = p_sys->i_pointed_pce;
+
+        if (p_sys->s_current_param.b_preview )
+            puzzle_draw_preview(p_filter, p_pic_in, p_pic_out);
+
+        /* highlight the selected piece when not playing jigsaw mode */
+        if ( p_sys->i_selected != NO_PCE && 
!p_sys->s_current_param.b_blackslot
+                && !p_sys->s_current_param.b_advanced )
+        {
+            int32_t c = (p_sys->i_selected % p_sys->s_allocated.i_cols);
+            int32_t r = (p_sys->i_selected / p_sys->s_allocated.i_cols);
+
+            puzzle_draw_rectangle(p_pic_out,
+                p_sys->ps_puzzle_array[r][c][0].i_x,
+                p_sys->ps_puzzle_array[r][c][0].i_y,
+                p_sys->ps_puzzle_array[r][c][0].i_width,
+                p_sys->ps_puzzle_array[r][c][0].i_lines,
+                255, 127, 127);
+        }
+
+        /* draw the blackslot when playing sliding puzzle mode */
+        if ( p_sys->i_selected != NO_PCE && 
p_sys->s_current_param.b_blackslot
+                && !p_sys->s_current_param.b_advanced )
+        {
+            int32_t c = (p_sys->i_selected % p_sys->s_allocated.i_cols);
+            int32_t r = (p_sys->i_selected / p_sys->s_allocated.i_cols);
+
+            puzzle_fill_rectangle(p_pic_out,
+                p_sys->ps_puzzle_array[r][c][0].i_x,
+                p_sys->ps_puzzle_array[r][c][0].i_y,
+                p_sys->ps_puzzle_array[r][c][0].i_width,
+                p_sys->ps_puzzle_array[r][c][0].i_lines,
+                0, 127, 127);
+        }
+
+        /* Draw the 'puzzle_shuffle' button if the puzzle is finished */
+        if ( p_sys->b_finished )
+            puzzle_draw_sign(p_pic_out, 0, 0, SHUFFLE_WIDTH, 
SHUFFLE_LINES, ppsz_shuffle_button, false);
+
+        /* draw an arrow at mouse pointer to indicate current action 
(rotation...) */
+        if ((p_sys->i_mouse_drag_pce != NO_PCE) && !p_sys->b_mouse_drag
+                && !p_sys->b_finished && 
p_sys->s_current_param.b_advanced )
+        {
+            vlc_mutex_lock( &p_sys->pce_lock );
+
+            int32_t i_delta_x;
+
+            if (p_sys->s_current_param.i_rotate != 3)
+                i_delta_x = 0;
+            else if ( 
(p_sys->ps_pieces[p_sys->i_mouse_drag_pce].i_actual_angle & 1) == 0)
+                i_delta_x = p_sys->ps_desk_planes[0].i_pce_max_width / 6;
+            else
+                i_delta_x = p_sys->ps_desk_planes[0].i_pce_max_lines / 6;
+
+            if (p_sys->s_current_param.i_rotate == 0)
+                p_sys->i_mouse_action = 0;
+            else if (p_sys->s_current_param.i_rotate == 1)
+                p_sys->i_mouse_action = 2;
+            else if ( p_sys->i_mouse_x >= ( 
p_sys->ps_pieces[p_sys->i_mouse_drag_pce].i_center_x + i_delta_x) )
+                p_sys->i_mouse_action = -1;  /* rotate counterclockwise */
+            else if ( p_sys->i_mouse_x <= ( 
p_sys->ps_pieces[p_sys->i_mouse_drag_pce].i_center_x - i_delta_x) )
+                p_sys->i_mouse_action = +1;
+            else
+                p_sys->i_mouse_action = 4;   /* center click: only 
mirror */
+
+            if ( p_sys->i_mouse_action == +1 )
+                puzzle_draw_sign(p_pic_out, p_sys->i_mouse_x - ARROW_WIDTH,
+                                 p_sys->i_mouse_y, ARROW_WIDTH, 
ARROW_LINES, ppsz_rot_arrow_sign, false);
+            else if ( p_sys->i_mouse_action == -1 )
+                puzzle_draw_sign(p_pic_out, p_sys->i_mouse_x - ARROW_WIDTH,
+                                 p_sys->i_mouse_y, ARROW_WIDTH, 
ARROW_LINES, ppsz_rot_arrow_sign, true);
+            else if ( p_sys->i_mouse_action == 4 )
+                puzzle_draw_sign(p_pic_out, p_sys->i_mouse_x - ARROW_WIDTH,
+                                 p_sys->i_mouse_y, ARROW_WIDTH, 
ARROW_LINES, ppsz_mir_arrow_sign, false);
+
+            vlc_mutex_unlock( &p_sys->pce_lock );
+        }
      }
-    return VLC_EGENERIC;
+
+    return CopyInfoAndRelease( p_pic_out, p_pic_in );
  }

  /*****************************************************************************
   * Misc stuff...
*****************************************************************************/
-static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
+int puzzle_Callback( vlc_object_t *p_this, char const *psz_var,
                             vlc_value_t oldval, vlc_value_t newval,
                             void *p_data )
  {
      VLC_UNUSED(p_this); VLC_UNUSED(oldval);
-    filter_sys_t *p_sys = p_data;
+    filter_sys_t *p_sys = (filter_sys_t *)p_data;

-    if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
-        atomic_store( &p_sys->change.i_rows, __MAX( 2, newval.i_int ) );
-    else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
-        atomic_store( &p_sys->change.i_cols, __MAX( 2, newval.i_int ) );
-    else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
-        atomic_store( &p_sys->change.b_blackslot, newval.b_bool );
-    atomic_flag_clear( &p_sys->change.b_uptodate );
+    vlc_mutex_lock( &p_sys->lock );
+    if( !strcmp( psz_var, CFG_PREFIX "rows" ) ) {
+        p_sys->s_new_param.i_rows = __MAX( 1, newval.i_int );
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "cols" ) ) {
+        p_sys->s_new_param.i_cols = __MAX( 1, newval.i_int );
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "border" ) ) {
+        p_sys->s_new_param.i_border = __MAX( 0, newval.i_int );
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "preview" ) ) {
+        p_sys->s_new_param.b_preview = newval.b_bool;
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "preview-size" ) ) {
+        p_sys->s_new_param.i_preview_size = newval.i_int;
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "shape-size" ) ) {
+        p_sys->s_new_param.i_shape_size = newval.i_int;
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "auto-shuffle" ) ) {
+        p_sys->s_new_param.i_auto_shuffle_speed = newval.i_int;
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "auto-solve" ) ) {
+        p_sys->s_new_param.i_auto_solve_speed = newval.i_int;
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "rotation" ) ) {
+        p_sys->s_new_param.i_rotate = newval.i_int;
+    }
+    else if( !strcmp( psz_var, CFG_PREFIX "mode" ) ) {
+        p_sys->s_new_param.i_mode = newval.i_int;
+    }
+
+    p_sys->b_change_param = true;
+    vlc_mutex_unlock( &p_sys->lock );

      return VLC_SUCCESS;
  }

-static bool IsFinished( filter_sys_t *p_sys )
+/* mouse callback */
+int puzzle_mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
+                  const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
  {
-    for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
-    {
-        if( i != p_sys->pi_order[i] )
-            return false;
-    }
-    return true;
-}
+    filter_sys_t *p_sys = p_filter->p_sys;
+    const video_format_t  *p_fmt_in = &p_filter->fmt_in.video;

-static bool IsValid( filter_sys_t *p_sys )
-{
-    const int i_count = p_sys->i_cols * p_sys->i_rows;
+    /* Only take events inside the puzzle area */
+    if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt_in->i_width ||
+        p_new->i_y < 0 || p_new->i_y >= (int)p_fmt_in->i_height )
+        return VLC_EGENERIC;

-    if( !p_sys->b_blackslot )
-        return true;
+    if (! p_sys->b_init || p_sys->b_change_param) {
+        *p_mouse = *p_new;
+        return VLC_SUCCESS;
+    }

-    int d = 0;
-    for( int i = 0; i < i_count; i++ )
-    {
-        if( p_sys->pi_order[i] == i_count - 1 )
+    p_sys->i_mouse_x = p_new->i_x;
+    p_sys->i_mouse_y = p_new->i_y;
+
+    /* If the puzzle is finished, shuffle it if needed */
+    if( p_sys->b_finished ) {
+        p_sys->b_mouse_drag = false;
+        p_sys->b_mouse_mvt = false;
+        if( vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT ) &&
+            p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_LINES )
          {
-            d += i / p_sys->i_cols + 1;
-            continue;
+            p_sys->b_shuffle_rqst = true;
+            return VLC_EGENERIC;
          }
-        for( int j = i+1; j < i_count; j++ )
+        else
          {
-            if( p_sys->pi_order[j] == i_count - 1 )
-                continue;
-            if( p_sys->pi_order[i] > p_sys->pi_order[j] )
-                d++;
+            /* otherwise we can forward the mouse */
+            *p_mouse = *p_new;
+            return VLC_SUCCESS;
          }
      }
-    return (d%2) == 0;
-}

-static void Shuffle( filter_sys_t *p_sys )
-{
-    const unsigned i_count = p_sys->i_cols * p_sys->i_rows;
+    if ( !p_sys->s_current_param.b_advanced ) {
+        /* "square" game mode (sliding puzzle, swap...) */
+        const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, 
MOUSE_BUTTON_LEFT );

-    free( p_sys->pi_order );
+        if( b_clicked )
+        {
+            /* */
+            const int32_t i_border_width = p_fmt_in->i_width * 
p_sys->s_current_param.i_border / 100 / 2;
+            const int32_t i_border_height = p_fmt_in->i_height * 
p_sys->s_current_param.i_border / 100 / 2;
+            const int32_t i_pos_x = (p_new->i_x - i_border_width) * 
p_sys->s_allocated.i_cols / (p_fmt_in->i_width - 2*i_border_width);
+            const int32_t i_pos_y = (p_new->i_y - i_border_height) * 
p_sys->s_allocated.i_rows / (p_fmt_in->i_height - 2*i_border_height);
+
+            const int32_t i_pos = i_pos_y * p_sys->s_allocated.i_cols + 
i_pos_x;
+            p_sys->i_mouse_drag_pce = i_pos;

-    p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
-    do
+            /* do not take into account if border clicked */
+            if ((p_new->i_x <= i_border_width) || (p_new->i_y <=  
i_border_height) || (p_new->i_x >= (int) p_fmt_in->i_width -  
i_border_width) || (p_new->i_y >= (int) p_fmt_in->i_height -  
i_border_height ) )
+            {
+                *p_mouse = *p_new;
+                return VLC_SUCCESS;
+            }
+            else if( p_sys->i_selected == NO_PCE )
+                p_sys->i_selected = i_pos;
+            else if( p_sys->i_selected == i_pos && 
!p_sys->s_current_param.b_blackslot )
+                p_sys->i_selected = -1;
+            else if( ( p_sys->i_selected == i_pos + 1 && 
p_sys->i_selected%p_sys->s_allocated.i_cols != 0 )
+                  || ( p_sys->i_selected == i_pos - 1 && i_pos % 
p_sys->s_allocated.i_cols != 0 )
+                  || p_sys->i_selected == i_pos + p_sys->s_allocated.i_cols
+                  || p_sys->i_selected == i_pos - p_sys->s_allocated.i_cols
+                  || !p_sys->s_current_param.b_near )
+
+            {
+                /* Swap two pieces */
+                int32_t a = p_sys->pi_order[ p_sys->i_selected ];
+                p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ 
i_pos ];
+                p_sys->pi_order[ i_pos ] = a;
+
+                /* regen piece location from updated pi_order */
+                if ( p_sys->ps_pieces != NULL && p_sys->pi_order != NULL )
+                {
+                    int32_t i = 0;
+                    for (int32_t row = 0; row < 
p_sys->s_allocated.i_rows; row++) {
+                        for (int32_t col = 0; col < 
p_sys->s_allocated.i_cols; col++) {
+                            int32_t orow = p_sys->pi_order[i] / 
(p_sys->s_allocated.i_cols);
+                            int32_t ocol = p_sys->pi_order[i] % 
(p_sys->s_allocated.i_cols);
+
+                            p_sys->ps_pieces[i].i_original_row = orow;
+                            p_sys->ps_pieces[i].i_original_col = ocol;
+                            p_sys->ps_pieces[i].i_top_shape    = 0;
+                            p_sys->ps_pieces[i].i_btm_shape    = 0;
+                            p_sys->ps_pieces[i].i_right_shape  = 0;
+                            p_sys->ps_pieces[i].i_left_shape   = 0;
+                            p_sys->ps_pieces[i].i_actual_angle = 0;
+                            p_sys->ps_pieces[i].i_actual_mirror = +1;
+                            p_sys->ps_pieces[i].b_overlap      = false;
+                            p_sys->ps_pieces[i].b_finished     = false;
+                            p_sys->ps_pieces[i].i_group_ID     = i;
+
+                            for (uint8_t i_plane = 0; i_plane < 
p_sys->s_allocated.i_planes; i_plane++) {
+ p_sys->ps_pieces[i].ps_piece_in_plane[i_plane].i_width     = 
p_sys->ps_puzzle_array[row][col][i_plane].i_width;
+ p_sys->ps_pieces[i].ps_piece_in_plane[i_plane].i_lines     = 
p_sys->ps_puzzle_array[row][col][i_plane].i_lines;
+ p_sys->ps_pieces[i].ps_piece_in_plane[i_plane].i_original_x = 
p_sys->ps_puzzle_array[orow][ocol][i_plane].i_x;
+ p_sys->ps_pieces[i].ps_piece_in_plane[i_plane].i_original_y = 
p_sys->ps_puzzle_array[orow][ocol][i_plane].i_y;
+ p_sys->ps_pieces[i].ps_piece_in_plane[i_plane].i_actual_x   = 
p_sys->ps_puzzle_array[row][col][i_plane].i_x;
+ p_sys->ps_pieces[i].ps_piece_in_plane[i_plane].i_actual_y   = 
p_sys->ps_puzzle_array[row][col][i_plane].i_y;
+                            }
+                            i++;
+                        }
+                    }
+                }
+
+                p_sys->i_selected = p_sys->s_current_param.b_blackslot 
? i_pos : NO_PCE;
+                p_sys->b_finished = puzzle_is_finished( p_sys, 
p_sys->pi_order );
+            }
+        }
+    }
+    else /* jigsaw puzzle mode */
      {
-        for( unsigned i = 0; i < i_count; i++ )
-            p_sys->pi_order[i] = -1;
+        if ((p_sys->ps_desk_planes == NULL)  || (p_sys->ps_pict_planes 
== NULL)  || (p_sys->ps_puzzle_array == NULL) || (p_sys->ps_pieces == 
NULL)) {
+            *p_mouse = *p_new;
+            return VLC_SUCCESS;
+        }

-        for( unsigned c = 0; c < i_count; )
+        if( vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT ) )
          {
-            unsigned i = ((unsigned)vlc_mrand48()) % i_count;
-            if( p_sys->pi_order[i] == -1 )
-                p_sys->pi_order[i] = c++;
+
+            vlc_mutex_lock( &p_sys->pce_lock );
+
+            if (p_sys->i_mouse_drag_pce != NO_PCE) {
+                int i_ret = puzzle_piece_foreground( p_filter, 
p_sys->i_mouse_drag_pce);
+                if (i_ret != VLC_SUCCESS)
+                    return i_ret;
+                p_sys->i_mouse_drag_pce = 0;
+
+                uint32_t i_group_ID = p_sys->ps_pieces[0].i_group_ID;
+                for (uint32_t i = 0; i < 
p_sys->s_allocated.i_pieces_nbr; i++) {
+                    if ( i_group_ID == p_sys->ps_pieces[i].i_group_ID ) {
+                        p_sys->ps_pieces[i].b_finished = false;
+                    }
+                    else {
+                        break;
+                    }
+                }
+
+                p_sys->b_mouse_drag = true;
+                p_sys->b_mouse_mvt = false;
+            }
+            else {
+            /* player click an empty area then search a piece which is 
overlapping another one and place it here */
+                p_sys->b_mouse_drag = false;
+                for (uint32_t i = 0; i < 
p_sys->s_allocated.i_pieces_nbr; i++)
+                    if ( p_sys->ps_pieces[i].b_overlap ) {
+                        puzzle_move_group( p_filter, i, p_new->i_x - 
p_sys->ps_pieces[i].i_center_x,  p_new->i_y - 
p_sys->ps_pieces[i].i_center_y );
+                        p_sys->ps_pieces[i].b_overlap = false;
+                        break;
+                    }
+                p_sys->b_mouse_drag = false;
+            }
+
+            vlc_mutex_unlock( &p_sys->pce_lock );
+
          }
-        p_sys->b_finished = IsFinished( p_sys );
+        else if( vlc_mouse_HasReleased( p_old, p_new, MOUSE_BUTTON_LEFT ) )
+        {
+            if ( !p_sys->b_mouse_mvt && p_sys->b_mouse_drag ) {
+                /* piece clicked without any mouse mvt => rotate it or 
mirror */
+                if ( p_sys->s_current_param.i_rotate != 0) {
+                    vlc_mutex_lock( &p_sys->pce_lock );

-    } while( p_sys->b_finished || !IsValid( p_sys ) );
+                    uint32_t i_group_ID = p_sys->ps_pieces[0].i_group_ID;

-    if( p_sys->b_blackslot )
-    {
-        for( unsigned i = 0; i < i_count; i++ )
+                    for (uint32_t i = 0; i < 
p_sys->s_allocated.i_pieces_nbr; i++)
+                        if ( i_group_ID == p_sys->ps_pieces[i].i_group_ID )
+                            puzzle_rotate_pce( p_filter, i, 
p_sys->i_mouse_action, p_sys->ps_pieces[0].i_center_x, 
p_sys->ps_pieces[0].i_center_y, p_sys->i_mouse_action != 4 ? true : false );
+
+                    vlc_mutex_unlock( &p_sys->pce_lock );
+                }
+            }
+            p_sys->b_mouse_drag = false;
+            p_sys->b_mouse_mvt = false;
+        }
+        else /* no action on left button */
          {
-            if( p_sys->pi_order[i] == (int)i_count - 1 )
+            /* check if the mouse is in the preview area */
+            switch ( p_sys->i_preview_pos )
              {
-                p_sys->i_selected = i;
+              case 0:
+                if ( p_new->i_x < (int)p_fmt_in->i_width / 2 && 
p_new->i_y < (int)p_fmt_in->i_height / 2 )
+                    p_sys->i_preview_pos++;
+                break;
+              case 1:
+                if ( p_new->i_x > (int)p_fmt_in->i_width / 2 && 
p_new->i_y < (int)p_fmt_in->i_height / 2 )
+                    p_sys->i_preview_pos++;
+                break;
+              case 2:
+                if ( p_new->i_x > (int)p_fmt_in->i_width / 2 && 
p_new->i_y > (int)p_fmt_in->i_height / 2 )
+                    p_sys->i_preview_pos++;
+                break;
+              case 3:
+                if ( p_new->i_x < (int)p_fmt_in->i_width / 2 && 
p_new->i_y > (int)p_fmt_in->i_height / 2 )
+                    p_sys->i_preview_pos++;
                  break;
              }
+            p_sys->i_preview_pos %= 4;
+
+            if ( !vlc_mouse_IsLeftPressed( p_new ) )
+                p_sys->b_mouse_drag = false;
+
+            int i_dx, i_dy;
+            vlc_mouse_GetMotion( &i_dx, &i_dy, p_old, p_new );
+            if ( i_dx != 0 || i_dy != 0 )
+                p_sys->b_mouse_mvt = true;
+
+            if (p_sys->b_mouse_drag) {
+                if ( ( p_new->i_x <= 0 ) || ( p_new->i_y <=  0 ) || ( 
p_new->i_x >= (int) p_fmt_in->i_width )
+                        || ( p_new->i_y >= (int) p_fmt_in->i_height ) )
+                {
+                    /* if the mouse is outside the window, stop moving 
the piece/group */
+                    p_sys->b_mouse_drag = false;
+                    p_sys->b_mouse_mvt = true;
+                }
+                else if ( i_dx != 0 || i_dy != 0 )
+                {
+                    vlc_mutex_lock( &p_sys->pce_lock );
+
+                    puzzle_move_group( p_filter, 
p_sys->i_mouse_drag_pce, i_dx, i_dy);
+
+                    vlc_mutex_unlock( &p_sys->pce_lock );
+                }
+            }
          }
      }
-    else
-    {
-        p_sys->i_selected = -1;
-    }
+    return VLC_EGENERIC;
  }
diff --git a/modules/video_filter/puzzle.h b/modules/video_filter/puzzle.h
new file mode 100644
index 0000000..f4638b1
--- /dev/null
+++ b/modules/video_filter/puzzle.h
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * puzzle.h : Puzzle game
+ 
*****************************************************************************
+ * Copyright (C) 2005-2009 VLC authors and VideoLAN
+ * Copyright (C) 2013      Vianney Boyer
+ * $Id$
+ *
+ * Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ 
*****************************************************************************/
+
+#ifndef VLC_PUZZLE_H
+#define VLC_PUZZLE_H 1
+
+#include "puzzle_mgt.h"
+
+struct filter_sys_t {
+    bool b_init;
+    bool b_bake_request;
+    bool b_shape_init;
+    bool b_change_param;
+    bool b_finished;
+    bool b_shuffle_rqst;
+    bool b_mouse_drag;
+    bool b_mouse_mvt;
+
+    param_t s_allocated;
+    param_t s_current_param;
+    param_t s_new_param;
+
+    uint32_t i_done_count, i_tmp_done_count;
+
+    int32_t i_mouse_drag_pce;
+    int32_t i_mouse_x, i_mouse_y;
+    int16_t i_pointed_pce;
+    int8_t  i_mouse_action;
+
+    uint32_t i_solve_acc_loop, i_solve_grp_loop, i_calc_corn_loop;
+    int32_t i_magnet_accuracy;
+    int32_t *pi_group_qty;
+
+    int32_t *pi_order;                 /* array which contains final 
pieces location (used in BASIC GAME MODE)                              */
+    puzzle_array_t ***ps_puzzle_array; /* array [row][col][plane] 
preset of location & size of each piece in the original image             */
+    piece_shape_t **ps_pieces_shapes;  /* array [each piece type 
(PCE_TYPE_NBR * negative * 4: top...)][each plane] of piece 
definition     */
+    piece_t *ps_pieces;                /* list [piece] of pieces data. */
+    piece_t *ps_pieces_tmp;            /* used when sorting layers */
+
+    puzzle_plane_t *ps_desk_planes;
+    puzzle_plane_t *ps_pict_planes;
+
+    uint8_t i_preview_pos;
+    int32_t i_selected;
+
+    vlc_mutex_t lock, pce_lock;
+
+    int32_t i_auto_shuffle_countdown_val, i_auto_solve_countdown_val;
+
+    point_t **ps_bezier_pts_H;
+};
+
+picture_t *Filter( filter_t *, picture_t * );
+int puzzle_Callback( vlc_object_t *, char const *, vlc_value_t, 
vlc_value_t, void * );
+int puzzle_mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t *, const 
vlc_mouse_t * );
+
+#endif
-- 
1.7.9.5





More information about the vlc-devel mailing list