c57bfc0bda5888c22f69b0663bafa8f90135c9e5
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / sketcher / sketcher.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * 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,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include<stdio.h>
22 #include<stdint.h>
23 #include<math.h>
24 #include<string.h>
25
26 #include "arraylist.h"
27 #include "bccmodels.h"
28 #include "bccolors.h"
29 #include "clip.h"
30 #include "edl.h"
31 #include "edlsession.h"
32 #include "filexml.h"
33 #include "keyframes.h"
34 #include "overlayframe.h"
35 #include "pluginserver.h"
36 #include "preferences.h"
37 #include "sketcher.h"
38 #include "sketcherwindow.h"
39 #include "transportque.inc"
40 #include "language.h"
41 #include "vframe.h"
42
43 void SketcherPoint::init(int id, int arc, coord x, coord y)
44 {
45         this->id = id;  this->arc = arc;
46         this->x = x;    this->y = y;
47 }
48 SketcherPoint::SketcherPoint(int id)
49 {
50         init(id, ARC_LINE, 0, 0);
51 }
52 SketcherPoint::SketcherPoint(int id, int arc, coord x, coord y)
53 {
54         init(id, arc, x, y);
55 }
56 SketcherPoint::~SketcherPoint()
57 {
58 }
59 SketcherPoint::SketcherPoint(SketcherPoint &pt)
60 {
61         copy_from(pt);
62 }
63 int SketcherPoint::equivalent(SketcherPoint &that)
64 {
65         return this->id == that.id &&
66                 this->arc == that.arc &&
67                 EQUIV(this->x, that.x) &&
68                 EQUIV(this->y, that.y) ? 1 : 0;
69 }
70 void SketcherPoint::copy_from(SketcherPoint &that)
71 {
72         this->id = that.id;  this->arc = that.arc;
73         this->x = that.x;    this->y = that.y;
74 }
75 void SketcherPoint::save_data(FileXML &output)
76 {
77         char point[BCSTRLEN];
78         sprintf(point,"/POINT_%d",id);
79         output.tag.set_title(point+1);
80         output.tag.set_property("TYPE", arc);
81         output.tag.set_property("X", x);
82         output.tag.set_property("Y", y);
83         output.append_tag();
84         output.tag.set_title(point+0);
85         output.append_tag();
86         output.append_newline();
87 }
88 void SketcherPoint::read_data(FileXML &input)
89 {
90         id = atoi(input.tag.get_title() + 6);
91         arc = input.tag.get_property("TYPE", ARC_OFF);
92         x = input.tag.get_property("X", (coord)0);
93         y = input.tag.get_property("Y", (coord)0);
94         bclamp(arc, 0, ARC_SZ-1);
95 }
96
97 void SketcherCurve::init(int id, int pen, int width, int color)
98 {
99         this->id = id;
100         this->width = width;
101         this->pen = pen;
102         this->color = color;
103 }
104 SketcherCurve::SketcherCurve(int id)
105 {
106         init(id, 1, PEN_SQUARE, CV_COLOR);
107 }
108 SketcherCurve::SketcherCurve(int id, int pen, int width, int color)
109 {
110         init(id, pen, width, color);
111 }
112 SketcherCurve::~SketcherCurve()
113 {
114 }
115 SketcherCurve::SketcherCurve(SketcherCurve &cv)
116 {
117         copy_from(cv);
118 }
119 int SketcherCurve::equivalent(SketcherCurve &that)
120 {
121         if( this->id != that.id ) return 0;
122         if( this->pen != that.pen ) return 0;
123         if( this->width != that.width ) return 0;
124         if( this->color != that.color ) return 0;
125         int n = this->points.size();
126         if( n != that.points.size() ) return 0;
127         for( int i=0; i<n; ++i ) {
128                 if( !points[i]->equivalent(*that.points[i]) ) return 0;
129         }
130         return 1;
131 }
132 void SketcherCurve::copy_from(SketcherCurve &that)
133 {
134         this->id = that.id;
135         this->pen = that.pen;
136         this->width = that.width;
137         this->color = that.color;
138         int m = points.size(), n = that.points.size();
139         while( m > n ) points.remove_object_number(--m);
140         while( m < n ) { points.append(new SketcherPoint());  ++m; }
141         for( int i=0; i<n; ++i ) points[i]->copy_from(*that.points[i]);
142 }
143 void SketcherCurve::save_data(FileXML &output)
144 {
145         char curve[BCSTRLEN];
146         sprintf(curve,"/CURVE_%d",id);
147         output.tag.set_title(curve+1);
148         output.tag.set_property("PEN", pen);
149         output.tag.set_property("RADIUS", width);
150         output.tag.set_property("COLOR", color);
151         output.append_tag();
152         output.append_newline();
153         for( int i=0,n=points.size(); i<n; ++i )
154                 points[i]->save_data(output);
155         output.tag.set_title(curve+0);
156         output.append_tag();
157         output.append_newline();
158 }
159 void SketcherCurve::read_data(FileXML &input)
160 {
161         id = atoi(input.tag.get_title() + 6);
162         pen = input.tag.get_property("PEN", PEN_OFF);
163         width = input.tag.get_property("RADIUS", 1.);
164         color = input.tag.get_property("COLOR", CV_COLOR);
165         bclamp(pen, 0, PEN_SZ-1);
166 }
167
168 int Sketcher::new_curve(int pen, int width, int color)
169 {
170         SketcherCurves &curves = config.curves;
171         int k = curves.size(), id = 1;
172         for( int i=k; --i>=0; ) {
173                 int n = config.curves[i]->id;
174                 if( n >= id ) id = n + 1;
175         }
176         SketcherCurve *cv = new SketcherCurve(id, pen, width, color);
177         curves.append(cv);
178         config.cv_selected = k;
179         return k;
180 }
181
182 int Sketcher::new_curve()
183 {
184         return new_curve(PEN_XLANT, 1, CV_COLOR);
185 }
186
187 int Sketcher::new_point(SketcherCurve *cv, int arc, coord x, coord y, int idx)
188 {
189         int id = 1;
190         for( int i=cv->points.size(); --i>=0; ) {
191                 int n = cv->points[i]->id;
192                 if( n >= id ) id = n + 1;
193         }
194         SketcherPoint *pt = new SketcherPoint(id, arc, x, y);
195         int n = cv->points.size();
196         if( idx < 0 || idx > n ) idx = n;
197         cv->points.insert(pt, idx);
198         return idx;
199 }
200
201 int Sketcher::new_point(int idx, int arc)
202 {
203         int ci = config.cv_selected;
204         if( ci < 0 || ci >= config.curves.size() )
205                 return -1;
206         SketcherCurve *cv = config.curves[ci];
207         EDLSession *session = get_edl()->session;
208         coord x = !session ? 0.f : session->output_w / 2.f;
209         coord y = !session ? 0.f : session->output_h / 2.f;
210         return new_point(cv, arc, x, y, idx);
211 }
212
213 double SketcherCurve::nearest_point(int &pi, coord x, coord y)
214 {
215         pi = -1;
216         double dist = DBL_MAX;
217         for( int i=0; i<points.size(); ++i ) {
218                 SketcherPoint *p = points[i];
219                 double d = DISTANCE(x,y, p->x,p->y);
220                 if( d < dist ) { dist = d;  pi = i;  }
221         }
222         return pi >= 0 ? dist : -1.;
223 }
224
225 double SketcherConfig::nearest_point(int &ci, int &pi, coord x, coord y)
226 {
227         double dist = DBL_MAX;
228         ci = -1;  pi = -1;
229         for( int i=0; i<curves.size(); ++i ) {
230                 SketcherCurve *crv = curves[i];
231                 SketcherPoints &points = crv->points;
232                 for( int k=0; k<points.size(); ++k ) {
233                         SketcherPoint *p = points[k];
234                         double d = DISTANCE(x,y, p->x,p->y);
235                         if( d < dist ) {  dist = d; ci = i;  pi = k; }
236                 }
237         }
238         return pi >= 0 ? dist : -1.;
239 }
240
241
242 REGISTER_PLUGIN(Sketcher)
243
244 SketcherConfig::SketcherConfig()
245 {
246         drag = 1;
247         aliasing = 0;
248         cv_selected = 0;
249         pt_selected = 0;
250 }
251 SketcherConfig::~SketcherConfig()
252 {
253 }
254
255 int SketcherConfig::equivalent(SketcherConfig &that)
256 {
257         if( this->drag != that.drag ) return 0;
258         if( this->aliasing != that.aliasing ) return 0;
259         if( this->cv_selected != that.cv_selected ) return 0;
260         if( this->pt_selected != that.pt_selected ) return 0;
261         if( this->curves.size() != that.curves.size() ) return 0;
262         for( int i=0, n=curves.size(); i<n; ++i ) {
263                 if( !curves[i]->equivalent(*that.curves[i]) ) return 0;
264         }
265         return 1;
266 }
267
268 void SketcherConfig::copy_from(SketcherConfig &that)
269 {
270         this->drag = that.drag;
271         this->aliasing = that.aliasing;
272         this->cv_selected = that.cv_selected;
273         this->pt_selected = that.pt_selected;
274         int m = curves.size(), n = that.curves.size();
275         while( m > n ) curves.remove_object_number(--m);
276         while( m < n ) { curves.append(new SketcherCurve());  ++m; }
277         for( int i=0; i<n; ++i ) curves[i]->copy_from(*that.curves[i]);
278 }
279
280 void SketcherConfig::interpolate(SketcherConfig &prev, SketcherConfig &next,
281                 long prev_frame, long next_frame, long current_frame)
282 {
283         this->cv_selected = prev.cv_selected;
284         this->pt_selected = prev.pt_selected;
285         this->drag = prev.drag;
286         this->aliasing = prev.aliasing;
287
288         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
289         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
290
291         curves.remove_all_objects();
292         int prev_cv_sz = prev.curves.size();
293         int next_cv_sz = next.curves.size();
294         for( int i=0; i<prev_cv_sz; ++i ) {
295                 SketcherCurve *pcv = prev.curves[i], *ncv = 0;
296                 SketcherCurve *cv = curves.append(new SketcherCurve());
297                 int k = next_cv_sz;  // associated by id in next
298                 while( --k >= 0 && pcv->id != (ncv=next.curves[k])->id );
299                 if( k >= 0 ) {
300                         cv->id = pcv->id;
301                         cv->pen = pcv->pen;
302                         cv->width = pcv->width == ncv->width ? pcv->width :
303                                 pcv->width*prev_scale + ncv->width*next_scale + 0.5;
304                         int pr =  (pcv->color>>16)&0xff, nr =  (ncv->color>>16)&0xff;
305                         int pg =  (pcv->color>> 8)&0xff, ng =  (ncv->color>> 8)&0xff;
306                         int pb =  (pcv->color>> 0)&0xff, nb =  (ncv->color>> 0)&0xff;
307                         int pa = (~pcv->color>>24)&0xff, na = (~ncv->color>>24)&0xff;
308                         int r = pr == nr ? pr : pr*prev_scale + nr*next_scale + 0.5;
309                         int g = pg == ng ? pg : pg*prev_scale + ng*next_scale + 0.5;
310                         int b = pb == nb ? pb : pb*prev_scale + nb*next_scale + 0.5;
311                         int a = pa == na ? pa : pa*prev_scale + na*next_scale + 0.5;
312                         bclamp(r,0,255); bclamp(g,0,255); bclamp(b,0,255); bclamp(a,0,255);
313                         cv->color = (~a<<24) | (r<<16) | (g<<8) | (b<<0);
314                         int prev_pt_sz = pcv->points.size(), next_pt_sz = ncv->points.size();
315                         for( int j=0; j<prev_pt_sz; ++j ) {
316                                 SketcherPoint &pt = *pcv->points[j], *nt = 0;
317                                 k = next_pt_sz;  // associated by id in next
318                                 while( --k >= 0 && pt.id != (nt=ncv->points[k])->id );
319                                 coord x = pt.x, y = pt.y;
320                                 if( k >= 0 ) {
321                                         if( x != nt->x )
322                                                 x = x * prev_scale + nt->x * next_scale;
323                                         if( y != nt->y )
324                                                 y = y * prev_scale + nt->y * next_scale;
325                                 }
326                                 cv->points.append(new SketcherPoint(pt.id, pt.arc, x, y));
327                         }
328                 }
329                 else
330                         cv->copy_from(*pcv);
331         }
332 }
333
334 void SketcherConfig::limits()
335 {
336 }
337
338
339 Sketcher::Sketcher(PluginServer *server)
340  : PluginVClient(server)
341 {
342         img = 0;
343         out = 0;
344         overlay_frame = 0;
345 }
346
347 Sketcher::~Sketcher()
348 {
349         delete img;
350         delete out;
351         delete overlay_frame;
352 }
353
354 const char* Sketcher::plugin_title() { return N_("Sketcher"); }
355 int Sketcher::is_realtime() { return 1; }
356 int Sketcher::is_synthesis() { return 1; }
357
358 NEW_WINDOW_MACRO(Sketcher, SketcherWindow);
359 LOAD_CONFIGURATION_MACRO(Sketcher, SketcherConfig)
360
361 void SketcherConfig::save_data(KeyFrame *keyframe)
362 {
363         FileXML output;
364 // cause data to be stored directly in text
365         output.set_shared_output(keyframe->xbuf);
366
367         output.tag.set_title("SKETCHER");
368         output.tag.set_property("DRAG", drag);
369         output.tag.set_property("ALIASING", aliasing);
370         output.tag.set_property("CV_SELECTED", cv_selected);
371         output.tag.set_property("PT_SELECTED", pt_selected);
372         output.append_tag();
373         output.append_newline();
374         for( int i=0,n=curves.size(); i<n; ++i ) {
375                 curves[i]->save_data(output);
376         }
377         output.tag.set_title("/SKETCHER");
378         output.append_tag();
379         output.append_newline();
380         output.terminate_string();
381 }
382
383 void Sketcher::save_data(KeyFrame *keyframe)
384 {
385         config.save_data(keyframe);
386 }
387
388 void SketcherConfig::read_data(KeyFrame *keyframe)
389 {
390         FileXML input;
391         input.set_shared_input(keyframe->xbuf);
392         curves.remove_all_objects();
393         int result = 0;
394         SketcherCurve *cv = 0;
395
396         while( !(result=input.read_tag()) ) {
397                 if( input.tag.title_is("SKETCHER") ) {
398                         drag = input.tag.get_property("DRAG", drag);
399                         aliasing = input.tag.get_property("ALIASING", aliasing);
400                         cv_selected = input.tag.get_property("CV_SELECTED", 0);
401                         pt_selected = input.tag.get_property("PT_SELECTED", 0);
402                 }
403                 else if( !strncmp(input.tag.get_title(),"CURVE_",6) ) {
404                         cv = new SketcherCurve();
405                         cv->read_data(input);
406                         curves.append(cv);
407                 }
408                 else if( !strncmp(input.tag.get_title(),"/CURVE_",7) )
409                         cv = 0;
410                 else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
411                         if( cv ) {
412                                 SketcherPoint *pt = new SketcherPoint();
413                                 pt->read_data(input);
414                                 cv->points.append(pt);
415                         }
416                         else
417                                 printf("SketcherConfig::read_data: no curve for point\n");
418                 }
419         }
420
421         limits();
422 }
423
424 void Sketcher::read_data(KeyFrame *keyframe)
425 {
426         config.read_data(keyframe);
427         if( !config.curves.size() )
428                 new_curve();
429 }
430
431
432 void SketcherPoint::update_parameter(SketcherPoint *prev, SketcherPoint *src)
433 {
434         if( prev->arc != src->arc ) arc = src->arc;
435         if( prev->x != src->x ) x = src->x;
436         if( prev->y != src->y ) y = src->y;
437 }
438
439 void SketcherCurve::update_parameter(SketcherCurve *prev, SketcherCurve *src)
440 {
441         if( prev->pen != src->pen ) pen = src->pen;
442         if( prev->width != src->width ) width = src->width;
443         if( prev->color != src->color ) color = src->color;
444         int prev_points = prev->points.size();
445         int src_points = src->points.size();
446         int dst_points = this->points.size();
447         int npoints = bmin(prev_points, bmin(src_points, dst_points));
448         for( int i=0; i<npoints; ++i ) {
449                 SketcherPoint *prev_point = prev->points[i];
450                 SketcherPoint *src_point = src->points[i];
451                 SketcherPoint *dst_point = this->points[i];
452                 dst_point->update_parameter(prev_point, src_point);
453         }
454 }
455
456 void Sketcher::span_keyframes(KeyFrame *src, int64_t start, int64_t end)
457 {
458         SketcherConfig src_config;
459         src_config.read_data(src);
460         KeyFrames *keyframes = (KeyFrames *)src->autos;
461         KeyFrame *prev = keyframes->get_prev_keyframe(start, PLAY_FORWARD);
462         SketcherConfig prev_config;
463         prev_config.read_data(prev);
464 // Always update the first one
465         update_parameter(prev_config, src_config, prev);
466         KeyFrame *curr = (KeyFrame*)prev->next;
467         while( curr && curr->position < end ) {
468                 update_parameter(prev_config, src_config, curr);
469                 curr = (KeyFrame*)curr->next;
470         }
471 }
472
473 void Sketcher::update_parameter(SketcherConfig &prev_config, SketcherConfig &src_config,
474                 KeyFrame *keyframe)
475 {
476         SketcherConfig dst_config;
477         dst_config.read_data(keyframe);
478         if( prev_config.drag != src_config.drag )
479                 dst_config.drag = src_config.drag;
480         if( prev_config.aliasing != src_config.aliasing )
481                 dst_config.aliasing = src_config.aliasing;
482         if( prev_config.cv_selected != src_config.cv_selected )
483                 dst_config.cv_selected = src_config.cv_selected;
484         if( prev_config.pt_selected != src_config.pt_selected )
485                 dst_config.pt_selected = src_config.pt_selected;
486         int src_curves = src_config.curves.size();
487         int dst_curves = dst_config.curves.size();
488         int prev_curves = prev_config.curves.size();
489         int ncurves = bmin(prev_curves, bmin(src_curves, dst_curves));
490         for( int i=0; i<ncurves; ++i ) {
491                 SketcherCurve *prev_curve = prev_config.curves[i];
492                 SketcherCurve *src_curve = src_config.curves[i];
493                 SketcherCurve *dst_curve = dst_config.curves[i];
494                 dst_curve->update_parameter(prev_curve, src_curve);
495         }
496         dst_config.save_data(keyframe);
497 }
498
499 void Sketcher::update_gui()
500 {
501         if( !thread ) return;
502         thread->window->lock_window("Sketcher::update_gui");
503         if( load_configuration() ) {
504                 SketcherWindow *window = (SketcherWindow*)thread->window;
505                 window->update_gui();
506                 window->flush();
507         }
508         thread->window->unlock_window();
509 }
510
511 void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d)
512 {
513         int r = d/2+1, x = pt->x, y = pt->y;
514         vfrm->set_pixel_color(color);
515         vfrm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
516         vfrm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
517         vfrm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
518         vfrm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
519         vfrm->draw_x(pt->x, pt->y, d);
520 }
521 void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color)
522 {
523         draw_point(vfrm, pt, color, bmax(w,h)/200 + 2);
524 }
525
526
527 int SketcherVPen::draw_mask(int x, int y)
528 {
529         int w = vfrm->get_w(), h = vfrm->get_h();
530         if( x < 0 || x >= w ) return 1;
531         if( y < 0 || y >= h ) return 1;
532         msk[w * y + x] = 0xff;
533         return 0;
534 }
535
536 int SketcherVPen::draw_pixel(float x, float y, float frac, int axis)
537 {
538         draw_mask(x, y);
539         return VFrame::draw_pixel(x, y, frac, axis);
540 }
541
542 int SketcherPenSquare::draw_pixel(float x, float y, float a)
543 {
544         vfrm->set_draw_alpha(a);
545         vfrm->draw_line(x-n, y, x+n, y);
546         for( int i=-n; i<n; ++i )
547                 vfrm->draw_line(x-n, y+i, x+n, y+i);
548         return 0;
549 }
550 int SketcherPenPlus::draw_pixel(float x, float y, float a)
551 {
552         vfrm->set_draw_alpha(a);
553         if( n > 1 ) {
554                 vfrm->draw_line(x-n, y, x+n, y);
555                 vfrm->draw_line(x, y-n, x, y+n);
556         }
557         else
558                 vfrm->draw_pixel(x, y);
559         return 0;
560 }
561 int SketcherPenSlant::draw_pixel(float x, float y, float a)
562 {
563         vfrm->set_draw_alpha(a);
564         vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
565         vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
566         vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
567         return 0;
568 }
569 int SketcherPenXlant::draw_pixel(float x, float y, float a)
570 {
571         vfrm->set_draw_alpha(a);
572         vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
573         vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
574         vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
575         vfrm->draw_line(x-n,   y-n,   x+n,   y+n);
576         vfrm->draw_line(x-n+1, y-n,   x+n+1, y+n);
577         vfrm->draw_line(x-n,   y-n+1, x+n,   y-n+1);
578         return 0;
579 }
580
581
582 SketcherVPen *SketcherCurve::new_vpen(VFrame *out)
583 {
584         switch( pen ) {
585         case PEN_SQUARE: return new SketcherPenSquare(out, width);
586         case PEN_PLUS:   return new SketcherPenPlus(out, width);
587         case PEN_SLANT:  return new SketcherPenSlant(out, width);
588         case PEN_XLANT:  return new SketcherPenXlant(out, width);
589         }
590         return 0;
591 }
592
593 static int intersects_at(float &x, float &y,
594                 float ax,float ay, float bx, float by, float cx,float cy,  // line slope ab thru c
595                 float dx,float dy, float ex, float ey, float fx,float fy, // line slope de thru f
596                 float mx=0)
597 {
598         float badx = bx - ax, bady = by - ay;
599         float eddx = ex - dx, eddy = ey - dy;
600         float d = badx*eddy - bady*eddx;
601         int ret = 0;
602         if( fabsf(d) < 1 ) { ret = 1;  d = signbit(d) ? -1 : 1; }
603         x = (badx*cy*eddx - badx*eddx*fy + badx*eddy*fx - bady*cx*eddx) / d;
604         y = (badx*cy*eddy - bady*cx*eddy - bady*eddx*fy + bady*eddy*fx) / d;
605         if( mx > 0 ) { bclamp(x, -mx,mx);  bclamp(y, -mx,mx); }
606         return ret;
607 }
608
609 static void smooth_axy(float &ax, float &ay,
610         float bx, float by, float cx, float cy, float dx, float dy)
611 {
612 //middle of bd reflected around ctr
613 // point ctr = (b+d)/2, dv=c-ctr, a=ctr-dv;
614         float xc = (bx+dx)*.5f, yc = (by+dy)*.5f;
615         float xd = cx - xc, yd = cy - yc;
616         ax = xc - xd;  ay = yc - yd;
617 }
618 static void smooth_dxy(float &dx, float &dy,
619         float ax, float ay, float bx, float by, float cx, float cy)
620 {
621 //middle of ac reflected around ctr
622 // point ctr = (a+c)/2, dv=c-ctr, d=ctr-dv;
623         float xc = (ax+cx)*.5f, yc = (ay+cy)*.5f;
624         float xd = bx - xc, yd = by - yc;
625         dx = xc - xd;  dy = yc - yd;
626 }
627
628 static int convex(float ax,float ay, float bx,float by,
629                   float cx,float cy, float dx,float dy)
630 {
631         float abdx = bx-ax, abdy = by-ay;
632         float acdx = cx-ax, acdy = cy-ay;
633         float bcdx = cx-bx, bcdy = cy-by;
634         float bddx = dx-bx, bddy = dy-by;
635         float abc = abdx*acdy - abdy*acdx;
636         float bcd = bcdx*bddy - bcdy*bddx;
637         float v = abc * bcd;
638         return !v ? 0 : v>0 ? 1 : -1;
639 }
640
641 class FillRegion
642 {
643         class segment { public: int y, lt, rt; };
644         ArrayList<segment> stack;
645
646         void push(int y, int lt, int rt) {
647                 segment &seg = stack.append();
648                 seg.y = y;  seg.lt = lt;  seg.rt = rt;
649         }
650         void pop(int &y, int &lt, int &rt) {
651                 segment &seg = stack.last();
652                 y = seg.y;  lt = seg.lt;  rt = seg.rt;
653                 stack.remove();
654         }
655  
656         VFrame *img;
657         uint8_t *msk;
658         int w, h, nxt;
659         SketcherPoints &points;
660 public:
661         SketcherPoint *next();
662         bool exists() { return stack.size() > 0; }
663         void draw_pixel(int x, int y) { img->draw_pixel(x, y); }
664         void start_at(int x, int y);
665         void run();
666         FillRegion(SketcherPoints &pts, SketcherVPen *vpen);
667         ~FillRegion();
668 };
669
670 FillRegion::FillRegion(SketcherPoints &pts, SketcherVPen *vpen)
671  : points(pts)
672 {
673         this->img = vpen->vfrm;
674         this->msk = vpen->msk;
675         this->w = img->get_w();
676         this->h = img->get_h();
677         nxt = 0;
678 }
679 FillRegion::~FillRegion()
680 {
681 }
682
683 void FillRegion::start_at(int x, int y)
684 {
685         bclamp(x, 0, w-1);
686         bclamp(y, 0, h-1);
687         push(y, x, x);
688 }
689
690 void FillRegion::run()
691 {
692         img->set_draw_alpha(1);
693         while( stack.size() > 0 ) {
694                 int y, ilt, irt;
695                 pop(y, ilt, irt);
696                 int ofs = y*w + ilt;
697                 for( int x=ilt; x<=irt; ++x,++ofs ) {
698                         draw_pixel(x, y);
699                         if( msk[ofs] ) continue;
700                         msk[ofs] = 0xff;
701                         int lt = x, rt = x;
702                         int lofs = ofs;
703                         for( int i=lt; --i>=0; ) {
704                                 draw_pixel(i, y);
705                                 if( msk[--lofs] ) break;
706                                 msk[lofs] = 0xff;  lt = i;
707                         }
708                         int rofs = ofs;
709                         for( int i=rt; ++i< w; ) {
710                                 draw_pixel(i, y);
711                                 if( msk[++rofs] ) break;
712                                 msk[rofs] = 0xff;  rt = i;
713                         }
714                         if( y+1 <  h ) push(y+1, lt, rt);
715                         if( y-1 >= 0 ) push(y-1, lt, rt);
716                 }
717         }
718 }
719
720 SketcherPoint *FillRegion::next()
721 {
722         while( nxt < points.size() ) {
723                 SketcherPoint *pt = points[nxt++];
724                 if( pt->arc == ARC_OFF ) continue;
725                 if( pt->arc != ARC_FILL ) return pt;
726                 start_at(pt->x, pt->y);
727         }
728         return 0;
729 }
730
731 void SketcherCurve::draw(VFrame *img, int alias)
732 {
733         if( !points.size() ) return;
734         img->set_pixel_color(color, (~color>>24)&0xff);
735         const float fmx = 16383;
736         SketcherVPen *vpen = new_vpen(img);
737         vpen->set_draw_flags(alias);
738         FillRegion fill(points, vpen);
739         SketcherPoint *pnt0 = fill.next();
740         SketcherPoint *pnt1 = pnt0 ? fill.next() : 0;
741         SketcherPoint *pnt2 = pnt1 ? fill.next() : 0;
742         if( pnt0 && pnt1 && pnt2 ) {
743                 SketcherPoint *pt0 = pnt0, *pt1 = pnt1, *pt2 = pnt2;
744                 float ax,ay, bx,by, cx,cy, dx,dy, sx,sy;
745                 bx = pt0->x;  by = pt0->y;
746                 cx = pt1->x;  cy = pt1->y;
747                 dx = pt2->x;  dy = pt2->y;
748                 smooth_axy(ax,ay, bx,by, cx,cy, dx,dy);
749                 while( pt2 ) {
750                         dx = pt2->x;  dy = pt2->y;
751                         switch( pt0->arc ) {
752                         case ARC_CURVE:
753                                 if( convex(ax,ay, bx,by, cx,cy, dx,dy) >= 0 ) {
754                                         // s = ac thru b x bd thru c
755                                         intersects_at(sx,sy, ax,ay,cx,cy,bx,by, bx,by,dx,dy,cx,cy,fmx);
756                                         vpen->draw_smooth(bx,by, sx,sy, cx,cy);
757                                         break;
758                                 } // fall thru
759                         case ARC_LINE:
760                                 vpen->draw_line(bx, by, cx, cy);
761                                 break;
762                         }
763                         ax = bx;  ay = by;  pt0 = pt1;
764                         bx = cx;  by = cy;  pt1 = pt2;
765                         cx = dx;  cy = dy;  pt2 = fill.next();
766                 }
767                 switch( pt1->arc ) {
768                 case ARC_LINE:
769                         vpen->draw_line(bx, by, cx, cy);
770                         if( fill.exists() ) {
771                                 dx = pnt0->x;  dy = pnt0->y;
772                                 vpen->draw_line(cx,cy, dx,dy);
773                         }
774                         break;
775                 case ARC_CURVE: {
776                         if( fill.exists() ) {
777                                 dx = pnt0->x;  dy = pnt0->y;
778                                 intersects_at(sx,sy, ax,ay,cx,cy,bx,by, bx,by,dx,dy,cx,cy,fmx);
779                                 vpen->draw_smooth(bx,by, sx,sy, cx,cy);
780                                 ax = bx;  ay = by;
781                                 bx = cx;  by = cy;
782                                 cx = dx;  cy = dy;
783                                 dx = pnt1->x;  dy = pnt1->y;
784                         }
785                         else
786                                 smooth_dxy(dx,dy, ax,ay, bx,by, cx,cy);
787                         intersects_at(sx,sy, ax,ay,cx,cy,bx,by, bx,by,dx,dy,cx,cy,fmx);
788                         vpen->draw_smooth(bx,by, sx,sy, cx,cy);
789                         break; }
790                 }
791                 fill.run();
792         }
793         else if( pnt0 && pnt1 ) {
794                 vpen->draw_line(pnt0->x, pnt0->y, pnt1->x, pnt1->y);
795         }
796         else if( pnt0 ) {
797                 vpen->draw_pixel(pnt0->x, pnt0->y, 1);
798         }
799         delete vpen;
800 }
801
802 int Sketcher::process_realtime(VFrame *input, VFrame *output)
803 {
804         this->input = input;  this->output = output;
805         w = output->get_w();  h = output->get_h();
806
807         load_configuration();
808
809         int out_color_model = output->get_color_model();
810         int color_model = out_color_model;
811         switch( color_model ) { // add alpha if needed
812         case BC_RGB888:         color_model = BC_RGBA8888;      break;
813         case BC_YUV888:         color_model = BC_YUVA8888;      break;
814         case BC_RGB161616:      color_model = BC_RGBA16161616;  break;
815         case BC_YUV161616:      color_model = BC_YUVA16161616;  break;
816         case BC_RGB_FLOAT:      color_model = BC_RGBA_FLOAT;    break;
817         case BC_RGB_FLOATP:     color_model = BC_RGBA_FLOATP;   break;
818         }
819         if( color_model == out_color_model ) {
820                 delete out;  out = output;
821                 if( output != input )
822                         output->transfer_from(input);
823         }
824         else {
825                 VFrame::get_temp(out, w, h, color_model);
826                 out->transfer_from(input);
827         }
828         VFrame::get_temp(img, w, h, color_model);
829         
830         if( !overlay_frame ) {
831                 int cpus = server->preferences->project_smp;
832                 int max = (w*h)/0x80000 + 2;
833                 if( cpus > max ) cpus = max;
834                 overlay_frame = new OverlayFrame(cpus);
835         }
836
837         for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
838                 SketcherCurve *cv = config.curves[ci];
839                 if( cv->pen == PEN_OFF ) continue;
840                 int m = cv->points.size();
841                 if( !m ) continue;
842                 img->clear_frame();
843                 int alias = config.aliasing < 0 ? ALIAS_OFF :
844                         config.aliasing > 0 ? ALIAS_DBL : ALIAS_NRM;
845                 cv->draw(img, alias);
846                 overlay_frame->overlay(out, img, 0,0,w,h, 0,0,w,h,
847                                 1.f, TRANSFER_SRC_OVER, NEAREST_NEIGHBOR);
848         }
849
850         if( config.drag ) {
851                 for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
852                         SketcherCurve *cv = config.curves[ci];
853                         for( int pi=0,m=cv->points.size(); pi<m; ++pi ) {
854                                 int color = pi==config.pt_selected && ci==config.cv_selected ?
855                                         RED : cv->color ; 
856                                 draw_point(out, cv->points[pi], color);
857                         }
858                 }
859         }
860
861         if( output != out )
862                 output->transfer_from(out);
863         else
864                 out = 0;
865
866         return 0;
867 }
868
869 void SketcherPoints::dump()
870 {
871         for( int i=0; i<size(); ++i ) {
872                 SketcherPoint *pt = get(i);
873                 printf("  Pt %d, id=%d, arc=%s, x=%0.1f, y=%0.1f\n",
874                         i, pt->id, pt_type[pt->arc], pt->x, pt->y);
875         }
876 }
877 void SketcherCurves::dump()
878 {
879         for( int i=0; i<size(); ++i ) {
880                 SketcherCurve *cv = get(i);
881                 printf("Curve %d, id=%d, pen=%s, r=%d, color=%02x%02x%02x%02x, %d points\n",
882                         i, cv->id, cv_pen[cv->pen], cv->width, (~cv->color>>24)&0xff,
883                         (cv->color>>16)&0xff, (cv->color>>8)&0xff, (cv->color>>0)&0xff,
884                         cv->points.size());
885                 cv->points.dump();
886         }
887 }
888 void SketcherConfig::dump()
889 {
890         printf("Config drag=%d, cv_selected=%d, pt_selected=%d %d curves\n",
891                         drag, cv_selected, pt_selected, curves.size());
892         curves.dump();
893 }
894