Fourth set of 50 GPL attribution for CV-Contributors added +
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / yuv411 / yuv411.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2016 Eric Olson
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published
7  * by 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, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 #include "clip.h"
22 #include "bccmodels.h"
23 #include "bchash.h"
24 #include "filexml.h"
25 #include "yuv411.h"
26 #include "yuv411win.h"
27 #include "language.h"
28
29 #include <stdint.h>
30 #include <string.h>
31
32 REGISTER_PLUGIN(yuv411Main)
33
34
35 yuv411Config::yuv411Config()
36 {
37         reset();
38 }
39
40 void yuv411Config::reset()
41 {
42         int_horizontal = 0;
43         avg_vertical = 0;
44         inpainting = 0;
45         offset = 1;
46         thresh = 4;
47         bias = 1;
48 }
49
50 void yuv411Config::copy_from(yuv411Config &that)
51 {
52         int_horizontal = that.int_horizontal;
53         avg_vertical = that.avg_vertical;
54         inpainting = that.inpainting;
55         offset = that.offset;
56         thresh = that.thresh;
57         bias = that.bias;
58 }
59
60 int yuv411Config::equivalent(yuv411Config &that)
61 {
62         return int_horizontal == that.int_horizontal &&
63                 avg_vertical == that.avg_vertical &&
64                 inpainting == that.inpainting &&
65                 offset == that.offset &&
66                 thresh == that.thresh &&
67                 bias == that.bias;
68 }
69
70 void yuv411Config::interpolate(yuv411Config &prev,
71         yuv411Config &next,
72         long prev_frame,
73         long next_frame,
74         long current_frame)
75 {
76         this->int_horizontal = prev.int_horizontal;
77         this->avg_vertical = prev.avg_vertical;
78         this->inpainting = prev.inpainting;
79         this->offset = prev.offset;
80         this->thresh = prev.thresh;
81         this->bias = prev.bias;
82 }
83
84
85 yuv411Main::yuv411Main(PluginServer *server)
86  : PluginVClient(server)
87 {
88         this->server = server;
89         this->temp_frame = 0;
90         colormodel = -1;
91 }
92
93 yuv411Main::~yuv411Main()
94 {
95         delete temp_frame;
96 }
97
98 const char *yuv411Main::plugin_title() { return N_("YUV411"); }
99 int yuv411Main::is_realtime() { return 1; }
100
101 #define YUV411_MACRO(type, components) \
102 { \
103     type **input_rows = ((type**)input_ptr->get_rows()), **in_rows = input_rows; \
104     type **output_rows = ((type**)output_ptr->get_rows()), **out_rows = output_rows; \
105     if( config.avg_vertical ) { \
106         if( config.int_horizontal ) \
107             in_rows = out_rows = ((type**)temp_frame->get_rows()); \
108         for( int i=0,h3=h-3; i<h3; i+=4 ) { \
109             type *in_row0 = input_rows[i+0], *in_row1 = input_rows[i+1]; \
110             type *in_row2 = input_rows[i+2], *in_row3 = input_rows[i+3]; \
111             type *out_row0 = out_rows[i+0], *out_row1 = out_rows[i+1]; \
112             type *out_row2 = out_rows[i+2], *out_row3 = out_rows[i+3]; \
113             for( int k = 0; k<w; ++k ) { \
114                 for( int uv=1; uv<=2; ++uv ) { \
115                    out_row0[uv] = out_row2[uv] = (in_row0[uv]+in_row2[uv]+1)/2; \
116                    out_row1[uv] = out_row3[uv] = (in_row1[uv]+in_row3[uv]+1)/2; \
117                 } \
118                 in_row0 += components; in_row1 += components; \
119                 in_row2 += components; in_row3 += components; \
120                 out_row0 += components; out_row1 += components; \
121                 out_row2 += components; out_row3 += components; \
122             } \
123         } \
124     } \
125  \
126     if( config.int_horizontal ) { \
127         if( config.inpainting ) { \
128             int kmax = w-7+config.offset; \
129             for( int i=0; i<h; ++i ) { \
130                 type *in_row0 = in_rows[i+0], *out_row0 = output_rows[i+0]; \
131                 for( int k=config.offset; k<kmax; k+=4 ) { \
132                     int k0 = (k+0) * components, a = in_row0[k0]; \
133                     int b, d = 0, k4 = (k+4) * components; \
134                     for( int jk=k0,n=4; --n>=0; a=b ) { \
135                         b = in_row0[jk+=components]; \
136                         d += a<b ? b-a : a-b;  d += config.bias; \
137                     } \
138                     if( d < config.thresh ) { \
139                         for( int jk=k0,n=4; --n>0; ) { \
140                             jk += components; \
141                             for( int uv=1; uv<=2; ++uv ) { \
142                                 out_row0[jk+uv] = (n*in_row0[jk+uv] + (4-n)*in_row0[k4+uv]+2)/4; \
143                             } \
144                         } \
145                     } \
146                     else { \
147                         int t = 0;  a = in_row0[k0]; \
148                         for( int jk=k0,n=4; --n>0; a=b ) { \
149                             b = in_row0[jk+=components]; \
150                             t += a<b ? b-a : a-b;  t += config.bias; \
151                             for( int uv=1; uv<=2; ++uv ) { \
152                               out_row0[jk+uv] = (2*((d-t)*in_row0[k0+uv] + t*in_row0[k4+uv])+d)/(2*d); \
153                             } \
154                         } \
155                     } \
156                 } \
157             } \
158         } \
159         else { \
160             int kmax = w-7+config.offset; \
161             for( int i=0; i<h; ++i ) { \
162                 type *in_row0 = in_rows[i]; \
163                 type *out_row0 = output_rows[i]; \
164                 for( int k=config.offset; k<kmax; k+=4 ) { \
165                     for( int uv=1; uv<=2; ++uv ) { \
166                         int sum, avg, k0 = (k+0) * components, k4 = (k+4) * components; \
167                         sum  = in_row0[k0 + uv]; sum += in_row0[(k0+=components) + uv]; \
168                         sum += in_row0[k4 + uv]; sum += in_row0[(k4+=components) + uv]; \
169                         avg = (sum + 2) / 4; \
170                         out_row0[(k0+=components)+uv] = avg; \
171                         out_row0[(k0+=components)+uv] = avg; \
172                     } \
173                 } \
174             } \
175         } \
176     } \
177 }
178
179 int yuv411Main::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
180 {
181         load_configuration();
182         int w = input_ptr->get_w();
183         int h = input_ptr->get_h();
184         colormodel = input_ptr->get_color_model();
185
186         if( input_ptr == output_ptr ||
187             ( config.avg_vertical && config.int_horizontal ) ) {
188                 if( temp_frame && (temp_frame->get_color_model() != colormodel ||
189                     temp_frame->get_w() != w || temp_frame->get_h() != h) ) {
190                         delete temp_frame;
191                         temp_frame = 0;
192                 }
193                 if( !temp_frame )
194                         temp_frame = new VFrame(w, h, colormodel, 0);
195                 if( input_ptr == output_ptr ) {
196                         temp_frame->copy_from(input_ptr);
197                         input_ptr = temp_frame;
198                 }
199         }
200
201         switch( colormodel ) {
202         case BC_YUV888:
203                 YUV411_MACRO(unsigned char, 3);
204                 break;
205         case BC_YUVA8888:
206                 YUV411_MACRO(unsigned char, 4);
207                 break;
208         }
209
210         send_render_gui(this);
211         return 0;
212 }
213
214
215 NEW_WINDOW_MACRO(yuv411Main, yuv411Window)
216
217 LOAD_CONFIGURATION_MACRO(yuv411Main, yuv411Config)
218
219 void yuv411Main::update_gui()
220 {
221         if(thread)
222         {
223                 load_configuration();
224                 thread->window->lock_window();
225                 yuv411Window *window = (yuv411Window *)thread->window;
226                 window->update_enables();
227                 window->avg_vertical->update((int)config.avg_vertical);
228                 window->int_horizontal->update((int)config.int_horizontal);
229                 window->inpainting->update((int)config.inpainting);
230                 window->offset->update((int)config.offset);
231                 window->thresh->update((int)config.thresh);
232                 window->bias->update((int)config.bias);
233                 window->unlock_window();
234         }
235 }
236
237 void yuv411Main::render_gui(void *data)
238 {
239         if(thread) {
240                 yuv411Window *window = (yuv411Window *)thread->window;
241                 yuv411Main *client = (yuv411Main *)data;
242                 if( window->colormodel != client->colormodel ) {
243                         int warn = 1;
244                         switch( client->colormodel ) {
245                         case BC_YUV888:
246                         case BC_YUVA8888:
247                                 warn = 0;
248                                 break;
249                         }
250                         if( warn == window->yuv_warning->is_hidden() ) {
251                                 window->lock_window("yuv411Main::render_gui");
252                                 window->show_warning(warn);
253                                 window->colormodel = client->colormodel;
254                                 window->unlock_window();
255                         }
256                 }
257         }
258 }
259
260 void yuv411Main::save_data(KeyFrame *keyframe)
261 {
262         FileXML output;
263
264 // cause data to be stored directly in text
265         output.set_shared_output(keyframe->xbuf);
266
267         output.tag.set_title("YUV411");
268         output.tag.set_property("OFFSET",config.offset);
269         output.tag.set_property("THRESH",config.thresh);
270         output.tag.set_property("BIAS",config.bias);
271         output.append_tag();
272         if(config.avg_vertical) {
273                 output.tag.set_title("VERTICAL");
274                 output.append_tag();
275                 output.tag.set_title("/VERTICAL");
276                 output.append_tag();
277         }
278         if(config.int_horizontal) {
279                 output.tag.set_title("HORIZONTAL");
280                 output.append_tag();
281                 output.tag.set_title("/HORIZONTAL");
282                 output.append_tag();
283         }
284         if(config.inpainting ) {
285                 output.tag.set_title("INPAINTING");
286                 output.append_tag();
287                 output.tag.set_title("/INPAINTING");
288                 output.append_tag();
289         }
290         output.tag.set_title("/YUV411");
291         output.append_tag();
292         output.terminate_string();
293 }
294
295 void yuv411Main::read_data(KeyFrame *keyframe)
296 {
297         FileXML input;
298
299         input.set_shared_input(keyframe->xbuf);
300
301         int result = 0;
302         config.avg_vertical = config.int_horizontal = 0;
303         config.inpainting = 0;
304         config.offset = 1;
305         config.thresh = 4;
306         config.bias = 1;
307
308         while(!(result = input.read_tag()) ) {
309                 if( input.tag.title_is("YUV411") ) {
310                         config.offset = input.tag.get_property("OFFSET",config.offset);
311                         config.thresh = input.tag.get_property("THRESH",config.thresh);
312                         config.bias = input.tag.get_property("BIAS",config.bias);
313                 }
314                 else if(input.tag.title_is("VERTICAL")) {
315                         config.avg_vertical = 1;
316                 }
317                 else if(input.tag.title_is("HORIZONTAL")) {
318                         config.int_horizontal = 1;
319                 }
320                 else if(input.tag.title_is("INPAINTING")) {
321                         config.inpainting = 1;
322                 }
323         }
324 }
325