]> git.sesse.net Git - mlt/blob - src/modules/opengl/filter_movit_resize.cpp
Merge branch 'frei0r-metadata'
[mlt] / src / modules / opengl / filter_movit_resize.cpp
1 /*
2  * filter_movit_resize.cpp
3  * Copyright (C) 2013 Dan Dennedy <dan@dennedy.org>
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.h>
21 #include <string.h>
22 #include <math.h>
23 #include <stdlib.h>
24 #include <assert.h>
25
26 #include "glsl_manager.h"
27 #include <movit/init.h>
28 #include <movit/padding_effect.h>
29
30 static float alignment_parse( char* align )
31 {
32         int ret = 0.0f;
33
34         if ( align == NULL );
35         else if ( isdigit( align[ 0 ] ) )
36                 ret = atoi( align );
37         else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
38                 ret = 1.0f;
39         else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' )
40                 ret = 2.0f;
41
42         return ret;
43 }
44
45 static struct mlt_geometry_item_s get_geometry( mlt_profile profile, mlt_filter filter, mlt_frame frame )
46 {
47         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
48         mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter );
49         struct mlt_geometry_item_s item;
50         mlt_geometry geometry = (mlt_geometry) mlt_properties_get_data( filter_props, "geometry", NULL );
51         char *string = mlt_properties_get( properties, "resize.geometry" );
52         int length = mlt_filter_get_length2( filter, frame );
53
54         if ( !geometry ) {
55                 geometry = mlt_geometry_init();
56                 mlt_properties_set_data( filter_props, "geometry", geometry, 0,
57                         (mlt_destructor) mlt_geometry_close, NULL );
58                 mlt_geometry_parse( geometry, string, length, profile->width, profile->height );
59         } else {
60                 mlt_geometry_refresh( geometry, string, length, profile->width, profile->height );
61         }
62
63         mlt_geometry_fetch( geometry, &item, mlt_filter_get_position( filter, frame ) );
64
65         if ( !mlt_properties_get_int( properties, "resize.fill" ) ) {
66                 int x = mlt_properties_get_int( properties, "meta.media.width" );
67                 item.w = item.w > x ? x : item.w;
68                 x = mlt_properties_get_int( properties, "meta.media.height" );
69                 item.h = item.h > x ? x : item.h;
70         }
71
72         return item;
73 }
74
75 static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
76 {
77         int error = 0;
78         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
79         mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame );
80         mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
81
82         // Retrieve the aspect ratio
83         double aspect_ratio = mlt_frame_get_aspect_ratio( frame );
84         double consumer_aspect = mlt_profile_sar( profile );
85
86         // Correct Width/height if necessary
87         if ( *width == 0 || *height == 0 )
88         {
89                 *width = profile->width;
90                 *height = profile->height;
91         }
92
93         // Assign requested width/height from our subordinate
94         int owidth = *width;
95         int oheight = *height;
96
97         // Use a geometry to compute position and size
98         struct mlt_geometry_item_s geometry_item;
99         geometry_item.x = geometry_item.y = 0.0f;
100         geometry_item.distort = 0;
101         if ( mlt_properties_get( properties, "resize.geometry" ) ) {
102                 geometry_item = get_geometry( profile, filter, frame );
103                 owidth = lrintf( geometry_item.w );
104                 oheight = lrintf( geometry_item.h );
105         }
106
107         // Check for the special case - no aspect ratio means no problem :-)
108         if ( aspect_ratio == 0.0 )
109                 aspect_ratio = consumer_aspect;
110
111         // Reset the aspect ratio
112         mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio );
113
114         // Skip processing if requested.
115         char *rescale = mlt_properties_get( properties, "rescale.interp" );
116         if ( *format == mlt_image_none || ( rescale && !strcmp( rescale, "none" ) ) )
117                 return mlt_frame_get_image( frame, image, format, width, height, writable );
118
119         if ( mlt_properties_get_int( properties, "distort" ) == 0 &&
120              geometry_item.distort == 0 )
121         {
122                 // Normalise the input and out display aspect
123                 int normalised_width = profile->width;
124                 int normalised_height = profile->height;
125                 int real_width = mlt_properties_get_int( properties, "meta.media.width" );
126                 int real_height = mlt_properties_get_int( properties, "meta.media.height" );
127                 if ( real_width == 0 )
128                         real_width = mlt_properties_get_int( properties, "width" );
129                 if ( real_height == 0 )
130                         real_height = mlt_properties_get_int( properties, "height" );
131                 double input_ar = aspect_ratio * real_width / real_height;
132                 double output_ar = consumer_aspect * owidth / oheight;
133                 
134                 // Optimised for the input_ar > output_ar case (e.g. widescreen on standard)
135                 int scaled_width = lrint( ( input_ar * normalised_width ) / output_ar );
136                 int scaled_height = normalised_height;
137
138                 // Now ensure that our images fit in the output frame
139                 if ( scaled_width > normalised_width )
140                 {
141                         scaled_width = normalised_width;
142                         scaled_height = lrint( ( output_ar * normalised_height ) / input_ar );
143                 }
144
145                 // Now calculate the actual image size that we want
146                 owidth = lrint( scaled_width * owidth / normalised_width );
147                 oheight = lrint( scaled_height * oheight / normalised_height );
148
149                 mlt_log_debug( MLT_FILTER_SERVICE(filter),
150                         "real %dx%d normalised %dx%d output %dx%d sar %f in-dar %f out-dar %f\n",
151                         real_width, real_height, normalised_width, normalised_height, owidth, oheight, aspect_ratio, input_ar, output_ar);
152
153                 // Tell frame we have conformed the aspect to the consumer
154                 mlt_frame_set_aspect_ratio( frame, consumer_aspect );
155         }
156
157         mlt_properties_set_int( properties, "distort", 0 );
158
159         // Now get the image
160         *format = mlt_image_glsl;
161         error = mlt_frame_get_image( frame, image, format, &owidth, &oheight, writable );
162
163         // Offset the position according to alignment
164         float w = float( *width - owidth );
165         float h = float( *height - oheight );
166         if ( mlt_properties_get( properties, "resize.geometry" ) ) {
167                 // default left if geometry supplied
168                 geometry_item.x += w * alignment_parse( mlt_properties_get( properties, "resize.halign" ) ) / 2.0f;
169                 geometry_item.y += h * alignment_parse( mlt_properties_get( properties, "resize.valign" ) ) / 2.0f;
170         } else {
171                 // default center if no geometry
172                 geometry_item.x = w * 0.5f;
173                 geometry_item.y = h * 0.5f;
174         }
175
176         if ( !error ) {
177                 GlslManager::get_instance()->lock_service( frame );
178                 Effect* effect = GlslManager::get_effect( filter, frame );
179                 if ( effect ) {
180                         bool ok = effect->set_int( "width", *width );
181                         ok |= effect->set_int( "height", *height );
182                         ok |= effect->set_float( "left", geometry_item.x );
183                         ok |= effect->set_float( "top", geometry_item.y );
184                         assert(ok);
185                 }
186                 GlslManager::get_instance()->unlock_service( frame );
187         }
188
189         return error;
190 }
191
192 static mlt_frame process( mlt_filter filter, mlt_frame frame )
193 {
194         if ( !GlslManager::get_effect( filter, frame ) )
195                 GlslManager::add_effect( filter, frame, new PaddingEffect );
196         mlt_frame_push_service( frame, filter );
197         mlt_frame_push_get_image( frame, get_image );
198         return frame;
199 }
200
201 extern "C"
202 mlt_filter filter_movit_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
203 {
204         mlt_filter filter = NULL;
205         GlslManager* glsl = GlslManager::get_instance();
206
207         if ( glsl && ( filter = mlt_filter_new() ) )
208         {
209                 filter->process = process;
210         }
211         return filter;
212 }