1 /*****************************************************************************
2 * puzzle.c : Puzzle game
3 *****************************************************************************
4 * Copyright (C) 2005-2009 VLC authors and VideoLAN
5 * Copyright (C) 2013 Vianney Boyer
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8 * Vianney Boyer <vlcvboyer -at- gmail -dot- com>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_mouse.h>
38 #include <vlc_picture.h>
41 #include "filter_picture.h"
44 #include "puzzle_bezier.h"
45 #include "puzzle_lib.h"
46 #include "puzzle_pce.h"
47 #include "puzzle_mgt.h"
49 /*****************************************************************************
51 *****************************************************************************/
52 #define ROWS_TEXT N_("Number of puzzle rows")
53 #define ROWS_LONGTEXT N_("Number of puzzle rows")
54 #define COLS_TEXT N_("Number of puzzle columns")
55 #define COLS_LONGTEXT N_("Number of puzzle columns")
56 #define MODE_TEXT N_("Game mode")
57 #define MODE_LONGTEXT N_("Select game mode variation from jigsaw puzzle to sliding puzzle.")
58 #define BORDER_TEXT N_("Border")
59 #define BORDER_LONGTEXT N_("Unshuffled Border width.")
60 #define PREVIEW_TEXT N_("Small preview")
61 #define PREVIEW_LONGTEXT N_("Show small preview.")
62 #define PREVIEWSIZE_TEXT N_("Small preview size")
63 #define PREVIEWSIZE_LONGTEXT N_("Show small preview size (percent of source).")
64 #define SHAPE_SIZE_TEXT N_("Piece edge shape size")
65 #define SHAPE_SIZE_LONGTEXT N_("Size of the curve along the piece's edge")
66 #define AUTO_SHUFFLE_TEXT N_("Auto shuffle")
67 #define AUTO_SHUFFLE_LONGTEXT N_("Auto shuffle delay during game")
68 #define AUTO_SOLVE_TEXT N_("Auto solve")
69 #define AUTO_SOLVE_LONGTEXT N_("Auto solve delay during game")
70 #define ROTATION_TEXT N_("Rotation")
71 #define ROTATION_LONGTEXT N_("Rotation parameter: none;180;90-270;mirror")
73 static const int pi_mode_values
[] = { (int) 0, (int) 1, (int) 2, (int) 3 };
74 static const char *const ppsz_mode_descriptions
[] = { N_("jigsaw puzzle"), N_("sliding puzzle"), N_("swap puzzle"), N_("exchange puzzle") };
75 static const int pi_rotation_values
[] = { (int) 0, (int) 1, (int) 2, (int) 3 };
76 static const char *const ppsz_rotation_descriptions
[] = { N_("0"), N_("0/180"), N_("0/90/180/270"), N_("0/90/180/270/mirror") };
78 #define CFG_PREFIX "puzzle-"
80 static int Open ( vlc_object_t
* );
81 static void Close( vlc_object_t
* );
84 set_description( N_("Puzzle interactive game video filter") )
85 set_shortname( N_( "Puzzle" ))
86 set_capability( "video filter", 0 )
87 set_category( CAT_VIDEO
)
88 set_subcategory( SUBCAT_VIDEO_VFILTER
)
90 add_integer_with_range( CFG_PREFIX
"rows", 4, 2, 42,
91 ROWS_TEXT
, ROWS_LONGTEXT
, false )
92 add_integer_with_range( CFG_PREFIX
"cols", 4, 2, 42,
93 COLS_TEXT
, COLS_LONGTEXT
, false )
94 add_integer_with_range( CFG_PREFIX
"border", 3, 0, 40,
95 BORDER_TEXT
, BORDER_LONGTEXT
, false )
96 add_bool( CFG_PREFIX
"preview", false,
97 PREVIEW_TEXT
, PREVIEW_LONGTEXT
, false )
98 add_integer_with_range( CFG_PREFIX
"preview-size", 15, 0, 100,
99 PREVIEWSIZE_TEXT
, PREVIEWSIZE_LONGTEXT
, false )
100 add_integer_with_range( CFG_PREFIX
"shape-size", 90, 0, 100,
101 SHAPE_SIZE_TEXT
, SHAPE_SIZE_LONGTEXT
, false )
102 add_integer_with_range( CFG_PREFIX
"auto-shuffle", 0, 0, 30000,
103 AUTO_SHUFFLE_TEXT
, AUTO_SHUFFLE_LONGTEXT
, false )
104 add_integer_with_range( CFG_PREFIX
"auto-solve", 0, 0, 30000,
105 AUTO_SOLVE_TEXT
, AUTO_SOLVE_LONGTEXT
, false )
106 add_integer( CFG_PREFIX
"rotation", 0,
107 ROTATION_TEXT
, ROTATION_LONGTEXT
, false )
108 change_integer_list(pi_rotation_values
, ppsz_rotation_descriptions
)
109 add_integer( CFG_PREFIX
"mode", 0,
110 MODE_TEXT
, MODE_LONGTEXT
, false )
111 change_integer_list(pi_mode_values
, ppsz_mode_descriptions
)
113 set_callbacks( Open
, Close
)
116 /*****************************************************************************
118 *****************************************************************************/
120 const char *const ppsz_filter_options
[] = {
121 "rows", "cols","border", "preview", "preview-size", "mode", "shape-size", "auto-shuffle", "auto-solve", "rotation", NULL
127 static int Open( vlc_object_t
*p_this
)
129 filter_t
*p_filter
= (filter_t
*)p_this
;
132 /* Assert video in match with video out */
133 if( !es_format_IsSimilar( &p_filter
->fmt_in
, &p_filter
->fmt_out
) ) {
134 msg_Err( p_filter
, "Input and output format does not match" );
138 const vlc_chroma_description_t
*p_chroma
=
139 vlc_fourcc_GetChromaDescription( p_filter
->fmt_in
.video
.i_chroma
);
140 if( p_chroma
== NULL
|| p_chroma
->plane_count
== 0 || p_chroma
->pixel_size
> 1 )
143 /* Allocate structure */
144 p_filter
->p_sys
= p_sys
= calloc(1, sizeof( *p_sys
) );
148 /* init some values */
149 p_sys
->b_shuffle_rqst
= true;
150 p_sys
->b_change_param
= true;
151 p_sys
->i_mouse_drag_pce
= NO_PCE
;
152 p_sys
->i_pointed_pce
= NO_PCE
;
153 p_sys
->i_magnet_accuracy
= 3;
155 /* Generate values of random bezier shapes */
156 p_sys
->ps_bezier_pts_H
= calloc( SHAPES_QTY
, sizeof( point_t
*) );
157 if( !p_sys
->ps_bezier_pts_H
)
159 free(p_filter
->p_sys
);
160 p_filter
->p_sys
= NULL
;
163 for (int32_t i_shape
= 0; i_shape
<SHAPES_QTY
; i_shape
++)
164 p_sys
->ps_bezier_pts_H
[i_shape
] = puzzle_rand_bezier(7);
167 config_ChainParse( p_filter
, CFG_PREFIX
, ppsz_filter_options
,
170 vlc_mutex_init( &p_sys
->lock
);
171 vlc_mutex_init( &p_sys
->pce_lock
);
173 p_sys
->s_new_param
.i_rows
=
174 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"rows" );
175 p_sys
->s_new_param
.i_cols
=
176 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"cols" );
177 p_sys
->s_new_param
.i_border
=
178 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"border" );
179 p_sys
->s_new_param
.b_preview
=
180 var_CreateGetBoolCommand( p_filter
, CFG_PREFIX
"preview" );
181 p_sys
->s_new_param
.i_preview_size
=
182 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"preview-size" );
183 p_sys
->s_new_param
.i_shape_size
=
184 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"shape-size" );
185 p_sys
->s_new_param
.i_auto_shuffle_speed
=
186 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"auto-shuffle" );
187 p_sys
->s_new_param
.i_auto_solve_speed
=
188 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"auto-solve" );
189 p_sys
->s_new_param
.i_rotate
=
190 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"rotation" );
191 p_sys
->s_new_param
.i_mode
=
192 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"mode" );
194 var_AddCallback( p_filter
, CFG_PREFIX
"rows", puzzle_Callback
, p_sys
);
195 var_AddCallback( p_filter
, CFG_PREFIX
"cols", puzzle_Callback
, p_sys
);
196 var_AddCallback( p_filter
, CFG_PREFIX
"border", puzzle_Callback
, p_sys
);
197 var_AddCallback( p_filter
, CFG_PREFIX
"preview", puzzle_Callback
, p_sys
);
198 var_AddCallback( p_filter
, CFG_PREFIX
"preview-size", puzzle_Callback
, p_sys
);
199 var_AddCallback( p_filter
, CFG_PREFIX
"shape-size", puzzle_Callback
, p_sys
);
200 var_AddCallback( p_filter
, CFG_PREFIX
"auto-shuffle", puzzle_Callback
, p_sys
);
201 var_AddCallback( p_filter
, CFG_PREFIX
"auto-solve", puzzle_Callback
, p_sys
);
202 var_AddCallback( p_filter
, CFG_PREFIX
"rotation", puzzle_Callback
, p_sys
);
203 var_AddCallback( p_filter
, CFG_PREFIX
"mode", puzzle_Callback
, p_sys
);
205 p_filter
->pf_video_filter
= Filter
;
206 p_filter
->pf_video_mouse
= puzzle_mouse
;
214 static void Close( vlc_object_t
*p_this
) {
215 filter_t
*p_filter
= (filter_t
*)p_this
;
216 filter_sys_t
*p_sys
= p_filter
->p_sys
;
218 var_DelCallback( p_filter
, CFG_PREFIX
"rows", puzzle_Callback
, p_sys
);
219 var_DelCallback( p_filter
, CFG_PREFIX
"cols", puzzle_Callback
, p_sys
);
220 var_DelCallback( p_filter
, CFG_PREFIX
"border", puzzle_Callback
, p_sys
);
221 var_DelCallback( p_filter
, CFG_PREFIX
"preview", puzzle_Callback
, p_sys
);
222 var_DelCallback( p_filter
, CFG_PREFIX
"preview-size", puzzle_Callback
, p_sys
);
223 var_DelCallback( p_filter
, CFG_PREFIX
"shape-size", puzzle_Callback
, p_sys
);
224 var_DelCallback( p_filter
, CFG_PREFIX
"auto-shuffle", puzzle_Callback
, p_sys
);
225 var_DelCallback( p_filter
, CFG_PREFIX
"auto-solve", puzzle_Callback
, p_sys
);
226 var_DelCallback( p_filter
, CFG_PREFIX
"rotation", puzzle_Callback
, p_sys
);
227 var_DelCallback( p_filter
, CFG_PREFIX
"mode", puzzle_Callback
, p_sys
);
229 /* Free allocated memory */
230 puzzle_free_ps_puzzle_array ( p_filter
);
231 puzzle_free_ps_pieces_shapes ( p_filter
);
232 puzzle_free_ps_pieces ( p_filter
);
233 free(p_sys
->ps_desk_planes
);
234 free(p_sys
->ps_pict_planes
);
235 free( p_sys
->pi_order
);
237 for (int32_t i_shape
= 0; i_shape
<SHAPES_QTY
; i_shape
++)
238 free(p_sys
->ps_bezier_pts_H
[i_shape
]);
239 free(p_sys
->ps_bezier_pts_H
);
247 picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic_in
) {
248 if( !p_pic_in
|| !p_filter
) return NULL
;
250 const video_format_t
*p_fmt_in
= &p_filter
->fmt_in
.video
;
251 filter_sys_t
*p_sys
= p_filter
->p_sys
;
253 picture_t
*p_pic_out
= filter_NewPicture( p_filter
);
255 picture_Release( p_pic_in
);
260 p_sys
->b_bake_request
= false;
262 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
))
263 p_sys
->b_init
= false;
265 if ((p_sys
->ps_pieces_shapes
== NULL
) && p_sys
->s_current_param
.b_advanced
&& (p_sys
->s_current_param
.i_shape_size
!= 0))
266 p_sys
->b_init
= false;
268 /* assert initialized & allocated data match with current frame characteristics */
269 if ( p_sys
->s_allocated
.i_planes
!= p_pic_out
->i_planes
)
270 p_sys
->b_init
= false;
271 p_sys
->s_current_param
.i_planes
= p_pic_out
->i_planes
;
272 if (p_sys
->ps_pict_planes
!= NULL
) {
273 for (uint8_t i_plane
= 0; i_plane
< p_sys
->s_allocated
.i_planes
; i_plane
++) {
274 if ( (p_sys
->ps_pict_planes
[i_plane
].i_lines
!= p_pic_in
->p
[i_plane
].i_visible_lines
)
275 || (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
)
276 || (p_sys
->ps_desk_planes
[i_plane
].i_lines
!= p_pic_out
->p
[i_plane
].i_visible_lines
)
277 || (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
) )
278 p_sys
->b_init
= false;
282 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
;
283 p_sys
->s_current_param
.i_pict_height
= (int) p_pic_in
->p
[0].i_visible_lines
;
284 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
;
285 p_sys
->s_current_param
.i_desk_height
= (int) p_pic_out
->p
[0].i_visible_lines
;
287 /* assert no mismatch between sizes */
288 if ( p_sys
->s_current_param
.i_pict_width
!= p_sys
->s_current_param
.i_desk_width
289 || p_sys
->s_current_param
.i_pict_height
!= p_sys
->s_current_param
.i_desk_height
290 || p_sys
->s_current_param
.i_pict_width
!= (int) p_fmt_in
->i_visible_width
291 || p_sys
->s_current_param
.i_pict_height
!= (int) p_fmt_in
->i_visible_height
) {
292 picture_Release(p_pic_in
);
293 picture_Release(p_pic_out
);
297 vlc_mutex_lock( &p_sys
->lock
);
299 /* check if we have to compute initial data */
300 if ( p_sys
->b_change_param
|| p_sys
->b_bake_request
|| !p_sys
->b_init
) {
301 if ( p_sys
->s_allocated
.i_rows
!= p_sys
->s_new_param
.i_rows
302 || p_sys
->s_allocated
.i_cols
!= p_sys
->s_new_param
.i_cols
303 || p_sys
->s_allocated
.i_rotate
!= p_sys
->s_new_param
.i_rotate
304 || p_sys
->s_allocated
.i_mode
!= p_sys
->s_new_param
.i_mode
305 || p_sys
->b_bake_request
|| !p_sys
->b_init
)
307 p_sys
->b_bake_request
= true;
308 p_sys
->b_init
= false;
309 p_sys
->b_shuffle_rqst
= true;
310 p_sys
->b_shape_init
= false;
313 if ( p_sys
->s_current_param
.i_border
!= p_sys
->s_new_param
.i_border
314 || p_sys
->s_current_param
.i_shape_size
!= p_sys
->s_new_param
.i_shape_size
)
316 p_sys
->b_bake_request
= true;
317 p_sys
->b_shape_init
= false;
320 /* depending on the game selected, set associated internal flags */
321 switch ( p_sys
->s_new_param
.i_mode
)
323 case 0: /* jigsaw puzzle */
324 p_sys
->s_new_param
.b_advanced
= true;
325 p_sys
->s_new_param
.b_blackslot
= false;
326 p_sys
->s_new_param
.b_near
= false;
328 case 1: /* sliding puzzle */
329 p_sys
->s_new_param
.b_advanced
= false;
330 p_sys
->s_new_param
.b_blackslot
= true;
331 p_sys
->s_new_param
.b_near
= true;
333 case 2: /* swap puzzle */
334 p_sys
->s_new_param
.b_advanced
= false;
335 p_sys
->s_new_param
.b_blackslot
= false;
336 p_sys
->s_new_param
.b_near
= true;
338 case 3: /* exchange puzzle */
339 p_sys
->s_new_param
.b_advanced
= false;
340 p_sys
->s_new_param
.b_blackslot
= false;
341 p_sys
->s_new_param
.b_near
= false;
344 p_sys
->s_current_param
.i_mode
= p_sys
->s_new_param
.i_mode
;
346 if ( p_sys
->s_current_param
.b_blackslot
!= p_sys
->s_new_param
.b_blackslot
347 && p_sys
->i_selected
== NO_PCE
348 && p_sys
->s_current_param
.b_blackslot
)
349 p_sys
->i_selected
= 0;
351 if ( p_sys
->s_current_param
.i_auto_shuffle_speed
!= p_sys
->s_new_param
.i_auto_shuffle_speed
)
352 p_sys
->i_auto_shuffle_countdown_val
= init_countdown(p_sys
->s_new_param
.i_auto_shuffle_speed
);
354 if ( p_sys
->s_current_param
.i_auto_solve_speed
!= p_sys
->s_new_param
.i_auto_solve_speed
)
355 p_sys
->i_auto_solve_countdown_val
= init_countdown(p_sys
->s_current_param
.i_auto_solve_speed
);
357 p_sys
->s_current_param
.i_rows
= p_sys
->s_new_param
.i_rows
;
358 p_sys
->s_current_param
.i_cols
= p_sys
->s_new_param
.i_cols
;
359 p_sys
->s_current_param
.i_pieces_nbr
= p_sys
->s_current_param
.i_rows
* p_sys
->s_current_param
.i_cols
;
360 p_sys
->s_current_param
.b_advanced
= p_sys
->s_new_param
.b_advanced
;
361 if (!p_sys
->s_new_param
.b_advanced
) {
362 p_sys
->s_current_param
.b_blackslot
= p_sys
->s_new_param
.b_blackslot
;
363 p_sys
->s_current_param
.b_near
= p_sys
->s_new_param
.b_near
|| p_sys
->s_new_param
.b_blackslot
;
364 p_sys
->s_current_param
.i_border
= 0;
365 p_sys
->s_current_param
.b_preview
= false;
366 p_sys
->s_current_param
.i_preview_size
= 0;
367 p_sys
->s_current_param
.i_shape_size
= 0;
368 p_sys
->s_current_param
.i_auto_shuffle_speed
= 0;
369 p_sys
->s_current_param
.i_auto_solve_speed
= 0;
370 p_sys
->s_current_param
.i_rotate
= 0;
374 p_sys
->s_current_param
.b_blackslot
= false;
375 p_sys
->s_current_param
.b_near
= false;
376 p_sys
->s_current_param
.i_border
= p_sys
->s_new_param
.i_border
;
377 p_sys
->s_current_param
.b_preview
= p_sys
->s_new_param
.b_preview
;
378 p_sys
->s_current_param
.i_preview_size
= p_sys
->s_new_param
.i_preview_size
;
379 p_sys
->s_current_param
.i_shape_size
= p_sys
->s_new_param
.i_shape_size
;
380 p_sys
->s_current_param
.i_auto_shuffle_speed
= p_sys
->s_new_param
.i_auto_shuffle_speed
;
381 p_sys
->s_current_param
.i_auto_solve_speed
= p_sys
->s_new_param
.i_auto_solve_speed
;
382 p_sys
->s_current_param
.i_rotate
= p_sys
->s_new_param
.i_rotate
;
384 p_sys
->b_change_param
= false;
387 vlc_mutex_unlock( &p_sys
->lock
);
389 /* generate initial puzzle data when needed */
390 if ( p_sys
->b_bake_request
) {
391 if (!p_sys
->b_shuffle_rqst
) {
392 /* here we have to keep the same position
393 * we have to save locations before generating new data
395 save_game_t
*ps_save_game
= puzzle_save(p_filter
);
397 return CopyInfoAndRelease( p_pic_out
, p_pic_in
);
398 i_ret
= puzzle_bake( p_filter
, p_pic_out
, p_pic_in
);
399 if ( i_ret
!= VLC_SUCCESS
)
401 free(ps_save_game
->ps_pieces
);
403 return CopyInfoAndRelease( p_pic_out
, p_pic_in
);
405 puzzle_load( p_filter
, ps_save_game
);
406 free(ps_save_game
->ps_pieces
);
410 i_ret
= puzzle_bake( p_filter
, p_pic_out
, p_pic_in
);
411 if ( i_ret
!= VLC_SUCCESS
)
412 return CopyInfoAndRelease( p_pic_out
, p_pic_in
);
416 /* shuffle the desk and generate pieces data */
417 if ( p_sys
->b_shuffle_rqst
&& p_sys
->b_init
) {
418 i_ret
= puzzle_bake_piece ( p_filter
);
419 if (i_ret
!= VLC_SUCCESS
)
420 return CopyInfoAndRelease( p_pic_out
, p_pic_in
);
423 /* preset output pic */
424 if ( !p_sys
->b_bake_request
&& !p_sys
->b_shuffle_rqst
&& p_sys
->b_init
&& !p_sys
->b_finished
)
425 puzzle_preset_desk_background(p_pic_out
, 0, 127, 127);
427 /* copy src to dst during init & bake process */
428 for( uint8_t i_plane
= 0; i_plane
< p_pic_out
->i_planes
; i_plane
++ )
429 memcpy( p_pic_out
->p
[i_plane
].p_pixels
, p_pic_in
->p
[i_plane
].p_pixels
,
430 p_pic_in
->p
[i_plane
].i_pitch
* (int32_t) p_pic_in
->p
[i_plane
].i_visible_lines
);
433 vlc_mutex_lock( &p_sys
->pce_lock
);
435 /* manage the game, adjust locations, groups and regenerate some corrupted data if any */
436 for (uint32_t i
= 0; i
< __MAX( 4, p_sys
->s_allocated
.i_pieces_nbr
/ 4 )
437 && ( !p_sys
->b_bake_request
&& !p_sys
->b_mouse_drag
438 && p_sys
->b_init
&& p_sys
->s_current_param
.b_advanced
); i
++)
440 puzzle_solve_pces_accuracy( p_filter
);
443 for (uint32_t i
= 0; i
< __MAX( 4, p_sys
->s_allocated
.i_pieces_nbr
/ 4 )
444 && ( !p_sys
->b_bake_request
&& !p_sys
->b_mouse_drag
445 && p_sys
->b_init
&& p_sys
->s_current_param
.b_advanced
); i
++)
447 puzzle_solve_pces_group( p_filter
);
450 if ( !p_sys
->b_bake_request
&& !p_sys
->b_mouse_drag
&& p_sys
->b_init
451 && p_sys
->s_current_param
.b_advanced
)
452 puzzle_count_pce_group( p_filter
);
453 if ( !p_sys
->b_bake_request
&& !p_sys
->b_mouse_drag
&& p_sys
->b_init
454 && p_sys
->s_current_param
.b_advanced
) {
455 i_ret
= puzzle_sort_layers( p_filter
);
456 if (i_ret
!= VLC_SUCCESS
)
458 vlc_mutex_unlock( &p_sys
->pce_lock
);
459 return CopyInfoAndRelease( p_pic_out
, p_pic_in
);
463 for (uint32_t i
= 0; i
< __MAX( 4, p_sys
->s_allocated
.i_pieces_nbr
/ 24 )
464 && ( !p_sys
->b_bake_request
&& !p_sys
->b_mouse_drag
465 && p_sys
->b_init
&& p_sys
->s_current_param
.b_advanced
); i
++)
467 p_sys
->i_calc_corn_loop
++;
468 p_sys
->i_calc_corn_loop
%= p_sys
->s_allocated
.i_pieces_nbr
;
469 puzzle_calculate_corners( p_filter
, p_sys
->i_calc_corn_loop
);
472 /* computer moves some piece depending on auto_solve and auto_shuffle param */
473 if ( !p_sys
->b_bake_request
&& !p_sys
->b_mouse_drag
&& p_sys
->b_init
474 && p_sys
->ps_puzzle_array
!= NULL
&& p_sys
->s_current_param
.b_advanced
)
476 puzzle_auto_shuffle( p_filter
);
477 puzzle_auto_solve( p_filter
);
480 vlc_mutex_unlock( &p_sys
->pce_lock
);
482 /* draw output pic */
483 if ( !p_sys
->b_bake_request
&& p_sys
->b_init
&& p_sys
->ps_puzzle_array
!= NULL
) {
485 puzzle_draw_borders(p_filter
, p_pic_in
, p_pic_out
);
487 p_sys
->i_pointed_pce
= NO_PCE
;
488 puzzle_draw_pieces(p_filter
, p_pic_in
, p_pic_out
);
490 /* when puzzle_draw_pieces() has not updated p_sys->i_pointed_pce,
491 * use puzzle_find_piece to define the piece pointed by the mouse
493 if (p_sys
->i_pointed_pce
== NO_PCE
)
494 p_sys
->i_mouse_drag_pce
= puzzle_find_piece( p_filter
, p_sys
->i_mouse_x
, p_sys
->i_mouse_y
, -1);
496 p_sys
->i_mouse_drag_pce
= p_sys
->i_pointed_pce
;
498 if (p_sys
->s_current_param
.b_preview
)
499 puzzle_draw_preview(p_filter
, p_pic_in
, p_pic_out
);
501 /* highlight the selected piece when not playing jigsaw mode */
502 if ( p_sys
->i_selected
!= NO_PCE
&& !p_sys
->s_current_param
.b_blackslot
503 && !p_sys
->s_current_param
.b_advanced
)
505 int32_t c
= (p_sys
->i_selected
% p_sys
->s_allocated
.i_cols
);
506 int32_t r
= (p_sys
->i_selected
/ p_sys
->s_allocated
.i_cols
);
508 puzzle_draw_rectangle(p_pic_out
,
509 p_sys
->ps_puzzle_array
[r
][c
][0].i_x
,
510 p_sys
->ps_puzzle_array
[r
][c
][0].i_y
,
511 p_sys
->ps_puzzle_array
[r
][c
][0].i_width
,
512 p_sys
->ps_puzzle_array
[r
][c
][0].i_lines
,
516 /* draw the blackslot when playing sliding puzzle mode */
517 if ( p_sys
->i_selected
!= NO_PCE
&& p_sys
->s_current_param
.b_blackslot
518 && !p_sys
->s_current_param
.b_advanced
)
520 int32_t c
= (p_sys
->i_selected
% p_sys
->s_allocated
.i_cols
);
521 int32_t r
= (p_sys
->i_selected
/ p_sys
->s_allocated
.i_cols
);
523 puzzle_fill_rectangle(p_pic_out
,
524 p_sys
->ps_puzzle_array
[r
][c
][0].i_x
,
525 p_sys
->ps_puzzle_array
[r
][c
][0].i_y
,
526 p_sys
->ps_puzzle_array
[r
][c
][0].i_width
,
527 p_sys
->ps_puzzle_array
[r
][c
][0].i_lines
,
531 /* Draw the 'puzzle_shuffle' button if the puzzle is finished */
532 if ( p_sys
->b_finished
)
533 puzzle_draw_sign(p_pic_out
, 0, 0, SHUFFLE_WIDTH
, SHUFFLE_LINES
, ppsz_shuffle_button
, false);
535 /* draw an arrow at mouse pointer to indicate current action (rotation...) */
536 if ((p_sys
->i_mouse_drag_pce
!= NO_PCE
) && !p_sys
->b_mouse_drag
537 && !p_sys
->b_finished
&& p_sys
->s_current_param
.b_advanced
)
539 vlc_mutex_lock( &p_sys
->pce_lock
);
543 if (p_sys
->s_current_param
.i_rotate
!= 3)
545 else if ( (p_sys
->ps_pieces
[p_sys
->i_mouse_drag_pce
].i_actual_angle
& 1) == 0)
546 i_delta_x
= p_sys
->ps_desk_planes
[0].i_pce_max_width
/ 6;
548 i_delta_x
= p_sys
->ps_desk_planes
[0].i_pce_max_lines
/ 6;
550 if (p_sys
->s_current_param
.i_rotate
== 0)
551 p_sys
->i_mouse_action
= 0;
552 else if (p_sys
->s_current_param
.i_rotate
== 1)
553 p_sys
->i_mouse_action
= 2;
554 else if ( p_sys
->i_mouse_x
>= ( p_sys
->ps_pieces
[p_sys
->i_mouse_drag_pce
].i_center_x
+ i_delta_x
) )
555 p_sys
->i_mouse_action
= -1; /* rotate counterclockwise */
556 else if ( p_sys
->i_mouse_x
<= ( p_sys
->ps_pieces
[p_sys
->i_mouse_drag_pce
].i_center_x
- i_delta_x
) )
557 p_sys
->i_mouse_action
= +1;
559 p_sys
->i_mouse_action
= 4; /* center click: only mirror */
561 if ( p_sys
->i_mouse_action
== +1 )
562 puzzle_draw_sign(p_pic_out
, p_sys
->i_mouse_x
- ARROW_WIDTH
,
563 p_sys
->i_mouse_y
, ARROW_WIDTH
, ARROW_LINES
, ppsz_rot_arrow_sign
, false);
564 else if ( p_sys
->i_mouse_action
== -1 )
565 puzzle_draw_sign(p_pic_out
, p_sys
->i_mouse_x
- ARROW_WIDTH
,
566 p_sys
->i_mouse_y
, ARROW_WIDTH
, ARROW_LINES
, ppsz_rot_arrow_sign
, true);
567 else if ( p_sys
->i_mouse_action
== 4 )
568 puzzle_draw_sign(p_pic_out
, p_sys
->i_mouse_x
- ARROW_WIDTH
,
569 p_sys
->i_mouse_y
, ARROW_WIDTH
, ARROW_LINES
, ppsz_mir_arrow_sign
, false);
571 vlc_mutex_unlock( &p_sys
->pce_lock
);
575 return CopyInfoAndRelease( p_pic_out
, p_pic_in
);
578 /*****************************************************************************
580 *****************************************************************************/
581 int puzzle_Callback( vlc_object_t
*p_this
, char const *psz_var
,
582 vlc_value_t oldval
, vlc_value_t newval
,
585 VLC_UNUSED(p_this
); VLC_UNUSED(oldval
);
586 filter_sys_t
*p_sys
= (filter_sys_t
*)p_data
;
588 vlc_mutex_lock( &p_sys
->lock
);
589 if( !strcmp( psz_var
, CFG_PREFIX
"rows" ) ) {
590 p_sys
->s_new_param
.i_rows
= __MAX( 1, newval
.i_int
);
592 else if( !strcmp( psz_var
, CFG_PREFIX
"cols" ) ) {
593 p_sys
->s_new_param
.i_cols
= __MAX( 1, newval
.i_int
);
595 else if( !strcmp( psz_var
, CFG_PREFIX
"border" ) ) {
596 p_sys
->s_new_param
.i_border
= __MAX( 0, newval
.i_int
);
598 else if( !strcmp( psz_var
, CFG_PREFIX
"preview" ) ) {
599 p_sys
->s_new_param
.b_preview
= newval
.b_bool
;
601 else if( !strcmp( psz_var
, CFG_PREFIX
"preview-size" ) ) {
602 p_sys
->s_new_param
.i_preview_size
= newval
.i_int
;
604 else if( !strcmp( psz_var
, CFG_PREFIX
"shape-size" ) ) {
605 p_sys
->s_new_param
.i_shape_size
= newval
.i_int
;
607 else if( !strcmp( psz_var
, CFG_PREFIX
"auto-shuffle" ) ) {
608 p_sys
->s_new_param
.i_auto_shuffle_speed
= newval
.i_int
;
610 else if( !strcmp( psz_var
, CFG_PREFIX
"auto-solve" ) ) {
611 p_sys
->s_new_param
.i_auto_solve_speed
= newval
.i_int
;
613 else if( !strcmp( psz_var
, CFG_PREFIX
"rotation" ) ) {
614 p_sys
->s_new_param
.i_rotate
= newval
.i_int
;
616 else if( !strcmp( psz_var
, CFG_PREFIX
"mode" ) ) {
617 p_sys
->s_new_param
.i_mode
= newval
.i_int
;
620 p_sys
->b_change_param
= true;
621 vlc_mutex_unlock( &p_sys
->lock
);
627 int puzzle_mouse( filter_t
*p_filter
, vlc_mouse_t
*p_new
,
628 const vlc_mouse_t
*p_old
)
630 filter_sys_t
*p_sys
= p_filter
->p_sys
;
631 const video_format_t
*p_fmt_in
= &p_filter
->fmt_in
.video
;
633 /* Only take events inside the puzzle area */
634 if( p_new
->i_x
< 0 || p_new
->i_x
>= (int)p_fmt_in
->i_width
||
635 p_new
->i_y
< 0 || p_new
->i_y
>= (int)p_fmt_in
->i_height
)
638 if (! p_sys
->b_init
|| p_sys
->b_change_param
) {
642 p_sys
->i_mouse_x
= p_new
->i_x
;
643 p_sys
->i_mouse_y
= p_new
->i_y
;
645 /* If the puzzle is finished, shuffle it if needed */
646 if( p_sys
->b_finished
) {
647 p_sys
->b_mouse_drag
= false;
648 p_sys
->b_mouse_mvt
= false;
649 if( vlc_mouse_HasPressed( p_old
, p_new
, MOUSE_BUTTON_LEFT
) &&
650 p_new
->i_x
< SHUFFLE_WIDTH
&& p_new
->i_y
< SHUFFLE_LINES
)
652 p_sys
->b_shuffle_rqst
= true;
657 /* otherwise we can forward the mouse */
662 if ( !p_sys
->s_current_param
.b_advanced
) {
663 /* "square" game mode (sliding puzzle, swap...) */
664 const bool b_clicked
= vlc_mouse_HasPressed( p_old
, p_new
, MOUSE_BUTTON_LEFT
);
669 const int32_t i_border_width
= p_fmt_in
->i_width
* p_sys
->s_current_param
.i_border
/ 100 / 2;
670 const int32_t i_border_height
= p_fmt_in
->i_height
* p_sys
->s_current_param
.i_border
/ 100 / 2;
671 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
);
672 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
);
674 const int32_t i_pos
= i_pos_y
* p_sys
->s_allocated
.i_cols
+ i_pos_x
;
675 p_sys
->i_mouse_drag_pce
= i_pos
;
677 /* do not take into account if border clicked */
678 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
) )
682 else if( p_sys
->i_selected
== NO_PCE
)
683 p_sys
->i_selected
= i_pos
;
684 else if( p_sys
->i_selected
== i_pos
&& !p_sys
->s_current_param
.b_blackslot
)
685 p_sys
->i_selected
= -1;
686 else if( ( p_sys
->i_selected
== i_pos
+ 1 && p_sys
->i_selected
%p_sys
->s_allocated
.i_cols
!= 0 )
687 || ( p_sys
->i_selected
== i_pos
- 1 && i_pos
% p_sys
->s_allocated
.i_cols
!= 0 )
688 || p_sys
->i_selected
== i_pos
+ p_sys
->s_allocated
.i_cols
689 || p_sys
->i_selected
== i_pos
- p_sys
->s_allocated
.i_cols
690 || !p_sys
->s_current_param
.b_near
)
693 /* Swap two pieces */
694 int32_t a
= p_sys
->pi_order
[ p_sys
->i_selected
];
695 p_sys
->pi_order
[ p_sys
->i_selected
] = p_sys
->pi_order
[ i_pos
];
696 p_sys
->pi_order
[ i_pos
] = a
;
698 /* regen piece location from updated pi_order */
699 if ( p_sys
->ps_pieces
!= NULL
&& p_sys
->pi_order
!= NULL
)
702 for (int32_t row
= 0; row
< p_sys
->s_allocated
.i_rows
; row
++) {
703 for (int32_t col
= 0; col
< p_sys
->s_allocated
.i_cols
; col
++) {
704 int32_t orow
= p_sys
->pi_order
[i
] / (p_sys
->s_allocated
.i_cols
);
705 int32_t ocol
= p_sys
->pi_order
[i
] % (p_sys
->s_allocated
.i_cols
);
707 p_sys
->ps_pieces
[i
].i_original_row
= orow
;
708 p_sys
->ps_pieces
[i
].i_original_col
= ocol
;
709 p_sys
->ps_pieces
[i
].i_top_shape
= 0;
710 p_sys
->ps_pieces
[i
].i_btm_shape
= 0;
711 p_sys
->ps_pieces
[i
].i_right_shape
= 0;
712 p_sys
->ps_pieces
[i
].i_left_shape
= 0;
713 p_sys
->ps_pieces
[i
].i_actual_angle
= 0;
714 p_sys
->ps_pieces
[i
].i_actual_mirror
= +1;
715 p_sys
->ps_pieces
[i
].b_overlap
= false;
716 p_sys
->ps_pieces
[i
].b_finished
= false;
717 p_sys
->ps_pieces
[i
].i_group_ID
= i
;
719 for (uint8_t i_plane
= 0; i_plane
< p_sys
->s_allocated
.i_planes
; i_plane
++) {
720 p_sys
->ps_pieces
[i
].ps_piece_in_plane
[i_plane
].i_width
= p_sys
->ps_puzzle_array
[row
][col
][i_plane
].i_width
;
721 p_sys
->ps_pieces
[i
].ps_piece_in_plane
[i_plane
].i_lines
= p_sys
->ps_puzzle_array
[row
][col
][i_plane
].i_lines
;
722 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
;
723 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
;
724 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
;
725 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
;
732 p_sys
->i_selected
= p_sys
->s_current_param
.b_blackslot
? i_pos
: NO_PCE
;
733 p_sys
->b_finished
= puzzle_is_finished( p_sys
, p_sys
->pi_order
);
737 else /* jigsaw puzzle mode */
739 if ((p_sys
->ps_desk_planes
== NULL
) || (p_sys
->ps_pict_planes
== NULL
) || (p_sys
->ps_puzzle_array
== NULL
) || (p_sys
->ps_pieces
== NULL
)) {
743 if( vlc_mouse_HasPressed( p_old
, p_new
, MOUSE_BUTTON_LEFT
) )
746 vlc_mutex_lock( &p_sys
->pce_lock
);
748 if (p_sys
->i_mouse_drag_pce
!= NO_PCE
) {
749 int i_ret
= puzzle_piece_foreground( p_filter
, p_sys
->i_mouse_drag_pce
);
750 if (i_ret
!= VLC_SUCCESS
)
752 vlc_mutex_unlock( &p_sys
->pce_lock
);
755 p_sys
->i_mouse_drag_pce
= 0;
757 uint32_t i_group_ID
= p_sys
->ps_pieces
[0].i_group_ID
;
758 for (uint32_t i
= 0; i
< p_sys
->s_allocated
.i_pieces_nbr
; i
++) {
759 if ( i_group_ID
== p_sys
->ps_pieces
[i
].i_group_ID
) {
760 p_sys
->ps_pieces
[i
].b_finished
= false;
767 p_sys
->b_mouse_drag
= true;
768 p_sys
->b_mouse_mvt
= false;
771 /* player click an empty area then search a piece which is overlapping another one and place it here */
772 p_sys
->b_mouse_drag
= false;
773 for (uint32_t i
= 0; i
< p_sys
->s_allocated
.i_pieces_nbr
; i
++)
774 if ( p_sys
->ps_pieces
[i
].b_overlap
) {
775 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
);
776 p_sys
->ps_pieces
[i
].b_overlap
= false;
779 p_sys
->b_mouse_drag
= false;
782 vlc_mutex_unlock( &p_sys
->pce_lock
);
785 else if( vlc_mouse_HasReleased( p_old
, p_new
, MOUSE_BUTTON_LEFT
) )
787 if ( !p_sys
->b_mouse_mvt
&& p_sys
->b_mouse_drag
) {
788 /* piece clicked without any mouse mvt => rotate it or mirror */
789 if ( p_sys
->s_current_param
.i_rotate
!= 0) {
790 vlc_mutex_lock( &p_sys
->pce_lock
);
792 uint32_t i_group_ID
= p_sys
->ps_pieces
[0].i_group_ID
;
794 for (uint32_t i
= 0; i
< p_sys
->s_allocated
.i_pieces_nbr
; i
++)
795 if ( i_group_ID
== p_sys
->ps_pieces
[i
].i_group_ID
)
796 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 );
798 vlc_mutex_unlock( &p_sys
->pce_lock
);
801 p_sys
->b_mouse_drag
= false;
802 p_sys
->b_mouse_mvt
= false;
804 else /* no action on left button */
806 /* check if the mouse is in the preview area */
807 switch ( p_sys
->i_preview_pos
)
810 if ( p_new
->i_x
< (int)p_fmt_in
->i_width
/ 2 && p_new
->i_y
< (int)p_fmt_in
->i_height
/ 2 )
811 p_sys
->i_preview_pos
++;
814 if ( p_new
->i_x
> (int)p_fmt_in
->i_width
/ 2 && p_new
->i_y
< (int)p_fmt_in
->i_height
/ 2 )
815 p_sys
->i_preview_pos
++;
818 if ( p_new
->i_x
> (int)p_fmt_in
->i_width
/ 2 && p_new
->i_y
> (int)p_fmt_in
->i_height
/ 2 )
819 p_sys
->i_preview_pos
++;
822 if ( p_new
->i_x
< (int)p_fmt_in
->i_width
/ 2 && p_new
->i_y
> (int)p_fmt_in
->i_height
/ 2 )
823 p_sys
->i_preview_pos
++;
826 p_sys
->i_preview_pos
%= 4;
828 if ( !vlc_mouse_IsLeftPressed( p_new
) )
829 p_sys
->b_mouse_drag
= false;
832 vlc_mouse_GetMotion( &i_dx
, &i_dy
, p_old
, p_new
);
833 if ( i_dx
!= 0 || i_dy
!= 0 )
834 p_sys
->b_mouse_mvt
= true;
836 if (p_sys
->b_mouse_drag
) {
837 if ( ( p_new
->i_x
<= 0 ) || ( p_new
->i_y
<= 0 ) || ( p_new
->i_x
>= (int) p_fmt_in
->i_width
)
838 || ( p_new
->i_y
>= (int) p_fmt_in
->i_height
) )
840 /* if the mouse is outside the window, stop moving the piece/group */
841 p_sys
->b_mouse_drag
= false;
842 p_sys
->b_mouse_mvt
= true;
844 else if ( i_dx
!= 0 || i_dy
!= 0 )
846 vlc_mutex_lock( &p_sys
->pce_lock
);
848 puzzle_move_group( p_filter
, p_sys
->i_mouse_drag_pce
, i_dx
, i_dy
);
850 vlc_mutex_unlock( &p_sys
->pce_lock
);