no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / posterize / posterize.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2021 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "clip.h"
23 #include "filexml.h"
24 #include "language.h"
25 #include "posterize.h"
26 #include "theme.h"
27
28 #include <stdio.h>
29 #include <string.h>
30
31
32 REGISTER_PLUGIN(PosterizeMain)
33
34
35 #define MIN_STEPS 1
36 #define MAX_STEPS 255
37
38 PosterizeConfig::PosterizeConfig()
39 {
40         steps = 255;
41 }
42
43 int PosterizeConfig::equivalent(PosterizeConfig &that)
44 {
45     return steps == that.steps;
46 }
47
48 void PosterizeConfig::copy_from(PosterizeConfig &that)
49 {
50     steps = that.steps;
51 }
52
53 void PosterizeConfig::boundaries()
54 {
55     CLAMP(steps, MIN_STEPS, MAX_STEPS);
56 }
57
58
59 void PosterizeConfig::interpolate(PosterizeConfig &prev, 
60         PosterizeConfig &next, 
61         int64_t prev_frame, 
62         int64_t next_frame, 
63         int64_t current_frame)
64 {
65         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
66         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
67         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale);
68         boundaries();
69 }
70
71 PosterizeMain::PosterizeMain(PluginServer *server)
72  : PluginVClient(server)
73 {
74         
75 }
76
77 PosterizeMain::~PosterizeMain()
78 {
79         
80 }
81
82 const char* PosterizeMain::plugin_title() { return N_("Posterize"); }
83 int PosterizeMain::is_realtime() { return 1; }
84
85 NEW_WINDOW_MACRO(PosterizeMain, PosterizeWindow)
86 LOAD_CONFIGURATION_MACRO(PosterizeMain, PosterizeConfig)
87
88
89 void PosterizeMain::update_gui()
90 {
91         if(thread)
92         {
93                 if(load_configuration())
94         {
95                 thread->window->lock_window();
96             ((PosterizeWindow*)thread->window)->update(1, 1);
97                 thread->window->unlock_window();
98         }
99         }
100 }
101
102
103
104 void PosterizeMain::save_data(KeyFrame *keyframe)
105 {
106         FileXML output;
107
108 // cause data to be stored directly in text
109         output.set_shared_output(keyframe->xbuf);
110         output.tag.set_title("POSTERIZE");
111         output.tag.set_property("STEPS", config.steps);
112         output.append_tag();
113         output.tag.set_title("/POSTERIZE");
114         output.append_tag();
115         output.append_newline();
116         output.terminate_string();
117 }
118
119 void PosterizeMain::read_data(KeyFrame *keyframe)
120 {
121         FileXML input;
122
123         input.set_shared_input(keyframe->xbuf);
124
125         int result = 0;
126
127         while(!result)
128         {
129                 result = input.read_tag();
130
131                 if(!result)
132                 {
133                         if(input.tag.title_is("POSTERIZE"))
134                         {
135                                 config.steps = input.tag.get_property("STEPS", config.steps);
136                         }
137                 }
138         }
139     
140         config.boundaries();
141 }
142
143
144 #define PROCESS(type, components, yuv) \
145 { \
146         for(int i = 0; i < h; i++) \
147         { \
148                 type *in_row = (type*)frame->get_rows()[i]; \
149                 type *out_row = (type*)frame->get_rows()[i]; \
150  \
151         for(int j = 0; j < w; j++) \
152         { \
153             if(sizeof(type) == 4) \
154             { \
155                 out_row[j * components + 0] = (int)(in_row[j * components + 0] / division) * division; \
156                 out_row[j * components + 1] = (int)(in_row[j * components + 1] / division) * division; \
157                 out_row[j * components + 2] = (int)(in_row[j * components + 2] / division) * division; \
158             } \
159             else \
160             { \
161                 out_row[j * components + 0] = table_r[(int)in_row[j * components + 0]]; \
162                 out_row[j * components + 1] = table_g[(int)in_row[j * components + 1]]; \
163                 out_row[j * components + 2] = table_b[(int)in_row[j * components + 2]]; \
164             } \
165         } \
166         } \
167 }
168
169
170 int PosterizeMain::process_buffer(VFrame *frame,
171         int64_t start_position,
172         double frame_rate)
173 {
174         load_configuration();
175
176         read_frame(frame, 
177                 0, 
178                 start_position, 
179                 frame_rate,
180                 0);
181
182         int w = frame->get_w();
183         int h = frame->get_h();
184
185     int table_r[256];
186     int table_g[256];
187     int table_b[256];
188     float division = (float)255 / config.steps;
189     for(int i = 0; i < 256; i++)
190     {
191 // luma/red
192         table_r[i] = (int)(i / division) * division;
193 //printf("PosterizeMain::process_buffer %d i=%d %d\n", __LINE__, i, table_r[i]);
194 //         if(BC_CModels::is_yuv(frame->get_color_model()))
195 //         {
196 //             table_g[i] = (int)(i / division) * division;
197 //             table_b[i] = (int)(i / division) * division;
198 //         }
199 //         else
200 //         {
201             table_g[i] = table_r[i];
202             table_b[i] = table_r[i];
203 //        }
204     }
205
206
207
208         switch(frame->get_color_model())
209         {
210                 case BC_YUV888:
211                         PROCESS(unsigned char, 3, 1);
212                         break;
213                 case BC_YUVA8888:
214                         PROCESS(unsigned char, 4, 1);
215                         break;
216                 case BC_RGB888:
217                         PROCESS(unsigned char, 3, 0);
218                         break;
219                 case BC_RGBA8888:
220                         PROCESS(unsigned char, 4, 0);
221                         break;
222                 case BC_RGB_FLOAT:
223             division = (float)1 / config.steps;
224                         PROCESS(float, 3, 0);
225                         break;
226                 case BC_RGBA_FLOAT:
227             division = (float)1 / config.steps;
228                         PROCESS(float, 4, 0);
229                         break;
230         }
231
232         return 0;
233 }
234
235
236
237 PosterizeText::PosterizeText(PosterizeMain *plugin,
238     PosterizeWindow *gui,
239     int x,
240     int y,
241     int w,
242     int *output)
243  : BC_TextBox(x, y, w, 1, (int64_t)*output, 1, MEDIUMFONT)
244 {
245     this->plugin = plugin;
246     this->gui = gui;
247         this->output = output;
248 }
249
250 int PosterizeText::handle_event()
251 {
252     *output = atoi(get_text());
253     gui->update(1, 0);
254     plugin->send_configure_change();
255     return 1;
256 }
257
258
259
260
261 PosterizeSlider::PosterizeSlider(PosterizeMain *plugin,
262     PosterizeWindow *gui,
263         int x,
264         int y,
265     int w,
266         int *output,
267         int min,
268         int max)
269  : BC_ISlider(x, y, 0, w, w, min, max, *output)
270 {
271         this->plugin = plugin;
272     this->gui = gui;
273         this->output = output;
274 }
275 int PosterizeSlider::handle_event()
276 {
277         *output = get_value();
278     gui->update(0, 1);
279         plugin->send_configure_change();
280         return 1;
281 }
282
283
284
285
286
287 PosterizeWindow::PosterizeWindow(PosterizeMain *plugin)
288  : PluginClientWindow(plugin,
289         xS(300),
290         yS(100),
291         xS(300),
292         yS(100),
293         0)
294
295         this->plugin = plugin; 
296 }
297
298 PosterizeWindow::~PosterizeWindow()
299 {
300 }
301
302 void PosterizeWindow::create_objects()
303 {
304     int text_w = xS(100);
305         int margin = client->get_theme()->widget_border;
306         int x = margin, y = margin;
307     BC_Title *title;
308         add_tool(title = new BC_Title(x, y, _("Steps per channel:")));
309     y += title->get_h() + margin;
310     add_tool(slider = new PosterizeSlider(plugin,
311         this,
312             x,
313             y,
314         get_w() - text_w - margin - margin - margin,
315             &plugin->config.steps,
316             MIN_STEPS,
317             MAX_STEPS));
318     x += slider->get_w() + margin;
319     add_tool(text = new PosterizeText(plugin,
320         this,
321         x,
322         y,
323         text_w,
324         &plugin->config.steps));
325
326         show_window();
327         flush();
328 }
329
330 void PosterizeWindow::update(int do_slider, int do_text)
331 {
332         if(do_text) text->update((int64_t)plugin->config.steps);
333         if(do_slider) slider->update(plugin->config.steps);
334 }
335
336
337
338
339