]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
video_filter_puzzle: remove unnedeed structure (it does not change anything to the...
[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 };
101
102 #define SHUFFLE_WIDTH 81
103 #define SHUFFLE_HEIGHT 13
104 static const char *shuffle_button[] =
105 {
106 ".................................................................................",
107 "..............  ............................   ........   ......  ...............",
108 "..............  ...........................  .........  ........  ...............",
109 "..............  ...........................  .........  ........  ...............",
110 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
111 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
112 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
113 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
114 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
115 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
116 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
117 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
118 "................................................................................."
119 };
120
121
122 /**
123  * Open the filter
124  */
125 static int Open( vlc_object_t *p_this )
126 {
127     filter_t *p_filter = (filter_t *)p_this;
128     filter_sys_t *p_sys;
129
130     /* */
131     if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
132     {
133         msg_Err( p_filter, "Input and output format does not match" );
134         return VLC_EGENERIC;
135     }
136
137     /* Allocate structure */
138     p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
139     if( !p_sys )
140         return VLC_ENOMEM;
141
142     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
143                        p_filter->p_cfg );
144
145     p_sys->pi_order = NULL;
146
147     vlc_mutex_init( &p_sys->lock );
148     p_sys->i_rows = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" );
149     p_sys->i_cols = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" );
150     p_sys->b_blackslot = var_CreateGetBoolCommand( p_filter, CFG_PREFIX "black-slot" );
151     p_sys->b_change = true;
152
153     var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
154     var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
155     var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
156
157     p_filter->pf_video_filter = Filter;
158     p_filter->pf_mouse = Mouse;
159
160     return VLC_SUCCESS;
161 }
162
163 /**
164  * Close the filter
165  */
166 static void Close( vlc_object_t *p_this )
167 {
168     filter_t *p_filter = (filter_t *)p_this;
169     filter_sys_t *p_sys = p_filter->p_sys;
170
171     var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
172     var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
173     var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
174
175     vlc_mutex_destroy( &p_sys->lock );
176     free( p_sys->pi_order );
177
178     free( p_sys );
179 }
180
181 /**
182  * Filter a picture
183  */
184 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
185 {
186     filter_sys_t *p_sys = p_filter->p_sys;
187
188     picture_t *p_outpic = filter_NewPicture( p_filter );
189     if( !p_outpic )
190     {
191         picture_Release( p_pic );
192         return NULL;
193     }
194
195     /* */
196     vlc_mutex_lock( &p_sys->lock );
197     if( p_sys->b_change )
198     {
199         p_sys->b_change = false;
200         Shuffle( p_sys );
201     }
202     vlc_mutex_unlock( &p_sys->lock );
203
204     /* */
205     const int i_rows = p_sys->i_rows;
206     const int i_cols = p_sys->i_cols;
207
208     /* */
209     for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
210     {
211         const plane_t *p_in = &p_pic->p[i_plane];
212         const int i_pitch = p_in->i_pitch;
213         plane_t *p_out = &p_outpic->p[i_plane];
214
215         for( int i = 0; i < i_cols * i_rows; i++ )
216         {
217             int i_col = i % i_cols;
218             int i_row = i / i_cols;
219             int i_ocol = p_sys->pi_order[i] % i_cols;
220             int i_orow = p_sys->pi_order[i] / i_cols;
221             int i_last_row = i_row + 1;
222             i_orow *= p_in->i_lines / i_rows;
223             i_row *= p_in->i_lines / i_rows;
224             i_last_row *= p_in->i_lines / i_rows;
225
226             if( p_sys->b_blackslot && p_sys->b_finished && i == p_sys->i_selected )
227             {
228                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
229                 for( ; i_row < i_last_row; i_row++, i_orow++ )
230                 {
231                     memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
232                             color, i_pitch / i_cols );
233                 }
234             }
235             else
236             {
237                 for( ; i_row < i_last_row; i_row++, i_orow++ )
238                 {
239                     memcpy( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
240                             p_in->p_pixels + i_orow * i_pitch + i_ocol * i_pitch / i_cols,
241                             i_pitch / i_cols );
242                 }
243             }
244         }
245     }
246
247     if( p_sys->i_selected != -1 && !p_sys->b_blackslot )
248     {
249         const plane_t *p_in = &p_pic->p[Y_PLANE];
250         const int i_pitch = p_in->i_pitch;
251         plane_t *p_out = &p_outpic->p[Y_PLANE];
252
253         int i_col = p_sys->i_selected % i_cols;
254         int i_row = p_sys->i_selected / i_cols;
255         int i_last_row = i_row + 1;
256         i_row *= p_in->i_lines / i_rows;
257         i_last_row *= p_in->i_lines / i_rows;
258         memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
259                 0xff, i_pitch / i_cols );
260         for( ; i_row < i_last_row; i_row++ )
261         {
262             p_out->p_pixels[   i_row * i_pitch
263                              + i_col * i_pitch / i_cols ] = 0xff;
264             p_out->p_pixels[ i_row * i_pitch
265                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
266         }
267         i_row--;
268         memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
269                 0xff, i_pitch / i_cols );
270     }
271
272     if( p_sys->b_finished )
273     {
274         plane_t *p_out = &p_outpic->p[Y_PLANE];
275         for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
276         {
277             for( int j = 0; j < SHUFFLE_WIDTH; j++ )
278             {
279                 if( shuffle_button[i][j] == '.' )
280                    p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
281             }
282         }
283     }
284
285     return CopyInfoAndRelease( p_outpic, p_pic );
286 }
287
288 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
289                   const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
290 {
291     filter_sys_t *p_sys = p_filter->p_sys;
292     const video_format_t  *p_fmt = &p_filter->fmt_in.video;
293
294     if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
295         p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
296         return VLC_EGENERIC;
297
298     /* */
299     const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
300
301     if( p_sys->b_finished )
302     {
303         if( b_clicked &&
304             p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
305         {
306             p_sys->b_change = true;
307             return VLC_EGENERIC;
308         }
309         else
310         {
311             /* This is the only case where we can forward the mouse */
312             *p_mouse = *p_new;
313             return VLC_SUCCESS;
314         }
315     }
316     if( !b_clicked )
317         return VLC_EGENERIC;
318
319     /* */
320     const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
321     const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
322     const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
323
324     if( p_sys->i_selected == -1 )
325     {
326         p_sys->i_selected = i_pos;
327     }
328     else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
329     {
330         p_sys->i_selected = -1;
331     }
332     else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
333           || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
334           || p_sys->i_selected == i_pos + p_sys->i_cols
335           || p_sys->i_selected == i_pos - p_sys->i_cols )
336     {
337         int a = p_sys->pi_order[ p_sys->i_selected ];
338         p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ i_pos ];
339         p_sys->pi_order[ i_pos ] = a;
340
341         if( p_sys->b_blackslot )
342             p_sys->i_selected = i_pos;
343         else
344             p_sys->i_selected = -1;
345
346         p_sys->b_finished = IsFinished( p_sys );
347     }
348     return VLC_EGENERIC;
349 }
350
351 /*****************************************************************************
352  * Misc stuff...
353  *****************************************************************************/
354 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
355                            vlc_value_t oldval, vlc_value_t newval,
356                            void *p_data )
357 {
358     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
359     filter_sys_t *p_sys = (filter_sys_t *)p_data;
360
361     vlc_mutex_lock( &p_sys->lock );
362     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
363     {
364         p_sys->i_rows = __MAX( 1, newval.i_int );
365     }
366     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
367     {
368         p_sys->i_cols = __MAX( 1, newval.i_int );
369     }
370     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
371     {
372         p_sys->b_blackslot = newval.b_bool;
373     }
374     p_sys->b_change = true;
375     vlc_mutex_unlock( &p_sys->lock );
376
377     return VLC_SUCCESS;
378 }
379
380 static bool IsFinished( filter_sys_t *p_sys )
381 {
382     for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
383     {
384         if( i != p_sys->pi_order[i] )
385             return false;
386     }
387     return true;
388 }
389
390 static bool IsValid( filter_sys_t *p_sys )
391 {
392     const int i_count = p_sys->i_cols * p_sys->i_rows;
393
394     if( p_sys->b_blackslot )
395         return true;
396
397     int d = 0;
398     for( int i = 0; i < i_count; i++ )
399     {
400         if( p_sys->pi_order[i] == i_count - 1 )
401         {
402             d += i / p_sys->i_cols + 1;
403             continue;
404         }
405         for( int j = i+1; j < i_count; j++ )
406         {
407             if( p_sys->pi_order[j] == i_count - 1 )
408                 continue;
409             if( p_sys->pi_order[i] > p_sys->pi_order[j] )
410                 d++;
411         }
412     }
413     return (d%2) == 0;
414 }
415
416 static void Shuffle( filter_sys_t *p_sys )
417 {
418     const int i_count = p_sys->i_cols * p_sys->i_rows;
419
420     free( p_sys->pi_order );
421
422     p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
423     do
424     {
425         for( int i = 0; i < i_count; i++ )
426             p_sys->pi_order[i] = -1;
427
428         for( int c = 0; c < i_count; )
429         {
430             int i = rand() % i_count;
431             if( p_sys->pi_order[i] == -1 )
432                 p_sys->pi_order[i] = c++;
433         }
434         p_sys->b_finished = IsFinished( p_sys );
435
436     } while( p_sys->b_finished || IsValid( p_sys ) );
437
438     if( p_sys->b_blackslot )
439     {
440         for( int i = 0; i < i_count; i++ )
441         {
442             if( p_sys->pi_order[i] == i_count - 1 )
443             {
444                 p_sys->i_selected = i;
445                 break;
446             }
447         }
448     }
449     else
450     {
451         p_sys->i_selected = -1;
452     }
453 }
454