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