WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / libhb / rotate.c
blob090a1b4183be170a0f4f5705ea1bac228a80b37f
1 /* rorate.c
3 Copyright (c) 2003-2015 HandBrake Team
4 This file is part of the HandBrake source code
5 Homepage: <http://handbrake.fr/>.
6 It may be used under the terms of the GNU General Public License v2.
7 For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8 */
10 #include "hb.h"
11 #include "hbffmpeg.h"
12 #include "taskset.h"
14 #define MODE_DEFAULT 3
15 // Settings:
16 // degrees:mirror
18 // degrees - Rotation angle, may be one of 90, 180, or 270
19 // mirror - Mirror image around x axis
21 // Examples:
22 // Mode 180:1 Mirror then rotate 180'
23 // Mode 0:1 Mirror
24 // Mode 180:0 Rotate 180'
25 // Mode 90:0 Rotate 90'
26 // Mode 270:0 Rotate 270'
28 // Legacy Mode Examples (also accepted):
29 // Mode 1: Flip vertically (y0 becomes yN and yN becomes y0) (aka 180:1)
30 // Mode 2: Flip horizontally (x0 becomes xN and xN becomes x0) (aka 0:1)
31 // Mode 3: Flip both horizontally and vertically (aka 180:0)
32 // Mode 4: Rotate 90' (aka 90:0)
33 // Mode 7: Flip horiz & vert plus Rotate 90' (aka 270:0)
35 typedef struct rotate_arguments_s {
36 hb_buffer_t *dst;
37 hb_buffer_t *src;
38 } rotate_arguments_t;
40 struct hb_filter_private_s
42 int mode;
43 int width;
44 int height;
45 hb_rational_t par;
47 int cpu_count;
49 taskset_t rotate_taskset; // Threads for Rotate - one per CPU
50 rotate_arguments_t *rotate_arguments; // Arguments to thread for work
53 static int hb_rotate_init( hb_filter_object_t * filter,
54 hb_filter_init_t * init );
56 static int hb_rotate_work( hb_filter_object_t * filter,
57 hb_buffer_t ** buf_in,
58 hb_buffer_t ** buf_out );
60 static void hb_rotate_close( hb_filter_object_t * filter );
62 static int hb_rotate_info( hb_filter_object_t * filter,
63 hb_filter_info_t * info );
65 hb_filter_object_t hb_filter_rotate =
67 .id = HB_FILTER_ROTATE,
68 .enforce_order = 0,
69 .name = "Rotate (rotate & flip image axes)",
70 .settings = NULL,
71 .init = hb_rotate_init,
72 .work = hb_rotate_work,
73 .close = hb_rotate_close,
74 .info = hb_rotate_info
78 typedef struct rotate_thread_arg_s {
79 hb_filter_private_t *pv;
80 int segment;
81 } rotate_thread_arg_t;
84 * rotate this segment of all three planes in a single thread.
86 void rotate_filter_thread( void *thread_args_v )
88 rotate_arguments_t *rotate_work = NULL;
89 hb_filter_private_t * pv;
90 int run = 1;
91 int plane;
92 int segment, segment_start, segment_stop;
93 rotate_thread_arg_t *thread_args = thread_args_v;
94 uint8_t *dst;
95 hb_buffer_t *dst_buf;
96 hb_buffer_t *src_buf;
97 int y;
100 pv = thread_args->pv;
101 segment = thread_args->segment;
103 hb_log("Rotate thread started for segment %d", segment);
105 while( run )
108 * Wait here until there is work to do.
110 taskset_thread_wait4start( &pv->rotate_taskset, segment );
112 if( taskset_thread_stop( &pv->rotate_taskset, segment ) )
115 * No more work to do, exit this thread.
117 run = 0;
118 goto report_completion;
121 rotate_work = &pv->rotate_arguments[segment];
122 if( rotate_work->dst == NULL )
124 hb_error( "Thread started when no work available" );
125 hb_snooze(500);
126 goto report_completion;
130 * Process all three planes, but only this segment of it.
132 dst_buf = rotate_work->dst;
133 src_buf = rotate_work->src;
134 for( plane = 0; plane < 3; plane++)
136 int dst_stride, src_stride;
138 dst = dst_buf->plane[plane].data;
139 dst_stride = dst_buf->plane[plane].stride;
140 src_stride = src_buf->plane[plane].stride;
142 int h = src_buf->plane[plane].height;
143 int w = src_buf->plane[plane].width;
144 segment_start = ( h / pv->cpu_count ) * segment;
145 if( segment == pv->cpu_count - 1 )
148 * Final segment
150 segment_stop = h;
151 } else {
152 segment_stop = ( h / pv->cpu_count ) * ( segment + 1 );
155 for( y = segment_start; y < segment_stop; y++ )
157 uint8_t * cur;
158 int x, xo, yo;
160 cur = &src_buf->plane[plane].data[y * src_stride];
161 for( x = 0; x < w; x++)
163 if( pv->mode & 1 )
165 yo = h - y - 1;
167 else
169 yo = y;
171 if( pv->mode & 2 )
173 xo = w - x - 1;
175 else
177 xo = x;
179 if( pv->mode & 4 ) // Rotate 90 clockwise
181 int tmp = xo;
182 xo = h - yo - 1;
183 yo = tmp;
185 dst[yo*dst_stride + xo] = cur[x];
190 report_completion:
192 * Finished this segment, let everyone know.
194 taskset_thread_complete( &pv->rotate_taskset, segment );
200 * threaded rotate - each thread rotates a single segment of all
201 * three planes. Where a segment is defined as the frame divided by
202 * the number of CPUs.
204 * This function blocks until the frame is rotated.
206 static void rotate_filter(
207 hb_filter_private_t * pv,
208 hb_buffer_t *out,
209 hb_buffer_t *in )
212 int segment;
214 for( segment = 0; segment < pv->cpu_count; segment++ )
217 * Setup the work for this plane.
219 pv->rotate_arguments[segment].dst = out;
220 pv->rotate_arguments[segment].src = in;
224 * Allow the taskset threads to make one pass over the data.
226 taskset_cycle( &pv->rotate_taskset );
229 * Entire frame is now rotated.
234 static int hb_rotate_init( hb_filter_object_t * filter,
235 hb_filter_init_t * init )
237 filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
238 hb_filter_private_t * pv = filter->private_data;
240 int degrees = MODE_DEFAULT, mirror = 0, num_params = 0;
242 if( filter->settings )
244 num_params = sscanf(filter->settings, "%d:%d", &degrees, &mirror);
246 switch (degrees)
248 case 0:
249 pv->mode = 0;
250 break;
251 case 90:
252 pv->mode = 4;
253 break;
254 case 180:
255 pv->mode = 3;
256 break;
257 case 270:
258 pv->mode = 7;
259 break;
260 default:
261 if (degrees & ~7)
263 // Unsupported rotation angle
264 hb_error("Unsupported rotate mode %d", degrees);
266 // Legacy "mode" supplied in settings
267 pv->mode = degrees & 7;
268 break;
270 if (num_params > 1 && mirror)
271 pv->mode ^= 2;
273 pv->cpu_count = hb_get_cpu_count();
276 * Create rotate taskset.
278 pv->rotate_arguments = malloc( sizeof( rotate_arguments_t ) * pv->cpu_count );
279 if( pv->rotate_arguments == NULL ||
280 taskset_init( &pv->rotate_taskset, /*thread_count*/pv->cpu_count,
281 sizeof( rotate_thread_arg_t ) ) == 0 )
283 hb_error( "rotate could not initialize taskset" );
286 int i;
287 for( i = 0; i < pv->cpu_count; i++ )
289 rotate_thread_arg_t *thread_args;
291 thread_args = taskset_thread_args( &pv->rotate_taskset, i );
293 thread_args->pv = pv;
294 thread_args->segment = i;
295 pv->rotate_arguments[i].dst = NULL;
297 if( taskset_thread_spawn( &pv->rotate_taskset, i,
298 "rotate_filter_segment",
299 rotate_filter_thread,
300 HB_NORMAL_PRIORITY ) == 0 )
302 hb_error( "rotate could not spawn thread" );
305 // Set init width/height so the next stage in the pipline
306 // knows what it will be getting
307 if( pv->mode & 4 )
309 // 90 degree rotation, exchange width and height
310 int tmp = init->geometry.width;
311 init->geometry.width = init->geometry.height;
312 init->geometry.height = tmp;
314 tmp = init->geometry.par.num;
315 init->geometry.par.num = init->geometry.par.den;
316 init->geometry.par.den = tmp;
318 pv->width = init->geometry.width;
319 pv->height = init->geometry.height;
320 pv->par = init->geometry.par;
322 return 0;
325 static int hb_rotate_info( hb_filter_object_t * filter,
326 hb_filter_info_t * info )
328 hb_filter_private_t * pv = filter->private_data;
329 if( !pv )
330 return 1;
332 memset( info, 0, sizeof( hb_filter_info_t ) );
333 info->out.geometry.width = pv->width;
334 info->out.geometry.height = pv->height;
335 info->out.geometry.par = pv->par;
336 int pos = 0;
338 int mirror_x = !!(pv->mode & 2);
339 int mirror_y = !!(pv->mode & 1);
340 int degrees = 0;
341 if (mirror_y)
343 degrees += 180;
344 mirror_x ^= 1;
345 mirror_y ^= 1;
347 degrees += !!(pv->mode & 4) * 90;
349 if (degrees > 0)
351 sprintf(&info->human_readable_desc[pos], "Rotate %d%s", degrees,
352 mirror_x ? " / mirror image" : "");
354 else if (mirror_x)
356 sprintf(&info->human_readable_desc[pos], "Mirror image");
358 else
360 sprintf(&info->human_readable_desc[pos], "No rotation or mirror!");
362 return 0;
365 static void hb_rotate_close( hb_filter_object_t * filter )
367 hb_filter_private_t * pv = filter->private_data;
369 if( !pv )
371 return;
374 taskset_fini( &pv->rotate_taskset );
377 * free memory for rotate structs
379 free( pv->rotate_arguments );
381 free( pv );
382 filter->private_data = NULL;
385 static int hb_rotate_work( hb_filter_object_t * filter,
386 hb_buffer_t ** buf_in,
387 hb_buffer_t ** buf_out )
389 hb_filter_private_t * pv = filter->private_data;
390 hb_buffer_t * in = *buf_in, * out;
392 if (in->s.flags & HB_BUF_FLAG_EOF)
394 *buf_out = in;
395 *buf_in = NULL;
396 return HB_FILTER_DONE;
398 if (pv->mode == 0)
400 // Short circuit case where filter does nothing
401 *buf_out = in;
402 *buf_in = NULL;
403 return HB_FILTER_OK;
406 int width_out, height_out;
407 if ( pv->mode & 4 )
409 width_out = in->f.height;
410 height_out = in->f.width;
412 else
414 width_out = in->f.width;
415 height_out = in->f.height;
418 out = hb_video_buffer_init( width_out, height_out );
420 // Rotate!
421 rotate_filter( pv, out, in );
422 out->s = in->s;
423 hb_buffer_move_subs( out, in );
425 *buf_out = out;
427 return HB_FILTER_OK;