]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
Use vlc_memset/vlc_memcpy
[vlc] / modules / video_filter / puzzle.c
1 /*****************************************************************************
2  * puzzle.c : Puzzle game
3  *****************************************************************************
4  * Copyright (C) 2005-2006 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
32 #include <vlc/vlc.h>
33 #include <vlc_vout.h>
34
35 #include <math.h>
36
37 #include "filter_common.h"
38 #include "vlc_image.h"
39 #include "vlc_input.h"
40 #include "vlc_playlist.h"
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static int  Create    ( vlc_object_t * );
46 static void Destroy   ( vlc_object_t * );
47
48 static int  Init      ( vout_thread_t * );
49 static void End       ( vout_thread_t * );
50 static void Render    ( vout_thread_t *, picture_t * );
51
52 static int  SendEvents   ( vlc_object_t *, char const *,
53                            vlc_value_t, vlc_value_t, void * );
54 static int  MouseEvent   ( vlc_object_t *, char const *,
55                            vlc_value_t, vlc_value_t, void * );
56
57 static int PuzzleCallback( vlc_object_t *, char const *,
58                            vlc_value_t, vlc_value_t, void * );
59
60 /*****************************************************************************
61  * Module descriptor
62  *****************************************************************************/
63
64 #define ROWS_TEXT N_("Number of puzzle rows")
65 #define ROWS_LONGTEXT N_("Number of puzzle rows")
66 #define COLS_TEXT N_("Number of puzzle columns")
67 #define COLS_LONGTEXT N_("Number of puzzle columns")
68 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
69 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
70
71 #define CFG_PREFIX "puzzle-"
72
73 vlc_module_begin();
74     set_description( _("Puzzle interactive game video filter") );
75     set_shortname( _( "Puzzle" ));
76     set_capability( "video filter", 0 );
77     set_category( CAT_VIDEO );
78     set_subcategory( SUBCAT_VIDEO_VFILTER );
79
80     add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
81                             ROWS_TEXT, ROWS_LONGTEXT, false );
82     add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
83                             COLS_TEXT, COLS_LONGTEXT, false );
84     add_bool( CFG_PREFIX "black-slot", 0, NULL,
85               BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false );
86
87     set_callbacks( Create, Destroy );
88 vlc_module_end();
89
90 static const char *ppsz_filter_options[] = {
91     "rows", "cols", "black-slot", NULL
92 };
93
94 /*****************************************************************************
95  * vout_sys_t: Magnify video output method descriptor
96  *****************************************************************************/
97 struct vout_sys_t
98 {
99     vout_thread_t *p_vout;
100
101     image_handler_t *p_image;
102
103     int i_cols;
104     int i_rows;
105     int *pi_order;
106     int i_selected;
107     bool b_finished;
108
109     bool b_blackslot;
110 };
111
112 /*****************************************************************************
113  * Control: control facility for the vout (forwards to child vout)
114  *****************************************************************************/
115 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
116 {
117     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
118 }
119
120 /*****************************************************************************
121  * Misc stuff...
122  *****************************************************************************/
123 static bool finished( vout_sys_t *p_sys )
124 {
125     int i;
126     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
127     {
128         if( i != p_sys->pi_order[i] ) return false;
129     }
130     return true;
131 }
132 static bool is_valid( vout_sys_t *p_sys )
133 {
134     int i, j, d=0;
135     if( p_sys->b_blackslot == false ) return true;
136     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
137     {
138         if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
139         {
140             d += i / p_sys->i_cols + 1;
141             continue;
142         }
143         for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
144         {
145             if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
146                 continue;
147             if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
148         }
149     }
150     if( d%2!=0 ) return false;
151     else return true;
152 }
153 static void shuffle( vout_sys_t *p_sys )
154 {
155     int i, c;
156     free( p_sys->pi_order );
157     p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
158     do
159     {
160         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
161         {
162             p_sys->pi_order[i] = -1;
163         }
164         i = 0;
165         for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
166         {
167             i = rand()%( p_sys->i_cols * p_sys->i_rows );
168             if( p_sys->pi_order[i] == -1 )
169             {
170                 p_sys->pi_order[i] = c;
171                 c++;
172             }
173         }
174         p_sys->b_finished = finished( p_sys );
175     } while(    p_sys->b_finished == true
176              || is_valid( p_sys ) == false );
177
178     if( p_sys->b_blackslot == true )
179     {
180         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
181         {
182             if( p_sys->pi_order[i] ==
183                 p_sys->i_cols * p_sys->i_rows - 1 )
184             {
185                 p_sys->i_selected = i;
186                 break;
187             }
188         }
189     }
190     else
191     {
192         p_sys->i_selected = -1;
193     }
194 }
195
196 /*****************************************************************************
197  * Create: allocates Magnify video thread output method
198  *****************************************************************************/
199 static int Create( vlc_object_t *p_this )
200 {
201     vout_thread_t *p_vout = (vout_thread_t *)p_this;
202
203     /* Allocate structure */
204     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
205     if( p_vout->p_sys == NULL )
206     {
207         msg_Err( p_vout, "out of memory" );
208         return VLC_ENOMEM;
209     }
210
211     p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
212
213     config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
214                        p_vout->p_cfg );
215
216     p_vout->p_sys->i_rows =
217         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
218     p_vout->p_sys->i_cols =
219         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
220     p_vout->p_sys->b_blackslot =
221         var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
222     var_AddCallback( p_vout, CFG_PREFIX "rows",
223                      PuzzleCallback, p_vout->p_sys );
224     var_AddCallback( p_vout, CFG_PREFIX "cols",
225                      PuzzleCallback, p_vout->p_sys );
226     var_AddCallback( p_vout, CFG_PREFIX "black-slot",
227                      PuzzleCallback, p_vout->p_sys );
228
229     p_vout->p_sys->pi_order = NULL;
230     shuffle( p_vout->p_sys );
231
232     p_vout->pf_init = Init;
233     p_vout->pf_end = End;
234     p_vout->pf_manage = NULL;
235     p_vout->pf_render = Render;
236     p_vout->pf_display = NULL;
237     p_vout->pf_control = Control;
238
239     return VLC_SUCCESS;
240 }
241
242 /*****************************************************************************
243  * Init: initialize Magnify video thread output method
244  *****************************************************************************/
245 static int Init( vout_thread_t *p_vout )
246 {
247     int i_index;
248     picture_t *p_pic;
249     video_format_t fmt;
250     memset( &fmt, 0, sizeof( video_format_t ) );
251
252     I_OUTPUTPICTURES = 0;
253
254     /* Initialize the output structure */
255     p_vout->output.i_chroma = p_vout->render.i_chroma;
256     p_vout->output.i_width  = p_vout->render.i_width;
257     p_vout->output.i_height = p_vout->render.i_height;
258     p_vout->output.i_aspect = p_vout->render.i_aspect;
259
260     p_vout->fmt_out = p_vout->fmt_in;
261     fmt = p_vout->fmt_out;
262
263     /* Try to open the real video output */
264     msg_Dbg( p_vout, "spawning the real video output" );
265
266     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
267
268     /* Everything failed */
269     if( p_vout->p_sys->p_vout == NULL )
270     {
271         msg_Err( p_vout, "cannot open vout, aborting" );
272         return VLC_EGENERIC;
273     }
274
275     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
276     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
277     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
278                      MouseEvent, p_vout);
279
280     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
281     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
282     ADD_PARENT_CALLBACKS( SendEventsToChild );
283
284     return VLC_SUCCESS;
285 }
286
287 /*****************************************************************************
288  * End: terminate Magnify video thread output method
289  *****************************************************************************/
290 static void End( vout_thread_t *p_vout )
291 {
292     int i_index;
293
294     /* Free the fake output buffers we allocated */
295     for( i_index = I_OUTPUTPICTURES ; i_index ; )
296     {
297         i_index--;
298         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
299     }
300
301     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
302     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
303     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
304 }
305
306 #define SHUFFLE_WIDTH 81
307 #define SHUFFLE_HEIGHT 13
308 static const char *shuffle_button[] =
309 {
310 ".................................................................................",
311 "..............  ............................   ........   ......  ...............",
312 "..............  ...........................  .........  ........  ...............",
313 "..............  ...........................  .........  ........  ...............",
314 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
315 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
316 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
317 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
318 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
319 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
320 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
321 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
322 "................................................................................."};
323
324
325 /*****************************************************************************
326  * Destroy: destroy Magnify video thread output method
327  *****************************************************************************/
328 static void Destroy( vlc_object_t *p_this )
329 {
330     vout_thread_t *p_vout = (vout_thread_t *)p_this;
331
332     if( p_vout->p_sys->p_vout )
333     {
334         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
335         vlc_object_detach( p_vout->p_sys->p_vout );
336         vout_Destroy( p_vout->p_sys->p_vout );
337     }
338
339     image_HandlerDelete( p_vout->p_sys->p_image );
340     free( p_vout->p_sys->pi_order );
341
342     DEL_PARENT_CALLBACKS( SendEventsToChild );
343
344     free( p_vout->p_sys );
345 }
346
347 /*****************************************************************************
348  * Render: displays previously rendered output
349  *****************************************************************************/
350 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
351 {
352     picture_t *p_outpic;
353
354     //video_format_t fmt_out;
355     // memset( &fmt_out, 0, sizeof(video_format_t) );
356     //picture_t *p_converted;
357
358     int i_plane;
359
360     int i_rows = p_vout->p_sys->i_rows;
361     int i_cols = p_vout->p_sys->i_cols;
362
363     /* This is a new frame. Get a structure from the video_output. */
364     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
365               == NULL )
366     {
367         if( p_vout->b_die || p_vout->b_error )
368         {
369             return;
370         }
371         msleep( VOUT_OUTMEM_SLEEP );
372     }
373
374     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
375
376     for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
377     {
378         plane_t *p_in = p_pic->p+i_plane;
379         plane_t *p_out = p_outpic->p+i_plane;
380         int i_pitch = p_in->i_pitch;
381         int i;
382
383         for( i = 0; i < i_cols * i_rows; i++ )
384         {
385             int i_col = i % i_cols;
386             int i_row = i / i_cols;
387             int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
388             int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
389             int i_last_row = i_row + 1;
390             i_orow *= p_in->i_lines / i_rows;
391             i_row *= p_in->i_lines / i_rows;
392             i_last_row *= p_in->i_lines / i_rows;
393
394             if( p_vout->p_sys->b_blackslot == true
395                 && p_vout->p_sys->b_finished == false
396                 && i == p_vout->p_sys->i_selected )
397             {
398                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
399                 for( ; i_row < i_last_row; i_row++, i_orow++ )
400                 {
401                     vlc_memset( p_out->p_pixels + i_row * i_pitch
402                                                + i_col * i_pitch / i_cols,
403                                color, i_pitch / i_cols );
404                 }
405             }
406             else
407             {
408                 for( ; i_row < i_last_row; i_row++, i_orow++ )
409                 {
410                     vlc_memcpy( p_out->p_pixels + i_row * i_pitch
411                                                + i_col * i_pitch / i_cols,
412                                p_in->p_pixels + i_orow * i_pitch
413                                               + i_ocol * i_pitch / i_cols,
414                                i_pitch / i_cols );
415                 }
416             }
417         }
418     }
419
420     if(    p_vout->p_sys->i_selected != -1
421         && p_vout->p_sys->b_blackslot == false )
422     {
423         plane_t *p_in = p_pic->p+Y_PLANE;
424         plane_t *p_out = p_outpic->p+Y_PLANE;
425         int i_pitch = p_in->i_pitch;
426         int i_col = p_vout->p_sys->i_selected % i_cols;
427         int i_row = p_vout->p_sys->i_selected / i_cols;
428         int i_last_row = i_row + 1;
429         i_row *= p_in->i_lines / i_rows;
430         i_last_row *= p_in->i_lines / i_rows;
431         vlc_memset( p_out->p_pixels + i_row * i_pitch
432                                    + i_col * i_pitch / i_cols,
433                    0xff, i_pitch / i_cols );
434         for( ; i_row < i_last_row; i_row++ )
435         {
436             p_out->p_pixels[   i_row * i_pitch
437                              + i_col * i_pitch / i_cols ] = 0xff;
438             p_out->p_pixels[ i_row * i_pitch
439                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
440         }
441         i_row--;
442         vlc_memset( p_out->p_pixels + i_row * i_pitch
443                                    + i_col * i_pitch / i_cols,
444                    0xff, i_pitch / i_cols );
445     }
446
447     if( p_vout->p_sys->b_finished == true )
448     {
449         int i, j;
450         plane_t *p_out = p_outpic->p+Y_PLANE;
451         int i_pitch = p_out->i_pitch;
452         for( i = 0; i < SHUFFLE_HEIGHT; i++ )
453         {
454             for( j = 0; j < SHUFFLE_WIDTH; j++ )
455             {
456                 if( shuffle_button[i][j] == '.' )
457                    p_out->p_pixels[ i * i_pitch + j ] = 0xff;
458             }
459         }
460     }
461
462     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
463 }
464
465 /*****************************************************************************
466  * SendEvents: forward mouse and keyboard events to the parent p_vout
467  *****************************************************************************/
468 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
469                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
470 {
471     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
472
473     var_Set( (vlc_object_t *)p_data, psz_var, newval );
474
475     return VLC_SUCCESS;
476 }
477
478 /*****************************************************************************
479  * SendEventsToChild: forward events to the child/children vout
480  *****************************************************************************/
481 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
482                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
483 {
484     VLC_UNUSED(p_data); VLC_UNUSED(oldval);
485     vout_thread_t *p_vout = (vout_thread_t *)p_this;
486     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
487     return VLC_SUCCESS;
488 }
489
490 /*****************************************************************************
491  * MouseEvent: callback for mouse events
492  *****************************************************************************/
493 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
494                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
495 {
496     VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(newval);
497     vout_thread_t *p_vout = (vout_thread_t*)p_data;
498     int i_x, i_y;
499     int i_v;
500
501 #define MOUSE_DOWN    1
502 #define MOUSE_CLICKED 2
503 #define MOUSE_MOVE_X  4
504 #define MOUSE_MOVE_Y  8
505 #define MOUSE_MOVE    12
506     uint8_t mouse= 0;
507
508     int v_h = p_vout->output.i_height;
509     int v_w = p_vout->output.i_width;
510     int i_pos;
511
512     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
513     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
514     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
515
516     i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
517     if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
518     i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
519     i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
520
521     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
522         return VLC_SUCCESS;
523
524     if( mouse & MOUSE_CLICKED )
525     {
526         i_pos = p_vout->p_sys->i_cols * ( ( p_vout->p_sys->i_rows * i_y ) / v_h ) + (p_vout->p_sys->i_cols * i_x ) / v_w;
527         if( p_vout->p_sys->b_finished == true
528             && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
529         {
530             shuffle( p_vout->p_sys );
531         }
532         else if( p_vout->p_sys->i_selected == -1 )
533         {
534             p_vout->p_sys->i_selected = i_pos;
535         }
536         else if( p_vout->p_sys->i_selected == i_pos
537                  && p_vout->p_sys->b_blackslot == false )
538         {
539             p_vout->p_sys->i_selected = -1;
540         }
541         else if(    ( p_vout->p_sys->i_selected == i_pos + 1
542                       && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
543                  || ( p_vout->p_sys->i_selected == i_pos - 1
544                       && i_pos % p_vout->p_sys->i_cols != 0 )
545                  || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
546                  || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
547         {
548             int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
549             p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
550                 p_vout->p_sys->pi_order[ i_pos ];
551             p_vout->p_sys->pi_order[ i_pos ] = a;
552             if( p_vout->p_sys->b_blackslot == true )
553                 p_vout->p_sys->i_selected = i_pos;
554             else
555                 p_vout->p_sys->i_selected = -1;
556
557             p_vout->p_sys->b_finished = finished( p_vout->p_sys );
558         }
559     }
560     return VLC_SUCCESS;
561 }
562
563 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
564                            vlc_value_t oldval, vlc_value_t newval,
565                            void *p_data )
566 {
567     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
568     vout_sys_t *p_sys = (vout_sys_t *)p_data;
569     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
570     {
571         p_sys->i_rows = __MAX( 1, newval.i_int );
572     }
573     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
574     {
575         p_sys->i_cols = __MAX( 1, newval.i_int );
576     }
577     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
578     {
579         p_sys->b_blackslot = newval.b_bool;
580     }
581     shuffle( p_sys );
582     return VLC_SUCCESS;
583 }