]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
Use calloc.
[vlc] / modules / video_filter / puzzle.c
1 /*****************************************************************************
2  * puzzle.c : Puzzle game
3  *****************************************************************************
4  * Copyright (C) 2005-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 #include <math.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
36
37 #include "filter_picture.h"
38
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.")
48
49 #define CFG_PREFIX "puzzle-"
50
51 static int  Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
53
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 )
60
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 )
67
68     set_callbacks( Open, Close )
69 vlc_module_end()
70
71
72 /*****************************************************************************
73  * Local prototypes
74  *****************************************************************************/
75 static const char *const ppsz_filter_options[] = {
76     "rows", "cols", "black-slot", NULL
77 };
78
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 * );
81
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 * );
86
87 struct filter_sys_t
88 {
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;
96
97     /* */
98     vlc_mutex_t lock;
99     bool b_change;
100     struct
101     {
102         int i_cols;
103         int i_rows;
104         bool b_blackslot;
105     } change;
106 };
107
108 #define SHUFFLE_WIDTH 81
109 #define SHUFFLE_HEIGHT 13
110 static const char *shuffle_button[] =
111 {
112 ".................................................................................",
113 "..............  ............................   ........   ......  ...............",
114 "..............  ...........................  .........  ........  ...............",
115 "..............  ...........................  .........  ........  ...............",
116 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
117 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
118 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
119 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
120 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
121 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
122 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
123 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
124 "................................................................................."
125 };
126
127
128 /**
129  * Open the filter
130  */
131 static int Open( vlc_object_t *p_this )
132 {
133     filter_t *p_filter = (filter_t *)p_this;
134     filter_sys_t *p_sys;
135
136     /* */
137     if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
138     {
139         msg_Err( p_filter, "Input and output format does not match" );
140         return VLC_EGENERIC;
141     }
142
143     /* Allocate structure */
144     p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
145     if( !p_sys )
146         return VLC_ENOMEM;
147
148     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
149                        p_filter->p_cfg );
150
151     p_sys->pi_order = NULL;
152
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;
161
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 );
165
166     p_filter->pf_video_filter = Filter;
167     p_filter->pf_video_mouse = Mouse;
168
169     return VLC_SUCCESS;
170 }
171
172 /**
173  * Close the filter
174  */
175 static void Close( vlc_object_t *p_this )
176 {
177     filter_t *p_filter = (filter_t *)p_this;
178     filter_sys_t *p_sys = p_filter->p_sys;
179
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 );
183
184     vlc_mutex_destroy( &p_sys->lock );
185     free( p_sys->pi_order );
186
187     free( p_sys );
188 }
189
190 /**
191  * Filter a picture
192  */
193 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
194 {
195     filter_sys_t *p_sys = p_filter->p_sys;
196
197     picture_t *p_outpic = filter_NewPicture( p_filter );
198     if( !p_outpic )
199     {
200         picture_Release( p_pic );
201         return NULL;
202     }
203
204     /* */
205     vlc_mutex_lock( &p_sys->lock );
206     if( p_sys->b_change )
207     {
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;
212
213         Shuffle( p_sys );
214     }
215     vlc_mutex_unlock( &p_sys->lock );
216
217     /* */
218     const int i_rows = p_sys->i_rows;
219     const int i_cols = p_sys->i_cols;
220
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++ )
223     {
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];
227
228         for( int i = 0; i < i_cols * i_rows; i++ )
229         {
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;
238
239             if( p_sys->b_blackslot && !p_sys->b_finished && i == p_sys->i_selected )
240             {
241                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
242                 for( ; i_row < i_last_row; i_row++, i_orow++ )
243                 {
244                     memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
245                             color, i_pitch / i_cols );
246                 }
247             }
248             else
249             {
250                 for( ; i_row < i_last_row; i_row++, i_orow++ )
251                 {
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 );
255                 }
256             }
257         }
258     }
259
260     /* Draw the borders of the selected slot */
261     if( p_sys->i_selected != -1 && !p_sys->b_blackslot )
262     {
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];
266
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++ )
275         {
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;
280         }
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 );
284     }
285
286     /* Draw the 'Shuffle' button if the puzzle is finished */
287     if( p_sys->b_finished )
288     {
289         plane_t *p_out = &p_outpic->p[Y_PLANE];
290         for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
291         {
292             for( int j = 0; j < SHUFFLE_WIDTH; j++ )
293             {
294                 if( shuffle_button[i][j] == '.' )
295                    p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
296             }
297         }
298     }
299
300     return CopyInfoAndRelease( p_outpic, p_pic );
301 }
302
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 )
305 {
306     filter_sys_t *p_sys = p_filter->p_sys;
307     const video_format_t  *p_fmt = &p_filter->fmt_in.video;
308
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;
313
314     /* */
315     const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
316
317     /* If the puzzle is finished, shuffle it if needed */
318     if( p_sys->b_finished )
319     {
320         if( b_clicked &&
321             p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
322         {
323             p_sys->b_change = true;
324             return VLC_EGENERIC;
325         }
326         else
327         {
328             /* This is the only case where we can forward the mouse */
329             *p_mouse = *p_new;
330             return VLC_SUCCESS;
331         }
332     }
333     if( !b_clicked )
334         return VLC_EGENERIC;
335
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;
340
341     if( p_sys->i_selected == -1 )
342     {
343         p_sys->i_selected = i_pos;
344     }
345     else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
346     {
347         p_sys->i_selected = -1;
348     }
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 )
353     {
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;
358
359         p_sys->i_selected = p_sys->b_blackslot ? i_pos : -1;
360         p_sys->b_finished = IsFinished( p_sys );
361     }
362     return VLC_EGENERIC;
363 }
364
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 )
371 {
372     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
373     filter_sys_t *p_sys = (filter_sys_t *)p_data;
374
375     vlc_mutex_lock( &p_sys->lock );
376     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
377     {
378         p_sys->change.i_rows = __MAX( 1, newval.i_int );
379     }
380     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
381     {
382         p_sys->change.i_cols = __MAX( 1, newval.i_int );
383     }
384     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
385     {
386         p_sys->change.b_blackslot = newval.b_bool;
387     }
388     p_sys->b_change = true;
389     vlc_mutex_unlock( &p_sys->lock );
390
391     return VLC_SUCCESS;
392 }
393
394 static bool IsFinished( filter_sys_t *p_sys )
395 {
396     for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
397     {
398         if( i != p_sys->pi_order[i] )
399             return false;
400     }
401     return true;
402 }
403
404 static bool IsValid( filter_sys_t *p_sys )
405 {
406     const int i_count = p_sys->i_cols * p_sys->i_rows;
407
408     if( !p_sys->b_blackslot )
409         return true;
410
411     int d = 0;
412     for( int i = 0; i < i_count; i++ )
413     {
414         if( p_sys->pi_order[i] == i_count - 1 )
415         {
416             d += i / p_sys->i_cols + 1;
417             continue;
418         }
419         for( int j = i+1; j < i_count; j++ )
420         {
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++;
425         }
426     }
427     return (d%2) == 0;
428 }
429
430 static void Shuffle( filter_sys_t *p_sys )
431 {
432     const int i_count = p_sys->i_cols * p_sys->i_rows;
433
434     free( p_sys->pi_order );
435
436     p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
437     do
438     {
439         for( int i = 0; i < i_count; i++ )
440             p_sys->pi_order[i] = -1;
441
442         for( int c = 0; c < i_count; )
443         {
444             int i = rand() % i_count;
445             if( p_sys->pi_order[i] == -1 )
446                 p_sys->pi_order[i] = c++;
447         }
448         p_sys->b_finished = IsFinished( p_sys );
449
450     } while( p_sys->b_finished || !IsValid( p_sys ) );
451
452     if( p_sys->b_blackslot )
453     {
454         for( int i = 0; i < i_count; i++ )
455         {
456             if( p_sys->pi_order[i] == i_count - 1 )
457             {
458                 p_sys->i_selected = i;
459                 break;
460             }
461         }
462     }
463     else
464     {
465         p_sys->i_selected = -1;
466     }
467 }
468