contrib: cargo: use cargo/vendored-openssl if needed
[vlc.git] / modules / video_filter / puzzle.c
blobbef39368bc46ce25df2e1926ce0364dae6fb637a
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 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 #include <math.h>
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>
39 #include <vlc_rand.h>
41 #include "filter_picture.h"
43 #include "puzzle.h"
44 #include "puzzle_bezier.h"
45 #include "puzzle_lib.h"
46 #include "puzzle_pce.h"
47 #include "puzzle_mgt.h"
49 /*****************************************************************************
50 * Module descriptor
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 * );
83 vlc_module_begin()
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 )
114 vlc_module_end()
116 /*****************************************************************************
117 * Local prototypes
118 *****************************************************************************/
120 const char *const ppsz_filter_options[] = {
121 "rows", "cols","border", "preview", "preview-size", "mode", "shape-size", "auto-shuffle", "auto-solve", "rotation", NULL
125 * Open the filter
127 static int Open( vlc_object_t *p_this )
129 filter_t *p_filter = (filter_t *)p_this;
130 filter_sys_t *p_sys;
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" );
135 return VLC_EGENERIC;
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 )
141 return VLC_EGENERIC;
143 /* Allocate structure */
144 p_filter->p_sys = p_sys = calloc(1, sizeof( *p_sys ) );
145 if( !p_sys )
146 return VLC_ENOMEM;
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;
161 return VLC_ENOMEM;
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,
168 p_filter->p_cfg );
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;
208 return VLC_SUCCESS;
212 * Close the filter
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);
241 free( p_sys );
245 * Filter a picture
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 );
254 if( !p_pic_out ) {
255 picture_Release( p_pic_in );
256 return NULL;
259 int i_ret = 0;
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);
294 return NULL;
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;
327 break;
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;
332 break;
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;
337 break;
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;
342 break;
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;
372 else
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);
396 if (!ps_save_game)
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);
402 free(ps_save_game);
403 return CopyInfoAndRelease( p_pic_out, p_pic_in );
405 puzzle_load( p_filter, ps_save_game);
406 free(ps_save_game->ps_pieces);
407 free(ps_save_game);
409 else {
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);
426 else {
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);
495 else
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,
513 255, 127, 127);
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,
528 0, 127, 127);
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 );
541 int32_t i_delta_x;
543 if (p_sys->s_current_param.i_rotate != 3)
544 i_delta_x = 0;
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;
547 else
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;
558 else
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 /*****************************************************************************
579 * Misc stuff...
580 *****************************************************************************/
581 int puzzle_Callback( vlc_object_t *p_this, char const *psz_var,
582 vlc_value_t oldval, vlc_value_t newval,
583 void *p_data )
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 );
623 return VLC_SUCCESS;
626 /* mouse callback */
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 )
636 return VLC_EGENERIC;
638 if (! p_sys->b_init || p_sys->b_change_param) {
639 return VLC_SUCCESS;
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;
653 return VLC_EGENERIC;
655 else
657 /* otherwise we can forward the mouse */
658 return VLC_SUCCESS;
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 );
666 if( b_clicked )
668 /* */
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 ) )
680 return VLC_SUCCESS;
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 )
701 int32_t i = 0;
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;
727 i++;
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)) {
740 return VLC_SUCCESS;
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 );
753 return i_ret;
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;
762 else {
763 break;
767 p_sys->b_mouse_drag = true;
768 p_sys->b_mouse_mvt = false;
770 else {
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;
777 break;
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 )
809 case 0:
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++;
812 break;
813 case 1:
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++;
816 break;
817 case 2:
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++;
820 break;
821 case 3:
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++;
824 break;
826 p_sys->i_preview_pos %= 4;
828 if ( !vlc_mouse_IsLeftPressed( p_new ) )
829 p_sys->b_mouse_drag = false;
831 int i_dx, i_dy;
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 );
855 return VLC_EGENERIC;