]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
anaglyph: remove spurious printf
[vlc] / modules / video_filter / puzzle.c
1 /*****************************************************************************
2  * puzzle.c : Puzzle game
3  *****************************************************************************
4  * Copyright (C) 2005-2009 VLC authors and VideoLAN
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 it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * 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 #include <vlc_rand.h>
37
38 #include "filter_picture.h"
39
40 /*****************************************************************************
41  * Module descriptor
42  *****************************************************************************/
43 #define ROWS_TEXT N_("Number of puzzle rows")
44 #define ROWS_LONGTEXT N_("Number of puzzle rows")
45 #define COLS_TEXT N_("Number of puzzle columns")
46 #define COLS_LONGTEXT N_("Number of puzzle columns")
47 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
48 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
49
50 #define CFG_PREFIX "puzzle-"
51
52 static int  Open ( vlc_object_t * );
53 static void Close( vlc_object_t * );
54
55 vlc_module_begin()
56     set_description( N_("Puzzle interactive game video filter") )
57     set_shortname( N_( "Puzzle" ))
58     set_capability( "video filter2", 0 )
59     set_category( CAT_VIDEO )
60     set_subcategory( SUBCAT_VIDEO_VFILTER )
61
62     add_integer_with_range( CFG_PREFIX "rows", 4, 2, 16,
63                             ROWS_TEXT, ROWS_LONGTEXT, false )
64     add_integer_with_range( CFG_PREFIX "cols", 4, 2, 16,
65                             COLS_TEXT, COLS_LONGTEXT, false )
66     add_bool( CFG_PREFIX "black-slot", false,
67               BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false )
68
69     set_callbacks( Open, Close )
70 vlc_module_end()
71
72
73 /*****************************************************************************
74  * Local prototypes
75  *****************************************************************************/
76 static const char *const ppsz_filter_options[] = {
77     "rows", "cols", "black-slot", NULL
78 };
79
80 static picture_t *Filter( filter_t *, picture_t * );
81 static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t *, const vlc_mouse_t * );
82
83 static bool IsFinished( filter_sys_t * );
84 static void Shuffle( filter_sys_t * );
85 static int PuzzleCallback( vlc_object_t *, char const *,
86                            vlc_value_t, vlc_value_t, void * );
87
88 struct filter_sys_t
89 {
90     /* */
91     int i_cols;
92     int i_rows;
93     bool b_blackslot;
94     int *pi_order;
95     int i_selected;
96     bool b_finished;
97
98     /* */
99     struct
100     {
101         atomic_flag b_uptodate;
102         atomic_bool b_blackslot;
103         atomic_uint i_cols;
104         atomic_uint i_rows;
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     atomic_init( &p_sys->change.i_rows,
154                  var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" ) );
155     atomic_init( &p_sys->change.i_cols,
156                  var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" ) );
157     atomic_init( &p_sys->change.b_blackslot,
158                var_CreateGetBoolCommand( p_filter, CFG_PREFIX "black-slot" ) );
159     p_sys->change.b_uptodate = ATOMIC_FLAG_INIT;
160
161     var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
162     var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
163     var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
164
165     p_filter->pf_video_filter = Filter;
166     p_filter->pf_video_mouse = Mouse;
167
168     return VLC_SUCCESS;
169 }
170
171 /**
172  * Close the filter
173  */
174 static void Close( vlc_object_t *p_this )
175 {
176     filter_t *p_filter = (filter_t *)p_this;
177     filter_sys_t *p_sys = p_filter->p_sys;
178
179     var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
180     var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
181     var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
182
183     free( p_sys->pi_order );
184
185     free( p_sys );
186 }
187
188 /**
189  * Filter a picture
190  */
191 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
192 {
193     filter_sys_t *p_sys = p_filter->p_sys;
194
195     picture_t *p_outpic = filter_NewPicture( p_filter );
196     if( !p_outpic )
197     {
198         picture_Release( p_pic );
199         return NULL;
200     }
201
202     /* */
203     if( !atomic_flag_test_and_set( &p_sys->change.b_uptodate ) )
204     {
205         p_sys->i_rows      = atomic_load( &p_sys->change.i_rows );
206         p_sys->i_cols      = atomic_load( &p_sys->change.i_cols );
207         p_sys->b_blackslot = atomic_load( &p_sys->change.b_blackslot );
208         Shuffle( p_sys );
209     }
210
211     /* */
212     const int i_rows = p_sys->i_rows;
213     const int i_cols = p_sys->i_cols;
214
215     /* Draw each piece of the puzzle at the right place */
216     for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
217     {
218         const plane_t *p_in = &p_pic->p[i_plane];
219         plane_t *p_out = &p_outpic->p[i_plane];
220
221         for( int i = 0; i < i_cols * i_rows; i++ )
222         {
223             int i_piece_height = p_out->i_visible_lines / i_rows;
224             int i_piece_width  = p_out->i_visible_pitch / i_cols;
225
226             int i_col = (i % i_cols) * i_piece_width;
227             int i_row = (i / i_cols) * i_piece_height;
228             int i_last_row = i_row + i_piece_height;
229
230             int i_ocol = (p_sys->pi_order[i] % i_cols) * i_piece_width;
231             int i_orow = (p_sys->pi_order[i] / i_cols) * i_piece_height;
232
233             if( p_sys->b_blackslot && !p_sys->b_finished && i == p_sys->i_selected )
234             {
235                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
236                 for( int r = i_row; r < i_last_row; r++ )
237                 {
238                     memset( p_out->p_pixels + r * p_out->i_pitch + i_col,
239                             color, i_piece_width );
240                 }
241             }
242             else
243             {
244                 for( int r = i_row, or = i_orow; r < i_last_row; r++, or++ )
245                 {
246                     memcpy( p_out->p_pixels + r * p_out->i_pitch + i_col,
247                             p_in->p_pixels + or * p_in->i_pitch  + i_ocol,
248                             i_piece_width );
249                 }
250             }
251
252             /* Draw the borders of the selected slot */
253             if( i_plane == 0 && !p_sys->b_blackslot && p_sys->i_selected == i )
254             {
255                 memset( p_out->p_pixels + i_row * p_out->i_pitch + i_col,
256                         0xff, i_piece_width );
257                 for( int r = i_row; r < i_last_row; r++ )
258                 {
259                     p_out->p_pixels[r * p_out->i_pitch + i_col + 0             + 0 ] = 0xff;
260                     p_out->p_pixels[r * p_out->i_pitch + i_col + i_piece_width - 1 ] = 0xff;
261                 }
262                 memset( p_out->p_pixels + (i_last_row - 1) * p_out->i_pitch + i_col,
263                         0xff, i_piece_width );
264             }
265         }
266     }
267
268     /* Draw the 'Shuffle' button if the puzzle is finished */
269     if( p_sys->b_finished )
270     {
271         plane_t *p_out = &p_outpic->p[Y_PLANE];
272         for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
273         {
274             for( int j = 0; j < SHUFFLE_WIDTH; j++ )
275             {
276                 if( shuffle_button[i][j] == '.' )
277                    p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
278             }
279         }
280     }
281
282     return CopyInfoAndRelease( p_outpic, p_pic );
283 }
284
285 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
286                   const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
287 {
288     filter_sys_t *p_sys = p_filter->p_sys;
289     const video_format_t  *p_fmt = &p_filter->fmt_in.video;
290
291     /* Only take events inside the puzzle erea */
292     if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
293         p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
294         return VLC_EGENERIC;
295
296     /* */
297     const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
298
299     /* If the puzzle is finished, shuffle it if needed */
300     if( p_sys->b_finished )
301     {
302         if( b_clicked &&
303             p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
304         {
305             atomic_flag_clear( &p_sys->change.b_uptodate );
306             return VLC_EGENERIC;
307         }
308         else
309         {
310             /* This is the only case where we can forward the mouse */
311             *p_mouse = *p_new;
312             return VLC_SUCCESS;
313         }
314     }
315     if( !b_clicked )
316         return VLC_EGENERIC;
317
318     /* */
319     const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
320     const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
321     const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
322
323     if( p_sys->i_selected == -1 )
324     {
325         p_sys->i_selected = i_pos;
326     }
327     else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
328     {
329         p_sys->i_selected = -1;
330     }
331     else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
332           || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
333           || p_sys->i_selected == i_pos + p_sys->i_cols
334           || p_sys->i_selected == i_pos - p_sys->i_cols )
335     {
336         /* Swap two pieces */
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         p_sys->i_selected = p_sys->b_blackslot ? i_pos : -1;
342         p_sys->b_finished = IsFinished( p_sys );
343     }
344     return VLC_EGENERIC;
345 }
346
347 /*****************************************************************************
348  * Misc stuff...
349  *****************************************************************************/
350 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
351                            vlc_value_t oldval, vlc_value_t newval,
352                            void *p_data )
353 {
354     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
355     filter_sys_t *p_sys = p_data;
356
357     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
358         atomic_store( &p_sys->change.i_rows, __MAX( 2, newval.i_int ) );
359     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
360         atomic_store( &p_sys->change.i_cols, __MAX( 2, newval.i_int ) );
361     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
362         atomic_store( &p_sys->change.b_blackslot, newval.b_bool );
363     atomic_flag_clear( &p_sys->change.b_uptodate );
364
365     return VLC_SUCCESS;
366 }
367
368 static bool IsFinished( filter_sys_t *p_sys )
369 {
370     for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
371     {
372         if( i != p_sys->pi_order[i] )
373             return false;
374     }
375     return true;
376 }
377
378 static bool IsValid( filter_sys_t *p_sys )
379 {
380     const int i_count = p_sys->i_cols * p_sys->i_rows;
381
382     if( !p_sys->b_blackslot )
383         return true;
384
385     int d = 0;
386     for( int i = 0; i < i_count; i++ )
387     {
388         if( p_sys->pi_order[i] == i_count - 1 )
389         {
390             d += i / p_sys->i_cols + 1;
391             continue;
392         }
393         for( int j = i+1; j < i_count; j++ )
394         {
395             if( p_sys->pi_order[j] == i_count - 1 )
396                 continue;
397             if( p_sys->pi_order[i] > p_sys->pi_order[j] )
398                 d++;
399         }
400     }
401     return (d%2) == 0;
402 }
403
404 static void Shuffle( filter_sys_t *p_sys )
405 {
406     const unsigned i_count = p_sys->i_cols * p_sys->i_rows;
407
408     free( p_sys->pi_order );
409
410     p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
411     do
412     {
413         for( unsigned i = 0; i < i_count; i++ )
414             p_sys->pi_order[i] = -1;
415
416         for( unsigned c = 0; c < i_count; )
417         {
418             unsigned i = ((unsigned)vlc_mrand48()) % i_count;
419             if( p_sys->pi_order[i] == -1 )
420                 p_sys->pi_order[i] = c++;
421         }
422         p_sys->b_finished = IsFinished( p_sys );
423
424     } while( p_sys->b_finished || !IsValid( p_sys ) );
425
426     if( p_sys->b_blackslot )
427     {
428         for( unsigned i = 0; i < i_count; i++ )
429         {
430             if( p_sys->pi_order[i] == (int)i_count - 1 )
431             {
432                 p_sys->i_selected = i;
433                 break;
434             }
435         }
436     }
437     else
438     {
439         p_sys->i_selected = -1;
440     }
441 }