1 /*
2 * filter_typewriter.cpp -- typewriter filter
3 * Copyright (c) 2021 <rafallalik@gmail.com>
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 wrenderedanty 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 <framework/mlt.h>
21 #include <framework/mlt_filter.h>
22 #include <framework/mlt_frame.h>
23
24 #include <cassert>
25 #include <cstdio>
26 #include <cstdlib>
27 #include <cstring>
28 #include <vector>
29
30 #include "typewriter.h"
31 #include "kdenlivetitle_wrapper.h"
32
33 struct FilterContainer {
34 XmlParser xp;
35
36 std::vector<TypeWriter> renders; // rendered data [array]
37 bool init; // 1 if initialized
38
39 int current_frame; // currently parsed frame
40
41 std::string xml_data; // data field content (xml data)
42 bool is_template;
43 int step_length; // frame step value
44 float sigma; // sigma of fluctuations
45 int seed; // seed for random fluctuations
46 int macro; // macro type: 0 - custom, 1 - char, 2 - word, 3 - line
47
48 int producer_type; // 1 - kdenlivetitle
49 mlt_producer producer; // hold producer pointer
50
FilterContainerFilterContainer51 FilterContainer() {
52 clean();
53 }
54
cleanFilterContainer55 void clean()
56 {
57 renders.clear();
58 init = false;
59 current_frame = -1;
60 xml_data.clear();
61 is_template = false;
62 step_length = 0;
63 sigma = 0;
64 seed = 0;
65 macro = 0;
66 producer_type = 0;
67 producer = nullptr;
68 }
69 };
70
71 /*
72 * Get data for display.
73 */
get_producer_data(mlt_properties filter_p,mlt_properties frame_p,FilterContainer * cont)74 static int get_producer_data(mlt_properties filter_p, mlt_properties frame_p, FilterContainer * cont)
75 {
76 if (cont == nullptr)
77 return 0;
78
79 char * d = nullptr;
80 int step_length = 0;
81 int sigma = 0;
82 int seed = 0;
83 int macro = 0;
84
85 mlt_producer producer = nullptr;
86 mlt_properties producer_properties = nullptr;
87
88 unsigned int update_mask = 0;
89
90 /* Try with kdenlivetitle */
91 producer_ktitle kt = static_cast<producer_ktitle>(mlt_properties_get_data( frame_p, "producer_kdenlivetitle", NULL ) );
92 if (kt != nullptr)
93 {
94 /* Obtain properties of producer */
95 producer = &kt->parent;
96 producer_properties = MLT_PRODUCER_PROPERTIES( producer );
97
98 if (producer == nullptr || producer_properties == nullptr)
99 return 0;
100
101 d = mlt_properties_get( producer_properties, "resource" );
102 cont->is_template = (d && d[0] != '\0');
103
104 if (cont->is_template)
105 d = mlt_properties_get( producer_properties, "_xmldata");
106 else
107 d = mlt_properties_get( producer_properties, "xmldata" );
108
109 if (d == nullptr)
110 return 0;
111
112 step_length = mlt_properties_get_int(filter_p, "step_length");
113 sigma = mlt_properties_get_int(filter_p, "step_sigma");
114 seed = mlt_properties_get_int(filter_p, "random_seed");
115 macro = mlt_properties_get_int(filter_p, "macro_type");
116
117 // if xml data changed, set update mask 0x1
118 if (cont->xml_data != d || macro != cont->macro)
119 update_mask = 0x3;
120
121 if (step_length != cont->step_length || sigma != cont->sigma || seed != cont->seed)
122 update_mask |= 0x2;
123
124 // clear and prepare for new parsing
125 if (0 == update_mask)
126 return 1;
127 }
128 else
129 {
130 return 0;
131 }
132
133 if (update_mask & 0x1)
134 {
135 cont->clean();
136
137 // save new data field name
138 cont->xml_data = d;
139
140 // Get content data and backup in the tw container.
141 cont->xp.setDocument(d);
142 cont->xp.parse();
143 unsigned int n = cont->xp.getContentNodesNumber();
144 for (uint i = 0; i < n; ++i)
145 {
146 std::string key = cont->xp.getNodeContent(i).toStdString();
147 TypeWriter data;
148
149 if (macro) {
150 char * buff = new char[key.length()+5];
151 char c = 0;
152 switch (macro) {
153 case 1: c = 'c'; break;
154 case 2: c = 'w'; break;
155 case 3: c = 'l'; break;
156 default: break;
157 }
158
159 sprintf(buff, ":%c{%s}", c, key.c_str());
160 data.setPattern(buff);
161 delete [] buff;
162 } else {
163 data.setPattern(key);
164 }
165 cont->renders.push_back(data);
166 }
167
168 cont->macro = macro;
169 cont->producer_type = 1;
170 cont->producer = producer;
171
172 // mark as inited
173 cont->init = true;
174 }
175
176 if (update_mask & 0x2)
177 {
178 for (auto & render : cont->renders)
179 {
180 render.setFrameStep(step_length);
181 render.setStepSigma(sigma);
182 render.setStepSeed(seed);
183 render.parse();
184 }
185 cont->step_length = step_length;
186 cont->sigma = sigma;
187 cont->seed = seed;
188 }
189
190 return 1;
191 }
192
update_producer(mlt_frame frame,mlt_properties,FilterContainer * cont,bool restore)193 static int update_producer(mlt_frame frame, mlt_properties /*frame_p*/, FilterContainer * cont, bool restore)
194 {
195 if (cont->init == false)
196 return 0;
197
198 mlt_position pos = mlt_frame_original_position( frame );
199
200 mlt_properties producer_properties = nullptr;
201 if (cont->producer_type == 1)
202 {
203 producer_properties = MLT_PRODUCER_PROPERTIES( cont->producer );
204 if (restore)
205 mlt_properties_set_int( producer_properties, "force_reload", 0 );
206 else
207 mlt_properties_set_int( producer_properties, "force_reload", 1 );
208 }
209
210 if (producer_properties == nullptr)
211 return 0;
212
213 if (restore == true)
214 {
215 if (cont->is_template)
216 mlt_properties_set( producer_properties, "_xmldata", cont->xml_data.c_str() );
217 else
218 mlt_properties_set( producer_properties, "xmldata", cont->xml_data.c_str() );
219 return 1;
220 }
221
222 assert((cont->xp.getContentNodesNumber() == cont->renders.size()));
223 // render the string and set as a content value
224 unsigned int n = cont->xp.getContentNodesNumber();
225 for (uint i = 0; i < n; ++i) {
226 cont->xp.setNodeContent(i, cont->renders[i].render(pos).c_str());
227 }
228
229 // update producer for rest of the frame
230 QString dom = cont->xp.getDocument();
231
232 if (cont->is_template)
233 mlt_properties_set( producer_properties, "_xmldata", dom.toStdString().c_str() );
234 else
235 mlt_properties_set( producer_properties, "xmldata", dom.toStdString().c_str() );
236
237 cont->current_frame = pos;
238
239 return 1;
240 }
241
filter_get_image(mlt_frame frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int)242 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int /*writable*/ )
243 {
244 int error = 0;
245 mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame );
246 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
247
248 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
249
250 FilterContainer * cont = (FilterContainer*) filter->child;
251
252 mlt_service_lock(MLT_FILTER_SERVICE(filter));
253
254 int res = get_producer_data(properties, frame_properties, cont);
255 if (res == 0)
256 return mlt_frame_get_image( frame, image, format, width, height, 1 );
257
258 update_producer(frame, frame_properties, cont, false);
259
260 error = mlt_frame_get_image( frame, image, format, width, height, 1 );
261
262 update_producer(frame, frame_properties, cont, true);
263
264 mlt_service_unlock(MLT_FILTER_SERVICE(filter));
265
266 return error;
267 }
268
filter_process(mlt_filter filter,mlt_frame frame)269 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
270 {
271 mlt_frame_push_service( frame, filter );
272 mlt_frame_push_get_image( frame, filter_get_image );
273 return frame;
274 }
275
filter_close(mlt_filter filter)276 static void filter_close( mlt_filter filter)
277 {
278 FilterContainer * cont = (FilterContainer *) filter->child;
279
280 cont->clean();
281 }
282
283 extern "C" {
filter_typewriter_init(mlt_profile,mlt_service_type,const char *,char *)284 mlt_filter filter_typewriter_init( mlt_profile /*profile*/, mlt_service_type /*type*/, const char */*id*/, char */*arg*/ )
285 {
286 mlt_filter filter = mlt_filter_new( );
287 FilterContainer* cont = new FilterContainer;
288
289 if ( filter != nullptr && cont != nullptr)
290 {
291 filter->process = filter_process;
292 filter->child = cont;
293 filter->close = filter_close;
294 }
295
296 mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
297 mlt_properties_set_int(properties, "step_length", 25 );
298 mlt_properties_set_int(properties, "step_sigma", 0);
299 mlt_properties_set_int(properties, "random_seed", 0);
300 mlt_properties_set_int(properties, "macro_type", 1);
301
302 return filter;
303 }
304 }
305