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