X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_filter%2Fpuzzle.c;h=6c6433bbeea541dfe0b7e4292a96195491acd78e;hb=5c2fc028e78429b8a9d540c7fa9d227c5efa0620;hp=e2de62a2d5f8d520c116659bf9d012276e68fb2d;hpb=f5bcfe6da7cce84abcb2fc9670126e7a3ec6edf0;p=vlc diff --git a/modules/video_filter/puzzle.c b/modules/video_filter/puzzle.c index e2de62a2d5..6c6433bbee 100644 --- a/modules/video_filter/puzzle.c +++ b/modules/video_filter/puzzle.c @@ -1,546 +1,862 @@ /***************************************************************************** * puzzle.c : Puzzle game ***************************************************************************** - * Copyright (C) 2005-2006 the VideoLAN team + * Copyright (C) 2005-2009 VLC authors and VideoLAN + * Copyright (C) 2013 Vianney Boyer * $Id$ * * Authors: Antoine Cellerier + * Vianney Boyer * - * 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 + * 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 General Public License for more details. + * 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 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. + * 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. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* malloc(), free() */ -#include - -#include -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include -#include "filter_common.h" -#include "vlc_image.h" -#include "vlc_input.h" -#include "vlc_playlist.h" +#include +#include +#include +#include -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int Create ( vlc_object_t * ); -static void Destroy ( vlc_object_t * ); +#include "filter_picture.h" -static int Init ( vout_thread_t * ); -static void End ( vout_thread_t * ); -static void Render ( vout_thread_t *, picture_t * ); - -static int SendEvents ( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); -static int MouseEvent ( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); +#include "puzzle.h" +#include "puzzle_bezier.h" +#include "puzzle_lib.h" +#include "puzzle_pce.h" +#include "puzzle_mgt.h" /***************************************************************************** * Module descriptor *****************************************************************************/ - #define ROWS_TEXT N_("Number of puzzle rows") #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-" -vlc_module_begin(); - set_description( _("Puzzle interactive game video filter") ); - set_shortname( _( "Puzzle" )); - set_capability( "video filter", 0 ); - set_category( CAT_VIDEO ); - set_subcategory( SUBCAT_VIDEO_VFILTER ); - - add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL, - ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE ); - add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL, - COLS_TEXT, COLS_LONGTEXT, VLC_FALSE ); - add_bool( CFG_PREFIX "black-slot", 0, NULL, - BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE ); - - set_callbacks( Create, Destroy ); -vlc_module_end(); - -static const char *ppsz_filter_options[] = { - "rows", "cols", "black-slot", NULL -}; +int Open ( vlc_object_t * ); +void Close( vlc_object_t * ); + +vlc_module_begin() + set_description( N_("Puzzle interactive game video filter") ) + set_shortname( N_( "Puzzle" )) + set_capability( "video filter2", 0 ) + set_category( CAT_VIDEO ) + set_subcategory( SUBCAT_VIDEO_VFILTER ) + + add_integer_with_range( CFG_PREFIX "rows", 4, 2, 16, + ROWS_TEXT, ROWS_LONGTEXT, false ) + add_integer_with_range( CFG_PREFIX "cols", 4, 2, 16, + COLS_TEXT, COLS_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() /***************************************************************************** - * vout_sys_t: Magnify video output method descriptor + * Local prototypes *****************************************************************************/ -struct vout_sys_t + +const char *const ppsz_filter_options[] = { + "rows", "cols","border", "preview", "preview-size", "mode", "shape-size", "auto-shuffle", "auto-solve", "rotation", NULL +}; + +/** + * Open the filter + */ +int Open( vlc_object_t *p_this ) { - vout_thread_t *p_vout; + filter_t *p_filter = (filter_t *)p_this; + filter_sys_t *p_sys; - image_handler_t *p_image; + /* 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; + } - int i_cols; - int i_rows; - int *pi_order; - int i_selected; - vlc_bool_t b_finished; + const vlc_chroma_description_t *p_chroma = + vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma ); + if( p_chroma == NULL || p_chroma->plane_count == 0 ) + return VLC_EGENERIC; - vlc_bool_t b_blackslot; -}; + /* Allocate structure */ + p_filter->p_sys = p_sys = calloc(1, sizeof( *p_sys ) ); + if( !p_sys ) + return VLC_ENOMEM; -/***************************************************************************** - * Control: control facility for the vout (forwards to child vout) - *****************************************************************************/ -static int Control( vout_thread_t *p_vout, int i_query, va_list args ) -{ - return vout_vaControl( p_vout->p_sys->p_vout, i_query, args ); -} + /* init some values */ + 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; -/***************************************************************************** - * Misc stuff... - *****************************************************************************/ -static vlc_bool_t finished( vout_sys_t *p_sys ) -{ - int i; - for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ ) + /* 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 ) { - if( i != p_sys->pi_order[i] ) return VLC_FALSE; + free(p_filter->p_sys); + p_filter->p_sys = NULL; + return VLC_ENOMEM; } - return VLC_TRUE; + for (int32_t i_shape = 0; i_shapeps_bezier_pts_H[i_shape] = puzzle_rand_bezier(7); + + + config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, + p_filter->p_cfg ); + + 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 = puzzle_mouse; + + return VLC_SUCCESS; } -static vlc_bool_t is_valid( vout_sys_t *p_sys ) -{ - int i, j, d=0; - if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE; - for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ ) - { - if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 ) + +/** + * Close the filter + */ +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", 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_shapeps_bezier_pts_H[i_shape]); + free(p_sys->ps_bezier_pts_H); + + free( p_sys ); +} + +/** + * Filter a picture + */ +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_pic_out = filter_NewPicture( p_filter ); + if( !p_pic_out ) { + picture_Release( p_pic_in ); + return NULL; + } + + 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; + } + } + + 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; + + /* 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_visible_width + || p_sys->s_current_param.i_pict_height != (int) p_fmt_in->i_visible_height ) { + picture_Release(p_pic_in); + picture_Release(p_pic_out); + return NULL; + } + + vlc_mutex_lock( &p_sys->lock ); + + /* 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 ) { - d += i / p_sys->i_cols + 1; - continue; + p_sys->b_bake_request = true; + p_sys->b_init = false; + p_sys->b_shuffle_rqst = true; + p_sys->b_shape_init = false; } - for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ ) + + 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 ) { - if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 ) - continue; - if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++; + p_sys->b_bake_request = true; + p_sys->b_shape_init = false; } - } - if( d%2!=0 ) return VLC_FALSE; - else return VLC_TRUE; -} -static void shuffle( vout_sys_t *p_sys ) -{ - int i, c; - free( p_sys->pi_order ); - p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) ); - do - { - for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ ) + + /* depending on the game selected, set associated internal flags */ + switch ( p_sys->s_new_param.i_mode ) { - p_sys->pi_order[i] = -1; + 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; } - i = 0; - for( c = 0; c < p_sys->i_cols * p_sys->i_rows; ) + else { - i = rand()%( p_sys->i_cols * p_sys->i_rows ); - if( p_sys->pi_order[i] == -1 ) - { - p_sys->pi_order[i] = c; - c++; - } + 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_finished = finished( p_sys ); - } while( p_sys->b_finished == VLC_TRUE - || is_valid( p_sys ) == VLC_FALSE ); + p_sys->b_change_param = false; + } - if( p_sys->b_blackslot == VLC_TRUE ) - { - for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ ) - { - if( p_sys->pi_order[i] == - p_sys->i_cols * p_sys->i_rows - 1 ) + 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 ) { - p_sys->i_selected = i; - break; + 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 ); } } - else - { - p_sys->i_selected = -1; + + /* 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 ); } -} -/***************************************************************************** - * Create: allocates Magnify video thread output method - *****************************************************************************/ -static int Create( vlc_object_t *p_this ) -{ - vout_thread_t *p_vout = (vout_thread_t *)p_this; + /* 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 ); + } - /* Allocate structure */ - p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); - if( p_vout->p_sys == NULL ) + vlc_mutex_lock( &p_sys->pce_lock ); + + /* 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++) { - msg_Err( p_vout, "out of memory" ); - return VLC_ENOMEM; + puzzle_solve_pces_accuracy( p_filter ); } - p_vout->p_sys->p_image = image_HandlerCreate( p_vout ); - - config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options, - p_vout->p_cfg ); + 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_group( p_filter ); + } - p_vout->p_sys->i_rows = var_CreateGetInteger( p_vout, CFG_PREFIX "rows" ); - p_vout->p_sys->i_cols = var_CreateGetInteger( p_vout, CFG_PREFIX "cols" ); - p_vout->p_sys->b_blackslot = - var_CreateGetInteger( p_vout, CFG_PREFIX "black-slot" ); + 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) + { + vlc_mutex_unlock( &p_sys->pce_lock ); + return CopyInfoAndRelease( p_pic_out, p_pic_in ); + } + } - p_vout->p_sys->pi_order = NULL; - shuffle( p_vout->p_sys ); + 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_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 ); + } - p_vout->pf_init = Init; - p_vout->pf_end = End; - p_vout->pf_manage = NULL; - p_vout->pf_render = Render; - p_vout->pf_display = NULL; - p_vout->pf_control = Control; + /* 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 ) + { + puzzle_auto_shuffle( p_filter ); + puzzle_auto_solve( p_filter ); + } - return VLC_SUCCESS; -} + vlc_mutex_unlock( &p_sys->pce_lock ); -/***************************************************************************** - * Init: initialize Magnify video thread output method - *****************************************************************************/ -static int Init( vout_thread_t *p_vout ) -{ - int i_index; - picture_t *p_pic; - video_format_t fmt = {0}; + /* draw output pic */ + if ( !p_sys->b_bake_request && p_sys->b_init && p_sys->ps_puzzle_array != NULL ) { - I_OUTPUTPICTURES = 0; + puzzle_draw_borders(p_filter, p_pic_in, p_pic_out); - /* Initialize the output structure */ - p_vout->output.i_chroma = p_vout->render.i_chroma; - p_vout->output.i_width = p_vout->render.i_width; - p_vout->output.i_height = p_vout->render.i_height; - p_vout->output.i_aspect = p_vout->render.i_aspect; + p_sys->i_pointed_pce = NO_PCE; + puzzle_draw_pieces(p_filter, p_pic_in, p_pic_out); - p_vout->fmt_out = p_vout->fmt_in; - fmt = p_vout->fmt_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; - /* Try to open the real video output */ - msg_Dbg( p_vout, "spawning the real video output" ); + if (p_sys->s_current_param.b_preview ) + puzzle_draw_preview(p_filter, p_pic_in, p_pic_out); - p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt ); + /* 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); + } - /* Everything failed */ - if( p_vout->p_sys->p_vout == NULL ) - { - msg_Err( p_vout, "cannot open vout, aborting" ); - return VLC_EGENERIC; - } + /* 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); + } - var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout ); - var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout ); - var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked", - MouseEvent, p_vout); + /* 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); - ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES ); - ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents ); - ADD_PARENT_CALLBACKS( SendEventsToChild ); + /* 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 ); - return VLC_SUCCESS; -} + int32_t i_delta_x; -/***************************************************************************** - * End: terminate Magnify video thread output method - *****************************************************************************/ -static void End( vout_thread_t *p_vout ) -{ - int i_index; - - /* Free the fake output buffers we allocated */ - for( i_index = I_OUTPUTPICTURES ; i_index ; ) - { - i_index--; - free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig ); + 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 ); + } } - var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout); - var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout); - var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout); + return CopyInfoAndRelease( p_pic_out, p_pic_in ); } -#define SHUFFLE_WIDTH 81 -#define SHUFFLE_HEIGHT 13 -static const char *shuffle_button[] = -{ -".................................................................................", -".............. ............................ ........ ...... ...............", -".............. ........................... ......... ........ ...............", -".............. ........................... ......... ........ ...............", -".. ....... . ....... .... ...... ...... ...... ........ ...", -". .... ...... ... ...... .... ....... ......... ........ ....... .. ..", -". ........... .... ...... .... ....... ......... ........ ...... .... .", -". ....... .... ...... .... ....... ......... ........ ...... .", -".. ...... .... ...... .... ....... ......... ........ ...... .......", -"...... ...... .... ...... .... ....... ......... ........ ...... .......", -". .... ...... .... ...... ... ....... ......... ........ ....... .... .", -".. ....... .... ....... . ....... ......... ........ ........ ..", -"................................................................................."}; - - /***************************************************************************** - * Destroy: destroy Magnify video thread output method + * Misc stuff... *****************************************************************************/ -static void Destroy( vlc_object_t *p_this ) +int puzzle_Callback( vlc_object_t *p_this, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) { - vout_thread_t *p_vout = (vout_thread_t *)p_this; + VLC_UNUSED(p_this); VLC_UNUSED(oldval); + filter_sys_t *p_sys = (filter_sys_t *)p_data; - if( p_vout->p_sys->p_vout ) - { - DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents ); - vlc_object_detach( p_vout->p_sys->p_vout ); - vout_Destroy( p_vout->p_sys->p_vout ); + 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; } - image_HandlerDelete( p_vout->p_sys->p_image ); - free( p_vout->p_sys->pi_order ); - - DEL_PARENT_CALLBACKS( SendEventsToChild ); + p_sys->b_change_param = true; + vlc_mutex_unlock( &p_sys->lock ); - free( p_vout->p_sys ); + return VLC_SUCCESS; } -/***************************************************************************** - * Render: displays previously rendered output - *****************************************************************************/ -static void Render( vout_thread_t *p_vout, picture_t *p_pic ) +/* 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 ) { - picture_t *p_outpic; + filter_sys_t *p_sys = p_filter->p_sys; + const video_format_t *p_fmt_in = &p_filter->fmt_in.video; - //video_format_t fmt_out = {0}; - //picture_t *p_converted; + /* 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; - int i_plane; + if (! p_sys->b_init || p_sys->b_change_param) { + *p_mouse = *p_new; + return VLC_SUCCESS; + } - int i_rows = p_vout->p_sys->i_rows; - int i_cols = p_vout->p_sys->i_cols; + p_sys->i_mouse_x = p_new->i_x; + p_sys->i_mouse_y = p_new->i_y; - /* This is a new frame. Get a structure from the video_output. */ - while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) ) - == NULL ) - { - if( p_vout->b_die || p_vout->b_error ) + /* 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 ) + { + p_sys->b_shuffle_rqst = true; + return VLC_EGENERIC; + } + else { - return; + /* otherwise we can forward the mouse */ + *p_mouse = *p_new; + return VLC_SUCCESS; } - msleep( VOUT_OUTMEM_SLEEP ); } - vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date ); - - for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ ) - { - plane_t *p_in = p_pic->p+i_plane; - plane_t *p_out = p_outpic->p+i_plane; - int i_pitch = p_in->i_pitch; - int i; + 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 ); - for( i = 0; i < i_cols * i_rows; i++ ) + if( b_clicked ) { - int i_col = i % i_cols; - int i_row = i / i_cols; - int i_ocol = p_vout->p_sys->pi_order[i] % i_cols; - int i_orow = p_vout->p_sys->pi_order[i] / i_cols; - int i_last_row = i_row + 1; - i_orow *= p_in->i_lines / i_rows; - i_row *= p_in->i_lines / i_rows; - i_last_row *= p_in->i_lines / i_rows; - - if( p_vout->p_sys->b_blackslot == VLC_TRUE - && p_vout->p_sys->b_finished == VLC_FALSE - && i == p_vout->p_sys->i_selected ) + /* */ + 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; + + /* 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 ) ) { - uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 ); - for( ; i_row < i_last_row; i_row++, i_orow++ ) - { - p_vout->p_libvlc-> - pf_memset( p_out->p_pixels + i_row * i_pitch - + i_col * i_pitch / i_cols, - color, i_pitch / i_cols ); - } + *p_mouse = *p_new; + return VLC_SUCCESS; } - else + 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 ) + { - for( ; i_row < i_last_row; i_row++, i_orow++ ) + /* 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 ) { - p_vout->p_libvlc-> - pf_memcpy( p_out->p_pixels + i_row * i_pitch - + i_col * i_pitch / i_cols, - p_in->p_pixels + i_orow * i_pitch - + i_ocol * i_pitch / i_cols, - i_pitch / i_cols ); + 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 ); } } } - - if( p_vout->p_sys->i_selected != -1 - && p_vout->p_sys->b_blackslot == VLC_FALSE ) + else /* jigsaw puzzle mode */ { - plane_t *p_in = p_pic->p+Y_PLANE; - plane_t *p_out = p_outpic->p+Y_PLANE; - int i_pitch = p_in->i_pitch; - int i_col = p_vout->p_sys->i_selected % i_cols; - int i_row = p_vout->p_sys->i_selected / i_cols; - int i_last_row = i_row + 1; - i_row *= p_in->i_lines / i_rows; - i_last_row *= p_in->i_lines / i_rows; - p_vout->p_libvlc-> - pf_memset( p_out->p_pixels + i_row * i_pitch - + i_col * i_pitch / i_cols, - 0xff, i_pitch / i_cols ); - for( ; i_row < i_last_row; i_row++ ) - { - p_out->p_pixels[ i_row * i_pitch - + i_col * i_pitch / i_cols ] = 0xff; - p_out->p_pixels[ i_row * i_pitch - + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff; + 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; } - i_row--; - p_vout->p_libvlc-> - pf_memset( p_out->p_pixels + i_row * i_pitch - + i_col * i_pitch / i_cols, - 0xff, i_pitch / i_cols ); - } - if( p_vout->p_sys->b_finished == VLC_TRUE ) - { - int i, j; - plane_t *p_out = p_outpic->p+Y_PLANE; - int i_pitch = p_out->i_pitch; - for( i = 0; i < SHUFFLE_HEIGHT; i++ ) + if( vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT ) ) { - for( j = 0; j < SHUFFLE_WIDTH; j++ ) - { - if( shuffle_button[i][j] == '.' ) - p_out->p_pixels[ i * i_pitch + j ] = 0xff; - } - } - } - - vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic ); -} -/***************************************************************************** - * SendEvents: forward mouse and keyboard events to the parent p_vout - *****************************************************************************/ -static int SendEvents( vlc_object_t *p_this, char const *psz_var, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) -{ - var_Set( (vlc_object_t *)p_data, psz_var, newval ); + vlc_mutex_lock( &p_sys->pce_lock ); - return VLC_SUCCESS; -} + 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) + { + vlc_mutex_unlock( &p_sys->pce_lock ); + 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; + } + } -/***************************************************************************** - * SendEventsToChild: forward events to the child/children vout - *****************************************************************************/ -static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) -{ - vout_thread_t *p_vout = (vout_thread_t *)p_this; - var_Set( p_vout->p_sys->p_vout, psz_var, newval ); - return VLC_SUCCESS; -} + 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; + } -/***************************************************************************** - * MouseEvent: callback for mouse events - *****************************************************************************/ -static int MouseEvent( vlc_object_t *p_this, char const *psz_var, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) -{ - vout_thread_t *p_vout = (vout_thread_t*)p_data; - int i_x, i_y; - int i_v; - -#define MOUSE_DOWN 1 -#define MOUSE_CLICKED 2 -#define MOUSE_MOVE_X 4 -#define MOUSE_MOVE_Y 8 -#define MOUSE_MOVE 12 - uint8_t mouse= 0; - - int v_h = p_vout->output.i_height; - int v_w = p_vout->output.i_width; - int i_pos; - - if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X; - if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y; - if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED; - - i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" ); - if( i_v & 0x1 ) mouse |= MOUSE_DOWN; - i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" ); - i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" ); - - if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w ) - return VLC_SUCCESS; + vlc_mutex_unlock( &p_sys->pce_lock ); - if( mouse & MOUSE_CLICKED ) - { - i_pos = p_vout->p_sys->i_cols * ( ( p_vout->p_sys->i_rows * i_y ) / v_h ) + (p_vout->p_sys->i_cols * i_x ) / v_w; - if( p_vout->p_sys->b_finished == VLC_TRUE - && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT ) - { - shuffle( p_vout->p_sys ); } - else if( p_vout->p_sys->i_selected == -1 ) + else if( vlc_mouse_HasReleased( p_old, p_new, MOUSE_BUTTON_LEFT ) ) { - p_vout->p_sys->i_selected = i_pos; - } - else if( p_vout->p_sys->i_selected == i_pos - && p_vout->p_sys->b_blackslot == VLC_FALSE ) - { - p_vout->p_sys->i_selected = -1; + 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 ); + + 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 ) + 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 if( ( p_vout->p_sys->i_selected == i_pos + 1 - && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 ) - || ( p_vout->p_sys->i_selected == i_pos - 1 - && i_pos % p_vout->p_sys->i_cols != 0 ) - || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols - || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols ) + else /* no action on left button */ { - int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ]; - p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] = - p_vout->p_sys->pi_order[ i_pos ]; - p_vout->p_sys->pi_order[ i_pos ] = a; - if( p_vout->p_sys->b_blackslot == VLC_TRUE ) - p_vout->p_sys->i_selected = i_pos; - else - p_vout->p_sys->i_selected = -1; + /* check if the mouse is in the preview area */ + switch ( p_sys->i_preview_pos ) + { + 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); - p_vout->p_sys->b_finished = finished( p_vout->p_sys ); + vlc_mutex_unlock( &p_sys->pce_lock ); + } + } } } - return VLC_SUCCESS; + return VLC_EGENERIC; }