v4l: support libv4l
[vlc/solaris.git] / modules / video_filter / puzzle.c
blobd07cb69f32e84ebb33a85aa53f9753c8a49f2e49
1 /*****************************************************************************
2 * puzzle.c : Puzzle game
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
5 * $Id$
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 #include <math.h>
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
37 #include "filter_picture.h"
39 /*****************************************************************************
40 * Module descriptor
41 *****************************************************************************/
42 #define ROWS_TEXT N_("Number of puzzle rows")
43 #define ROWS_LONGTEXT N_("Number of puzzle rows")
44 #define COLS_TEXT N_("Number of puzzle columns")
45 #define COLS_LONGTEXT N_("Number of puzzle columns")
46 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
47 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
49 #define CFG_PREFIX "puzzle-"
51 static int Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
54 vlc_module_begin()
55 set_description( N_("Puzzle interactive game video filter") )
56 set_shortname( N_( "Puzzle" ))
57 set_capability( "video filter2", 0 )
58 set_category( CAT_VIDEO )
59 set_subcategory( SUBCAT_VIDEO_VFILTER )
61 add_integer_with_range( CFG_PREFIX "rows", 4, 2, 16, NULL,
62 ROWS_TEXT, ROWS_LONGTEXT, false )
63 add_integer_with_range( CFG_PREFIX "cols", 4, 2, 16, NULL,
64 COLS_TEXT, COLS_LONGTEXT, false )
65 add_bool( CFG_PREFIX "black-slot", false, NULL,
66 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false )
68 set_callbacks( Open, Close )
69 vlc_module_end()
72 /*****************************************************************************
73 * Local prototypes
74 *****************************************************************************/
75 static const char *const ppsz_filter_options[] = {
76 "rows", "cols", "black-slot", NULL
79 static picture_t *Filter( filter_t *, picture_t * );
80 static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t *, const vlc_mouse_t * );
82 static bool IsFinished( filter_sys_t * );
83 static void Shuffle( filter_sys_t * );
84 static int PuzzleCallback( vlc_object_t *, char const *,
85 vlc_value_t, vlc_value_t, void * );
87 struct filter_sys_t
89 /* */
90 int i_cols;
91 int i_rows;
92 bool b_blackslot;
93 int *pi_order;
94 int i_selected;
95 bool b_finished;
97 /* */
98 vlc_mutex_t lock;
99 bool b_change;
100 struct
102 int i_cols;
103 int i_rows;
104 bool b_blackslot;
105 } change;
108 #define SHUFFLE_WIDTH 81
109 #define SHUFFLE_HEIGHT 13
110 static const char *shuffle_button[] =
112 ".................................................................................",
113 ".............. ............................ ........ ...... ...............",
114 ".............. ........................... ......... ........ ...............",
115 ".............. ........................... ......... ........ ...............",
116 ".. ....... . ....... .... ...... ...... ...... ........ ...",
117 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
118 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
119 ". ....... .... ...... .... ....... ......... ........ ...... .",
120 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
121 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
122 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
123 ".. ....... .... ....... . ....... ......... ........ ........ ..",
124 "................................................................................."
129 * Open the filter
131 static int Open( vlc_object_t *p_this )
133 filter_t *p_filter = (filter_t *)p_this;
134 filter_sys_t *p_sys;
136 /* */
137 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
139 msg_Err( p_filter, "Input and output format does not match" );
140 return VLC_EGENERIC;
143 /* Allocate structure */
144 p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
145 if( !p_sys )
146 return VLC_ENOMEM;
148 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
149 p_filter->p_cfg );
151 p_sys->pi_order = NULL;
153 vlc_mutex_init( &p_sys->lock );
154 p_sys->change.i_rows =
155 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" );
156 p_sys->change.i_cols =
157 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" );
158 p_sys->change.b_blackslot =
159 var_CreateGetBoolCommand( p_filter, CFG_PREFIX "black-slot" );
160 p_sys->b_change = true;
162 var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
163 var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
164 var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
166 p_filter->pf_video_filter = Filter;
167 p_filter->pf_mouse = Mouse;
169 return VLC_SUCCESS;
173 * Close the filter
175 static void Close( vlc_object_t *p_this )
177 filter_t *p_filter = (filter_t *)p_this;
178 filter_sys_t *p_sys = p_filter->p_sys;
180 var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
181 var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
182 var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
184 vlc_mutex_destroy( &p_sys->lock );
185 free( p_sys->pi_order );
187 free( p_sys );
191 * Filter a picture
193 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
195 filter_sys_t *p_sys = p_filter->p_sys;
197 picture_t *p_outpic = filter_NewPicture( p_filter );
198 if( !p_outpic )
200 picture_Release( p_pic );
201 return NULL;
204 /* */
205 vlc_mutex_lock( &p_sys->lock );
206 if( p_sys->b_change )
208 p_sys->i_rows = p_sys->change.i_rows;
209 p_sys->i_cols = p_sys->change.i_cols;
210 p_sys->b_blackslot = p_sys->change.b_blackslot;
211 p_sys->b_change = false;
213 Shuffle( p_sys );
215 vlc_mutex_unlock( &p_sys->lock );
217 /* */
218 const int i_rows = p_sys->i_rows;
219 const int i_cols = p_sys->i_cols;
221 /* Draw each piece of the puzzle at the right place */
222 for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
224 const plane_t *p_in = &p_pic->p[i_plane];
225 const int i_pitch = p_in->i_pitch;
226 plane_t *p_out = &p_outpic->p[i_plane];
228 for( int i = 0; i < i_cols * i_rows; i++ )
230 int i_col = i % i_cols;
231 int i_row = i / i_cols;
232 int i_ocol = p_sys->pi_order[i] % i_cols;
233 int i_orow = p_sys->pi_order[i] / i_cols;
234 int i_last_row = i_row + 1;
235 i_orow *= p_in->i_lines / i_rows;
236 i_row *= p_in->i_lines / i_rows;
237 i_last_row *= p_in->i_lines / i_rows;
239 if( p_sys->b_blackslot && !p_sys->b_finished && i == p_sys->i_selected )
241 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
242 for( ; i_row < i_last_row; i_row++, i_orow++ )
244 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
245 color, i_pitch / i_cols );
248 else
250 for( ; i_row < i_last_row; i_row++, i_orow++ )
252 memcpy( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
253 p_in->p_pixels + i_orow * i_pitch + i_ocol * i_pitch / i_cols,
254 i_pitch / i_cols );
260 /* Draw the borders of the selected slot */
261 if( p_sys->i_selected != -1 && !p_sys->b_blackslot )
263 const plane_t *p_in = &p_pic->p[Y_PLANE];
264 const int i_pitch = p_in->i_pitch;
265 plane_t *p_out = &p_outpic->p[Y_PLANE];
267 int i_col = p_sys->i_selected % i_cols;
268 int i_row = p_sys->i_selected / i_cols;
269 int i_last_row = i_row + 1;
270 i_row *= p_in->i_lines / i_rows;
271 i_last_row *= p_in->i_lines / i_rows;
272 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
273 0xff, i_pitch / i_cols );
274 for( ; i_row < i_last_row; i_row++ )
276 p_out->p_pixels[ i_row * i_pitch
277 + i_col * i_pitch / i_cols ] = 0xff;
278 p_out->p_pixels[ i_row * i_pitch
279 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
281 i_row--;
282 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
283 0xff, i_pitch / i_cols );
286 /* Draw the 'Shuffle' button if the puzzle is finished */
287 if( p_sys->b_finished )
289 plane_t *p_out = &p_outpic->p[Y_PLANE];
290 for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
292 for( int j = 0; j < SHUFFLE_WIDTH; j++ )
294 if( shuffle_button[i][j] == '.' )
295 p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
300 return CopyInfoAndRelease( p_outpic, p_pic );
303 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
304 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
306 filter_sys_t *p_sys = p_filter->p_sys;
307 const video_format_t *p_fmt = &p_filter->fmt_in.video;
309 /* Only take events inside the puzzle erea */
310 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
311 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
312 return VLC_EGENERIC;
314 /* */
315 const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
317 /* If the puzzle is finished, shuffle it if needed */
318 if( p_sys->b_finished )
320 if( b_clicked &&
321 p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
323 p_sys->b_change = true;
324 return VLC_EGENERIC;
326 else
328 /* This is the only case where we can forward the mouse */
329 *p_mouse = *p_new;
330 return VLC_SUCCESS;
333 if( !b_clicked )
334 return VLC_EGENERIC;
336 /* */
337 const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
338 const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
339 const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
341 if( p_sys->i_selected == -1 )
343 p_sys->i_selected = i_pos;
345 else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
347 p_sys->i_selected = -1;
349 else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
350 || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
351 || p_sys->i_selected == i_pos + p_sys->i_cols
352 || p_sys->i_selected == i_pos - p_sys->i_cols )
354 /* Swap two pieces */
355 int a = p_sys->pi_order[ p_sys->i_selected ];
356 p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ i_pos ];
357 p_sys->pi_order[ i_pos ] = a;
359 p_sys->i_selected = p_sys->b_blackslot ? i_pos : -1;
360 p_sys->b_finished = IsFinished( p_sys );
362 return VLC_EGENERIC;
365 /*****************************************************************************
366 * Misc stuff...
367 *****************************************************************************/
368 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
369 vlc_value_t oldval, vlc_value_t newval,
370 void *p_data )
372 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
373 filter_sys_t *p_sys = (filter_sys_t *)p_data;
375 vlc_mutex_lock( &p_sys->lock );
376 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
378 p_sys->change.i_rows = __MAX( 1, newval.i_int );
380 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
382 p_sys->change.i_cols = __MAX( 1, newval.i_int );
384 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
386 p_sys->change.b_blackslot = newval.b_bool;
388 p_sys->b_change = true;
389 vlc_mutex_unlock( &p_sys->lock );
391 return VLC_SUCCESS;
394 static bool IsFinished( filter_sys_t *p_sys )
396 for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
398 if( i != p_sys->pi_order[i] )
399 return false;
401 return true;
404 static bool IsValid( filter_sys_t *p_sys )
406 const int i_count = p_sys->i_cols * p_sys->i_rows;
408 if( !p_sys->b_blackslot )
409 return true;
411 int d = 0;
412 for( int i = 0; i < i_count; i++ )
414 if( p_sys->pi_order[i] == i_count - 1 )
416 d += i / p_sys->i_cols + 1;
417 continue;
419 for( int j = i+1; j < i_count; j++ )
421 if( p_sys->pi_order[j] == i_count - 1 )
422 continue;
423 if( p_sys->pi_order[i] > p_sys->pi_order[j] )
424 d++;
427 return (d%2) == 0;
430 static void Shuffle( filter_sys_t *p_sys )
432 const int i_count = p_sys->i_cols * p_sys->i_rows;
434 free( p_sys->pi_order );
436 p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
439 for( int i = 0; i < i_count; i++ )
440 p_sys->pi_order[i] = -1;
442 for( int c = 0; c < i_count; )
444 int i = rand() % i_count;
445 if( p_sys->pi_order[i] == -1 )
446 p_sys->pi_order[i] = c++;
448 p_sys->b_finished = IsFinished( p_sys );
450 } while( p_sys->b_finished || !IsValid( p_sys ) );
452 if( p_sys->b_blackslot )
454 for( int i = 0; i < i_count; i++ )
456 if( p_sys->pi_order[i] == i_count - 1 )
458 p_sys->i_selected = i;
459 break;
463 else
465 p_sys->i_selected = -1;