]> git.sesse.net Git - mlt/blob - src/modules/rotoscoping/filter_rotoscoping.c
Add rotoscoping filter (WIP):
[mlt] / src / modules / rotoscoping / filter_rotoscoping.c
1 /*
2  * rotoscoping.c -- (not yet)keyframable vector based rotoscoping
3  * Copyright (C) 2011 Till Theato <root@ttill.de>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <framework/mlt_filter.h>
21 #include <framework/mlt_frame.h>
22 #include <framework/mlt_tokeniser.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h>
28
29 #define MAX( x, y ) x > y ? x : y
30 #define MIN( x, y ) x < y ? x : y
31
32 /** x, y pair with double precision */
33 typedef struct PointF
34 {
35     double x;
36     double y;
37 } PointF;
38
39 /**
40  * Extracts the polygon points stored in \param string
41  * \param string string containing the points in the format: x1;y1#x2;y2#...
42  * \param points pointer to array of points. Will be allocated and filled with the points in \param string
43  * \return number of points
44  */
45 int parsePolygonString( char *string, PointF **points )
46 {
47     // Create a tokeniser
48     mlt_tokeniser tokens = mlt_tokeniser_init( );
49
50     // Tokenise
51     if ( string != NULL )
52         mlt_tokeniser_parse_new( tokens, string, "#" );
53
54     // Iterate through each token
55     int count = mlt_tokeniser_count( tokens );
56     *points = malloc( (count + 1) * sizeof( struct PointF ) );
57     int i;
58     for ( i = 0; i < count; ++i )
59     {
60         char *value = mlt_tokeniser_get_string( tokens, i );
61         (*points)[i].x = atof( value );
62         (*points)[i].y = atof( strchr( value, ';' ) + 1 );
63     }
64
65     // Remove the tokeniser
66     mlt_tokeniser_close( tokens );
67
68     return count;
69 }
70
71 /** helper for using qsort with an array of integers. */
72 int ncompare( const void *a, const void *b )
73 {
74     return *(const int*)a - *(const int*)b;
75 }
76
77 /**
78  * Determines which points are located in the polygon and sets their value in \param map to 1
79  * \param vertices points defining the polygon
80  * \param count number of vertices
81  * \param with x range
82  * \param height y range
83  * \param map array of integers of the dimension width * height.
84  *            The map entries belonging to the points in the polygon will be set to 1, the other entries remain untouched.
85  * 
86  * based on public-domain code by Darel Rex Finley, 2007
87  * \see: http://alienryderflex.com/polygon_fill/
88  */
89 void fillMap( PointF *vertices, int count, int width, int height, int *map )
90 {
91     int nodes, nodeX[1024], pixelX, pixelY, i, j, swap;
92     
93     // Loop through the rows of the image
94     for ( pixelY = 0; pixelY < height; pixelY++ )
95     {
96         /*
97          * Build a list of nodes.
98          * nodes are located at the borders of the polygon
99          * and therefore indicate a move from in to out or vice versa
100          */
101         nodes = 0;
102         for ( i = 0, j = count - 1; i < count; j = i++ )
103             if ( (vertices[i].y > (double)pixelY) != (vertices[j].y > (double)pixelY) )
104                 nodeX[nodes++] = (int)(vertices[i].x + (pixelY - vertices[i].y) / (vertices[j].y - vertices[i].y) * (vertices[j].x - vertices[i].x) );
105
106         qsort( nodeX, nodes, sizeof( int ), ncompare );
107
108         // Fill the pixels between node pairs.
109         for ( i = 0; i < nodes; i += 2 )
110         {
111             if ( nodeX[i] >= width )
112                 break;
113             if ( nodeX[i+1] > 0 )
114             {
115                 nodeX[i] = MAX( 0, nodeX[i] );
116                 nodeX[i+1] = MIN( nodeX[i+1], width );
117                 for ( j = nodeX[i]; j < nodeX[i+1]; j++ )
118                     map[width * pixelY + j] = 1;
119             }
120         }
121     }
122 }
123
124 /** Do it :-).
125 */
126 static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
127 {
128     // Get the image
129     *format = mlt_image_rgb24a;
130     int error = mlt_frame_get_image( this, image, format, width, height, 1 );
131
132     // Only process if we have no error and a valid colour space
133     if ( !error )
134     {
135         struct PointF *points;
136         int length, count;
137         points = mlt_properties_get_data( MLT_FRAME_PROPERTIES( this ), "points", &length );
138         count = length / sizeof( struct PointF );
139
140         int i;
141         for ( i = 0; i < count; ++i ) {
142             points[i].x *= *width;
143             points[i].y *= *height;
144         }
145
146         if ( count )
147         {
148             int *map = calloc( *width * *height, sizeof( int ) );
149             fillMap( points, count, *width, *height, map );
150
151             uint8_t *p = *image;
152             uint8_t *q = *image + *width * *height * 4;
153
154             while ( p != q )
155             {
156                 int pixel = (p - *image) / 4;
157                 if ( !map[pixel] ) {
158                     // pixel is not in polygon
159                     // => set to black
160                     p[0] = p[1] = p[2] = 0;
161                 }
162                 p += 4;
163             }
164
165             free( map );
166         }
167     }
168
169     return error;
170 }
171
172 /** Filter processing.
173 */
174 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
175 {
176     char *polygon = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "polygon" );
177
178     if ( polygon == NULL || strcmp( polygon, "" ) == 0 ) {
179         // :( we are redundant
180         return frame;
181     }
182
183     struct PointF *points;
184     int count = parsePolygonString( polygon, &points );
185     int length = count * sizeof( struct PointF );
186
187     mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "points", points, length, free, NULL );
188     mlt_frame_push_get_image( frame, filter_get_image );
189
190     return frame;
191 }
192
193 /** Constructor for the filter.
194 */
195 mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
196 {
197         mlt_filter this = mlt_filter_new( );
198         if ( this != NULL )
199         {
200                 this->process = filter_process;
201                 if ( arg != NULL )
202                     mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "polygon", arg );
203         }
204         return this;
205 }