1 /*****************************************************************************
2 * rotate.c : video rotation filter
3 *****************************************************************************
4 * Copyright (C) 2000-2008 the VideoLAN team
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 /*****************************************************************************
26 *****************************************************************************/
32 #include <math.h> /* sin(), cos() */
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include "filter_picture.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Create ( vlc_object_t
* );
44 static void Destroy ( vlc_object_t
* );
46 static picture_t
*Filter( filter_t
*, picture_t
* );
47 static picture_t
*FilterPacked( filter_t
*, picture_t
* );
49 static int RotateCallback( vlc_object_t
*p_this
, char const *psz_var
,
50 vlc_value_t oldval
, vlc_value_t newval
,
53 static int PreciseRotateCallback( vlc_object_t
*p_this
, char const *psz_var
,
54 vlc_value_t oldval
, vlc_value_t newval
,
57 #define ANGLE_TEXT N_("Angle in degrees")
58 #define ANGLE_LONGTEXT N_("Angle in degrees (0 to 359)")
60 #define FILTER_PREFIX "rotate-"
62 /*****************************************************************************
64 *****************************************************************************/
66 set_description( N_("Rotate video filter") )
67 set_shortname( N_( "Rotate" ))
68 set_capability( "video filter2", 0 )
69 set_category( CAT_VIDEO
)
70 set_subcategory( SUBCAT_VIDEO_VFILTER
)
72 add_integer_with_range( FILTER_PREFIX
"angle", 30, 0, 359,
73 ANGLE_TEXT
, ANGLE_LONGTEXT
, false )
75 add_shortcut( "rotate" )
76 set_callbacks( Create
, Destroy
)
79 static const char *const ppsz_filter_options
[] = {
83 /*****************************************************************************
85 *****************************************************************************/
93 static inline void cache_trigo( int i_angle
, int *i_sin
, int *i_cos
)
95 const double f_angle
= (((double)i_angle
)*M_PI
)/1800.;
96 *i_sin
= (int)(sin( f_angle
)*4096.);
97 *i_cos
= (int)(cos( f_angle
)*4096.);
100 /*****************************************************************************
101 * Create: allocates Distort video filter
102 *****************************************************************************/
103 static int Create( vlc_object_t
*p_this
)
105 filter_t
*p_filter
= (filter_t
*)p_this
;
108 if( p_filter
->fmt_in
.video
.i_chroma
!= p_filter
->fmt_out
.video
.i_chroma
)
110 msg_Err( p_filter
, "Input and output chromas don't match" );
114 switch( p_filter
->fmt_in
.video
.i_chroma
)
117 p_filter
->pf_video_filter
= Filter
;
121 p_filter
->pf_video_filter
= FilterPacked
;
125 msg_Err( p_filter
, "Unsupported input chroma (%4.4s)",
126 (char*)&(p_filter
->fmt_in
.video
.i_chroma
) );
130 /* Allocate structure */
131 p_filter
->p_sys
= malloc( sizeof( filter_sys_t
) );
132 if( p_filter
->p_sys
== NULL
)
134 p_sys
= p_filter
->p_sys
;
136 config_ChainParse( p_filter
, FILTER_PREFIX
, ppsz_filter_options
,
139 int i_angle
= var_CreateGetIntegerCommand( p_filter
,
140 FILTER_PREFIX
"angle" ) * 10;
141 cache_trigo( i_angle
, &p_sys
->i_sin
, &p_sys
->i_cos
);
142 var_Create( p_filter
, FILTER_PREFIX
"deciangle",
143 VLC_VAR_INTEGER
|VLC_VAR_ISCOMMAND
);
144 vlc_spin_init( &p_sys
->lock
);
145 var_AddCallback( p_filter
, FILTER_PREFIX
"angle", RotateCallback
, p_sys
);
146 var_AddCallback( p_filter
, FILTER_PREFIX
"deciangle",
147 PreciseRotateCallback
, p_sys
);
152 /*****************************************************************************
153 * Destroy: destroy Distort filter
154 *****************************************************************************/
155 static void Destroy( vlc_object_t
*p_this
)
157 filter_t
*p_filter
= (filter_t
*)p_this
;
159 var_DelCallback( p_filter
, FILTER_PREFIX
"angle", RotateCallback
, p_filter
->p_sys
);
160 var_DelCallback( p_filter
, FILTER_PREFIX
"deciangle",
161 PreciseRotateCallback
, p_filter
->p_sys
);
162 vlc_spin_destroy( &p_filter
->p_sys
->lock
);
163 free( p_filter
->p_sys
);
166 /*****************************************************************************
168 *****************************************************************************/
169 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
172 filter_sys_t
*p_sys
= p_filter
->p_sys
;
174 if( !p_pic
) return NULL
;
176 p_outpic
= filter_NewPicture( p_filter
);
179 picture_Release( p_pic
);
183 vlc_spin_lock( &p_sys
->lock
);
184 const int i_sin
= p_sys
->i_sin
;
185 const int i_cos
= p_sys
->i_cos
;
186 vlc_spin_unlock( &p_sys
->lock
);
188 for( int i_plane
= 0 ; i_plane
< p_pic
->i_planes
; i_plane
++ )
190 plane_t
*p_srcp
= &p_pic
->p
[i_plane
];
191 plane_t
*p_dstp
= &p_outpic
->p
[i_plane
];
193 const int i_visible_lines
= p_srcp
->i_visible_lines
;
194 const int i_visible_pitch
= p_srcp
->i_visible_pitch
;
196 const int i_aspect
= __MAX( 1, ( i_visible_lines
* p_pic
->p
[Y_PLANE
].i_visible_pitch
) / ( p_pic
->p
[Y_PLANE
].i_visible_lines
* i_visible_pitch
));
197 /* = 2 for U and V planes in YUV 4:2:2, = 1 otherwise */
199 const int i_line_center
= i_visible_lines
>>1;
200 const int i_col_center
= i_visible_pitch
>>1;
202 const uint8_t black_pixel
= ( i_plane
== Y_PLANE
) ? 0x00 : 0x80;
204 const int i_line_next
= i_cos
/ i_aspect
-i_sin
*i_visible_pitch
;
205 const int i_col_next
= -i_sin
/ i_aspect
-i_cos
*i_visible_pitch
;
206 int i_line_orig0
= ( - i_cos
* i_line_center
/ i_aspect
207 - i_sin
* i_col_center
+ (1<<11) );
208 int i_col_orig0
= i_sin
* i_line_center
/ i_aspect
209 - i_cos
* i_col_center
+ (1<<11);
210 for( int y
= 0; y
< i_visible_lines
; y
++)
212 uint8_t *p_out
= &p_dstp
->p_pixels
[y
* p_dstp
->i_pitch
];
214 for( int x
= 0; x
< i_visible_pitch
; x
++, p_out
++ )
216 const int i_line_orig
= (i_line_orig0
>>12)*i_aspect
+ i_line_center
;
217 const int i_col_orig
= (i_col_orig0
>>12) + i_col_center
;
218 const uint8_t *p_orig_offset
= &p_srcp
->p_pixels
[i_line_orig
* p_srcp
->i_pitch
+ i_col_orig
];
219 const uint8_t i_line_percent
= (i_line_orig0
>>4) & 255;
220 const uint8_t i_col_percent
= (i_col_orig0
>>4) & 255;
222 if( -1 <= i_line_orig
&& i_line_orig
< i_visible_lines
223 && -1 <= i_col_orig
&& i_col_orig
< i_visible_pitch
)
228 if( ( i_col_orig
> i_visible_pitch
/2 ) )
231 uint8_t i_curpix
= black_pixel
;
232 uint8_t i_colpix
= black_pixel
;
233 uint8_t i_linpix
= black_pixel
;
234 uint8_t i_nexpix
= black_pixel
;
235 if( ( 0 <= i_line_orig
) && ( 0 <= i_col_orig
) )
236 i_curpix
= *p_orig_offset
;
239 if( ( i_col_orig
< i_visible_pitch
- 1)
240 && ( i_line_orig
>= 0 ) )
241 i_colpix
= *p_orig_offset
;
243 p_orig_offset
+= p_srcp
->i_pitch
;
244 if( ( i_line_orig
< i_visible_lines
- 1)
245 && ( i_col_orig
< i_visible_pitch
- 1) )
246 i_nexpix
= *p_orig_offset
;
249 if( ( i_line_orig
< i_visible_lines
- 1)
250 && ( i_col_orig
>= 0 ) )
251 i_linpix
= *p_orig_offset
;
253 unsigned int temp
= 0;
255 (256 - i_line_percent
) * ( 256 - i_col_percent
);
257 i_line_percent
* (256 - i_col_percent
);
259 ( i_col_percent
) * ( i_line_percent
);
261 i_col_percent
* (256 - i_line_percent
);
265 else if (i_col_orig
== i_visible_pitch
/2 )
266 { *p_out
= black_pixel
;
269 *p_out
= *p_orig_offset
;
275 *p_out
= black_pixel
;
278 i_line_orig0
+= i_sin
;
279 i_col_orig0
+= i_cos
;
282 i_line_orig0
+= i_line_next
;
283 i_col_orig0
+= i_col_next
;
287 return CopyInfoAndRelease( p_outpic
, p_pic
);
290 /*****************************************************************************
292 *****************************************************************************/
293 static picture_t
*FilterPacked( filter_t
*p_filter
, picture_t
*p_pic
)
296 filter_sys_t
*p_sys
= p_filter
->p_sys
;
298 if( !p_pic
) return NULL
;
300 int i_u_offset
, i_v_offset
, i_y_offset
;
302 if( GetPackedYuvOffsets( p_pic
->format
.i_chroma
, &i_y_offset
,
303 &i_u_offset
, &i_v_offset
) != VLC_SUCCESS
)
305 msg_Warn( p_filter
, "Unsupported input chroma (%4.4s)",
306 (char*)&(p_pic
->format
.i_chroma
) );
307 picture_Release( p_pic
);
311 p_outpic
= filter_NewPicture( p_filter
);
314 picture_Release( p_pic
);
318 const int i_visible_pitch
= p_pic
->p
->i_visible_pitch
>>1; /* In fact it's i_visible_pixels */
319 const int i_visible_lines
= p_pic
->p
->i_visible_lines
;
321 const uint8_t *p_in
= p_pic
->p
->p_pixels
+i_y_offset
;
322 const uint8_t *p_in_u
= p_pic
->p
->p_pixels
+i_u_offset
;
323 const uint8_t *p_in_v
= p_pic
->p
->p_pixels
+i_v_offset
;
324 const int i_in_pitch
= p_pic
->p
->i_pitch
;
326 uint8_t *p_out
= p_outpic
->p
->p_pixels
+i_y_offset
;
327 uint8_t *p_out_u
= p_outpic
->p
->p_pixels
+i_u_offset
;
328 uint8_t *p_out_v
= p_outpic
->p
->p_pixels
+i_v_offset
;
329 const int i_out_pitch
= p_outpic
->p
->i_pitch
;
331 const int i_line_center
= i_visible_lines
>>1;
332 const int i_col_center
= i_visible_pitch
>>1;
334 vlc_spin_lock( &p_sys
->lock
);
335 const int i_sin
= p_sys
->i_sin
;
336 const int i_cos
= p_sys
->i_cos
;
337 vlc_spin_unlock( &p_sys
->lock
);
340 for( i_line
= 0; i_line
< i_visible_lines
; i_line
++ )
342 for( i_col
= 0; i_col
< i_visible_pitch
; i_col
++ )
346 /* Handle "1st Y", U and V */
347 i_line_orig
= i_line_center
+
348 ( ( i_sin
* ( i_col
- i_col_center
)
349 + i_cos
* ( i_line
- i_line_center
) )>>12 );
350 i_col_orig
= i_col_center
+
351 ( ( i_cos
* ( i_col
- i_col_center
)
352 - i_sin
* ( i_line
- i_line_center
) )>>12 );
353 if( 0 <= i_col_orig
&& i_col_orig
< i_visible_pitch
354 && 0 <= i_line_orig
&& i_line_orig
< i_visible_lines
)
356 p_out
[i_line
*i_out_pitch
+2*i_col
] = p_in
[i_line_orig
*i_in_pitch
+2*i_col_orig
];
358 p_out_u
[i_line
*i_out_pitch
+2*i_col
] = p_in_u
[i_line_orig
*i_in_pitch
+4*i_col_orig
];
359 p_out_v
[i_line
*i_out_pitch
+2*i_col
] = p_in_v
[i_line_orig
*i_in_pitch
+4*i_col_orig
];
363 p_out
[i_line
*i_out_pitch
+2*i_col
] = 0x00;
364 p_out_u
[i_line
*i_out_pitch
+2*i_col
] = 0x80;
365 p_out_v
[i_line
*i_out_pitch
+2*i_col
] = 0x80;
370 if( i_col
>= i_visible_pitch
)
373 i_line_orig
= i_line_center
+
374 ( ( i_sin
* ( i_col
- i_col_center
)
375 + i_cos
* ( i_line
- i_line_center
) )>>12 );
376 i_col_orig
= i_col_center
+
377 ( ( i_cos
* ( i_col
- i_col_center
)
378 - i_sin
* ( i_line
- i_line_center
) )>>12 );
379 if( 0 <= i_col_orig
&& i_col_orig
< i_visible_pitch
380 && 0 <= i_line_orig
&& i_line_orig
< i_visible_lines
)
382 p_out
[i_line
*i_out_pitch
+2*i_col
] = p_in
[i_line_orig
*i_in_pitch
+2*i_col_orig
];
386 p_out
[i_line
*i_out_pitch
+2*i_col
] = 0x00;
391 return CopyInfoAndRelease( p_outpic
, p_pic
);
394 /*****************************************************************************
395 * Angle modification callbacks.
396 *****************************************************************************/
397 static int RotateCallback( vlc_object_t
*p_this
, char const *psz_var
,
398 vlc_value_t oldval
, vlc_value_t newval
,
403 return PreciseRotateCallback( p_this
, psz_var
, oldval
, newval
, p_data
);
406 static int PreciseRotateCallback( vlc_object_t
*p_this
, char const *psz_var
,
407 vlc_value_t oldval
, vlc_value_t newval
,
410 VLC_UNUSED(p_this
); VLC_UNUSED(psz_var
); VLC_UNUSED(oldval
);
411 filter_sys_t
*p_sys
= (filter_sys_t
*)p_data
;
414 cache_trigo( newval
.i_int
, &i_sin
, &i_cos
);
415 vlc_spin_lock( &p_sys
->lock
);
416 p_sys
->i_sin
= i_sin
;
417 p_sys
->i_cos
= i_cos
;
418 vlc_spin_unlock( &p_sys
->lock
);