1 /*
2 * transition_qtblend.cpp -- Qt composite transition
3 * Copyright (c) 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "common.h"
21 #include <framework/mlt.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <QImage>
25 #include <QPainter>
26 #include <QTransform>
27
get_image(mlt_frame a_frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)28 static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
29 {
30 int error = 0;
31 mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
32 mlt_properties b_properties = MLT_FRAME_PROPERTIES( b_frame );
33 mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame );
34 mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) );
35 mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition );
36
37 uint8_t *b_image = NULL;
38 bool hasAlpha = false;
39 double opacity = 1.0;
40 QTransform transform;
41 // reference rect
42 mlt_rect rect;
43
44 // Determine length
45 mlt_position length = mlt_transition_get_length( transition );
46 // Get current position
47 mlt_position position = mlt_transition_get_position( transition, a_frame );
48
49 // Obtain the normalised width and height from the a_frame
50 mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
51 int normalised_width = profile->width;
52 int normalised_height = profile->height;
53 double consumer_ar = mlt_profile_sar( profile );
54 int b_width = mlt_properties_get_int( b_properties, "meta.media.width" );
55 int b_height = mlt_properties_get_int( b_properties, "meta.media.height" );
56 if ( b_height == 0 )
57 {
58 b_width = normalised_width;
59 b_height = normalised_height;
60 }
61 double b_ar = mlt_frame_get_aspect_ratio( b_frame );
62 double b_dar = b_ar * b_width / b_height;
63 rect.w = -1;
64 rect.h = -1;
65 bool consumerScaling = false;
66
67 // Check transform
68 if ( mlt_properties_get( transition_properties, "rect" ) )
69 {
70 rect = mlt_properties_anim_get_rect( transition_properties, "rect", position, length );
71 if (mlt_properties_get(transition_properties, "rect") && ::strchr(mlt_properties_get(transition_properties, "rect"), '%')) {
72 rect.x *= normalised_width;
73 rect.y *= normalised_height;
74 rect.w *= normalised_width;
75 rect.h *= normalised_height;
76 }
77 double scale = mlt_profile_scale_width(profile, *width);
78 if ( scale != 1.0 ) {
79 consumerScaling = true;
80 }
81 rect.x *= scale;
82 rect.w *= scale;
83 scale = mlt_profile_scale_height(profile, *height);
84 if ( !consumerScaling && scale != 1.0 ) {
85 consumerScaling = true;
86 }
87 rect.y *= scale;
88 rect.h *= scale;
89 transform.translate(rect.x, rect.y);
90 opacity = rect.o;
91 }
92
93 double output_ar = mlt_profile_sar( profile );
94 if ( mlt_frame_get_aspect_ratio( b_frame ) == 0 )
95 {
96 mlt_frame_set_aspect_ratio( b_frame, output_ar );
97 }
98
99 if ( mlt_properties_get( transition_properties, "rotation" ) )
100 {
101 double angle = mlt_properties_anim_get_double( transition_properties, "rotation", position, length );
102 if (angle != 0.0) {
103 if ( mlt_properties_get_int( transition_properties, "rotate_center" ) )
104 {
105 transform.translate( rect.w / 2.0, rect.h / 2.0 );
106 transform.rotate( angle );
107 transform.translate( -rect.w / 2.0, -rect.h / 2.0 );
108 }
109 else
110 {
111 transform.rotate( angle );
112 }
113 hasAlpha = true;
114 }
115 }
116
117 // This is not a field-aware transform.
118 mlt_properties_set_int( b_properties, "consumer_deinterlace", 1 );
119
120 // Suppress padding and aspect normalization.
121 char *interps = mlt_properties_get( properties, "rescale.interp" );
122 if ( interps )
123 interps = strdup( interps );
124
125 if ( error )
126 {
127 return error;
128 }
129
130 // Adjust if consumer is scaling
131 if ( consumerScaling )
132 {
133 // Scale request of b frame image to consumer scale maintaining its aspect ratio.
134 b_height = *height;
135 b_width = b_height * b_dar / b_ar;
136 }
137
138 if ( rect.w != -1 )
139 {
140 if ( mlt_properties_get_int( transition_properties, "distort" ) && b_width != 0 && b_height != 0 )
141 {
142 transform.scale( rect.w / b_width, rect.h / b_height );
143 }
144 else
145 {
146 // Determine scale with respect to aspect ratio.
147 double geometry_dar = rect.w * consumer_ar / rect.h;
148 double scale;
149 if ( b_dar > geometry_dar )
150 {
151 scale = rect.w / b_width;
152 }
153 else
154 {
155 scale = rect.h / b_height * b_ar;
156 }
157
158 transform.translate((rect.w - (b_width * scale)) / 2.0, (rect.h - (b_height * scale)) / 2.0);
159 transform.scale( scale, scale );
160 }
161
162 if ( opacity < 1 || rect.x > 0 || rect.y > 0 || (rect.x + rect.w < *width ) || (rect.y + rect.w < *height ) )
163 {
164 // we will process operations on top frame, so also process b_frame
165 hasAlpha = true;
166 }
167 }
168 else
169 {
170 // No transform, request profile sized image
171 if (b_dar != mlt_profile_dar( profile ) )
172 {
173 // Activate transparency if the clips don't have the same aspect ratio
174 hasAlpha = true;
175 }
176 // resize to consumer request
177 b_width = *width;
178 b_height = *height;
179 }
180 if ( !hasAlpha && ( mlt_properties_get_int( transition_properties, "compositing" ) != 0 || b_width < *width || b_height < *height ) )
181 {
182 hasAlpha = true;
183 }
184
185 // Check if we have transparency
186 if ( !hasAlpha )
187 {
188 // fetch image
189 error = mlt_frame_get_image( b_frame, &b_image, format, width, height, 1 );
190 if ( *format == mlt_image_rgb24a || mlt_frame_get_alpha( b_frame ) )
191 {
192 hasAlpha = true;
193 }
194 }
195 if ( !hasAlpha )
196 {
197 // Prepare output image
198 *image = b_image;
199 mlt_frame_replace_image( a_frame, b_image, *format, *width, *height );
200 free( interps );
201 return 0;
202 }
203 // Get RGBA image to process
204 *format = mlt_image_rgb24a;
205 error = mlt_frame_get_image( b_frame, &b_image, format, &b_width, &b_height, writable );
206
207 // Get bottom frame
208 uint8_t *a_image = NULL;
209 error = mlt_frame_get_image( a_frame, &a_image, format, width, height, 1 );
210 if (error)
211 {
212 free( interps );
213 return error;
214 }
215 // Prepare output image
216 int image_size = mlt_image_format_size( *format, *width, *height, NULL );
217 *image = (uint8_t *) mlt_pool_alloc( image_size );
218
219 // Copy bottom frame in output
220 memcpy( *image, a_image, image_size );
221
222 bool hqPainting = false;
223 if ( interps )
224 {
225 if ( strcmp( interps, "bilinear" ) == 0 || strcmp( interps, "bicubic" ) == 0 )
226 {
227 hqPainting = true;
228 }
229 }
230
231 // convert bottom mlt image to qimage
232 QImage bottomImg;
233 convert_mlt_to_qimage_rgba( *image, &bottomImg, *width, *height );
234
235 // convert top mlt image to qimage
236 QImage topImg;
237 convert_mlt_to_qimage_rgba( b_image, &topImg, b_width, b_height );
238
239
240 // setup Qt drawing
241 QPainter painter( &bottomImg );
242 painter.setCompositionMode( ( QPainter::CompositionMode ) mlt_properties_get_int( transition_properties, "compositing" ) );
243 painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform, hqPainting );
244 painter.setTransform(transform);
245 painter.setOpacity(opacity);
246
247 // Composite top frame
248 painter.drawImage(0, 0, topImg);
249
250 // finish Qt drawing
251 painter.end();
252 convert_qimage_to_mlt_rgba( &bottomImg, *image, *width, *height );
253 mlt_frame_set_image( a_frame, *image, image_size, mlt_pool_release);
254 free( interps );
255 return error;
256 }
257
process(mlt_transition transition,mlt_frame a_frame,mlt_frame b_frame)258 static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
259 {
260 mlt_frame_push_service( a_frame, transition );
261 mlt_frame_push_frame( a_frame, b_frame );
262 mlt_frame_push_get_image( a_frame, get_image );
263 return a_frame;
264 }
265
266 extern "C" {
267
transition_qtblend_init(mlt_profile profile,mlt_service_type type,const char * id,void * arg)268 mlt_transition transition_qtblend_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
269 {
270 mlt_transition transition = mlt_transition_new();
271
272 if ( transition )
273 {
274 mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
275
276 if ( !createQApplicationIfNeeded( MLT_TRANSITION_SERVICE(transition) ) )
277 {
278 mlt_transition_close( transition );
279 return NULL;
280 }
281 transition->process = process;
282 mlt_properties_set_int( properties, "_transition_type", 1 ); // video only
283 mlt_properties_set( properties, "rect", (char *) arg );
284 mlt_properties_set_int( properties, "compositing", 0 );
285 mlt_properties_set_int( properties, "distort", 0 );
286 mlt_properties_set_int( properties, "rotate_center", 0 );
287 }
288
289 return transition;
290 }
291
292 } // extern "C"
293