this->title = title;
this->do_alpha = do_alpha;
this->do_okcancel = 0;
- this->output = BLACK;
- this->alpha = 255;
+ this->output = this->orig_color = BLACK;
+ this->alpha = this->orig_alpha = 255;
}
ColorPicker::~ColorPicker()
}
return;
}
+ this->orig_color = output;
+ this->orig_alpha = alpha;
this->output = output;
this->alpha = alpha;
this->do_okcancel = do_okcancel;
void update_gui(int output, int alpha);
BC_Window* new_gui();
+ int orig_color, orig_alpha;
int output, alpha;
int do_alpha, do_okcancel;
const char *title;
lmt = bfr + bsz;
isz = max_size;
destroy = del;
+ share_lock = new Mutex("XMLBuffer::share_lock");
}
XMLBuffer::XMLBuffer(const char *buf, long buf_size, int del)
lmt = inp = bfr+bsz;
isz = bsz;
destroy = del;
+ share_lock = new Mutex("XMLBuffer::share_lock");
}
XMLBuffer::XMLBuffer(long buf_size, char *buf, int del)
lmt = outp = bfr+bsz;
isz = bsz;
destroy = del;
+ share_lock = new Mutex("XMLBuffer::share_lock");
}
XMLBuffer::~XMLBuffer()
{
if( destroy ) delete [] bfr;
+ delete share_lock;
}
int XMLBuffer::demand(long len)
void XMLBuffer::copy_from(XMLBuffer *xbuf)
{
- if( bsz != xbuf->bsz ) { delete [] bfr; bfr = 0; }
- if( !bfr ) bfr = new unsigned char[bsz = xbuf->bsz];
- lmt = bfr + bsz;
- long ilen = xbuf->otell(), olen = xbuf->itell();
- inp = pos(ilen);
- outp = pos(olen);
- if( ilen > 0 )
- memmove(bfr, xbuf->bfr, ilen);
- destroy = xbuf->destroy;
+ xbuf->share_lock->lock("XMLBuffer::copy_from");
+ share_lock->lock("XMLBuffer::copy_from");
+ oseek(0);
+ write((const char*)xbuf->pos(0), xbuf->otell());
+ iseek(xbuf->itell());
+ xbuf->share_lock->unlock();
+ share_lock->unlock();
}
FileXML::~FileXML()
{
if( !shared ) delete buffer;
+ else buffer->share_lock->unlock();
delete [] output;
}
strcpy(this->filename, "");
delete buffer;
buffer = xbuf;
+ xbuf->share_lock->lock("FileXML::set_shared_input");
xbuf->iseek(0);
- set_coding(coded);
shared = 1;
+ set_coding(coded);
return 0;
}
strcpy(this->filename, "");
delete buffer;
buffer = xbuf;
+ xbuf->share_lock->lock("FileXML::set_shared_output");
xbuf->oseek(0);
- set_coding(coded);
shared = 1;
+ set_coding(coded);
return 0;
}
#include <limits.h>
#include "arraylist.h"
+#include "mutex.h"
#include "keyframe.inc"
#include "filexml.inc"
#include "sizes.h"
long bsz, isz;
unsigned char *inp, *outp, *bfr, *lmt;
int destroy;
+ Mutex *share_lock;
int demand(long len);
friend class KeyFrame;
+ friend class FileXML;
public:
XMLBuffer(long buf_size=0x1000, long max_size=LONG_MAX, int del=1);
XMLBuffer(long buf_size, char *buf, int del=0); // writing
xbuf = new XMLBuffer();
}
+KeyFrame::KeyFrame(const char *buf, long len)
+ : Auto()
+{
+ xbuf = new XMLBuffer(buf, len, 0);
+}
+
KeyFrame::KeyFrame(EDL *edl, KeyFrames *autos)
: Auto(edl, (Autos*)autos)
{
KeyFrame &operator =(KeyFrame &k) { return k; } //illegal
public:
KeyFrame();
+ KeyFrame(const char *buf, long len);
KeyFrame(EDL *edl, KeyFrames *autos);
virtual ~KeyFrame();
#include "track.h"
#include "transportque.inc"
-
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
#include <ctype.h>
#include <errno.h>
-#include <string.h>
-
-
-
PluginClientThread::PluginClientThread(PluginClient *client)
using_defaults = 1;
//printf("PluginClient::load_defaults_xml %d %s\n", __LINE__, path);
- KeyFrame temp_keyframe;
- FILE *fp = fopen(path, "r");
- if( fp ) {
- struct stat st; int fd = fileno(fp);
- int64_t sz = !fstat(fd, &st) ? st.st_size : BCTEXTLEN;
- char *data = temp_keyframe.get_data(sz+1);
- int data_size = fread(data, 1, sz, fp);
- if( data_size < 0 ) data_size = 0;
- if( data_size > 0 ) {
- data[data_size] = 0;
- temp_keyframe.xbuf->oseek(data_size);
+ char *data = 0;
+ int64_t len = -1;
+ struct stat st;
+ int fd = open(path, O_RDONLY);
+ if( fd >= 0 && !fstat(fd, &st) ) {
+ int64_t sz = st.st_size;
+ data = new char[sz+1];
+ len = read(fd, data, sz);
+ close(fd);
+ }
+ if( data && len >= 0 ) {
+ data[len] = 0;
// Get window extents
- int i = 0;
- for( int state=0; i<(data_size-8) && state>=0; ++i ) {
- if( !data[i] || data[i] == '<' ) break;
- if( !isdigit(data[i]) ) continue;
- if( !state ) {
- window_x = atoi(data + i);
- state = 1;
- }
- else {
- window_y = atoi(data + i);
- state = -1;
- }
- while( i<data_size && isdigit(data[i]) ) ++i;
+ int i = 0;
+ for( int state=0; i<len && state>=0; ++i ) {
+ if( !data[i] || data[i] == '<' ) break;
+ if( !isdigit(data[i]) ) continue;
+ if( !state ) {
+ window_x = atoi(data+i);
+ state = 1;
+ }
+ else {
+ window_y = atoi(data+i);
+ state = -1;
}
- temp_keyframe.xbuf->iseek(i);
- read_data(&temp_keyframe);
+ while( i<len && isdigit(data[i]) ) ++i;
}
-
- fclose(fp);
+ KeyFrame keyframe(data+i, len-i);
+ read_data(&keyframe);
}
+ delete [] data;
+
using_defaults = 0;
//printf("PluginClient::load_defaults_xml %d %s\n", __LINE__, path);
}
[ . ])
PKG_3RD([ffmpeg],[yes],
- [ffmpeg-4.0],
+ [ffmpeg-4.1],
[ libavutil/libavutil.a \
libavcodec/libavcodec.a \
libpostproc/libpostproc.a \
dbtv: dbtv.C
$(CXX) $(CXXFLAGS) $(LDFLAGS) dbtv.C $(LDLIBS) -lX11 -o $@
-XTV_MEDIA_OBJS := $(TOPDIR)/cinelerra/$(OBJDIR)/mediadb.o $(TOPDIR)/cinelerra/$(OBJDIR)/filexml.o
+XTV_MEDIA_OBJS := $(TOPDIR)/cinelerra/$(OBJDIR)/mediadb.o
+XTV_MEDIA_OBJS += $(TOPDIR)/cinelerra/$(OBJDIR)/filexml.o
+XTV_MEDIA_OBJS += $(TOPDIR)/guicast/$(OBJDIR)/mutex.o
+XTV_MEDIA_OBJS += $(TOPDIR)/guicast/$(OBJDIR)/bctrace.o
XTV_LIBS := $(TOPDIR)/libzmpeg3/$(OBJDIR)/libzmpeg3.a
XTV_LIBS += $(TOPDIR)/db/$(OBJDIR)/db.a
<tr>
<td height="29" align="left"><b><font face="Liberation Serif" size=5>Main Menu</font></b></td>
<td align="left"><font face="Liberation Serif" size=3>(- is a checkbox)</font></td>
- <td align="left" sdnum="1033;0;MM/DD/YY"><font face="Liberation Serif" size=3>06/26/2018 update</font></td>
+ <td align="left" sdnum="1033;0;MM/DD/YY"><font face="Liberation Serif" size=3>11/08/2018 update</font></td>
<td align="left"><font face="Liberation Serif"><br></font></td>
</tr>
<tr>
<td align="left"><font face="Liberation Serif" size=4>Double click</font></td>
<td align="left"><font face="Liberation Serif" size=4>On fade/speed, synch video/audio ganged</font></td>
</tr>
+ <tr>
+ <td height="26" align="left"><font face="Liberation Serif" size=4><br></font></td>
+ <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+ <td align="left"><font face="Liberation Serif" size=4>Double MMB</font></td>
+ <td align="left"><font face="Liberation Serif" size=4>On fade/speed, select keyframe position</font></td>
+ </tr>
<tr>
<td height="26" align="left"><font face="Liberation Serif" size=4><br></font></td>
<td align="left"><font face="Liberation Serif" size=4>'U'</font></td>
<td align="left"><font face="Liberation Serif" size=4><br></font></td>
<td align="left"><font face="Liberation Serif" size=4><br></font></td>
</tr>
+ <tr>
+ <td height="26" align="left"><b><font face="Liberation Serif" size=4> Patchbay</b></font></td>
+ <td align="left"><font face="Liberation Serif" size=4>Shift/Move</font></td>
+ <td align="left"><font face="Liberation Serif" size=4>Hold LMB</font></td>
+ <td align="left"><font face="Liberation Serif" size=4>On Fade slider bar, sets gain to 100% or 0db</font></td>
+ </tr>
+ <tr>
+ <td height="26" align="left"><font face="Liberation Serif" size=4><br></font></td>
+ <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+ <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+ <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+ </tr>
<tr>
<td height="26" align="left"><b><font face="Liberation Serif" size=4>Compositor</font></b></td>
<td align="left"><font face="Liberation Serif" size=4><br></font></td>
; new in 4.0
#acontrast Contrast=33
#afir
-aiir
#convolve
#deconvolve
drmeter
setrange
#unpremultiply
vfrdet
+; new in 4.1
+acue
+#adeclick
+#adeclip
+aderivative
+afftdn
+aintegral
+amplify
+chromahold
+cue
+deblock
+fttdnoiz
+#graphmonitor
+greyedge
+#highshelf
+#lowshelf
+lut1d
+pal75bars
+pal100bars
+setparams
+#sinc
+tmix
+vibrance
+; broken in 4.1
+#acrossover
+#aiir
+#amultiply
+#bm3d
+#sr
+#xstack
+#agraphmonitor
highlighted = popup_down = 0;
menu_popup = 0;
icon = 0;
- if(margin >= 0)
- this->margin = margin;
- else
- this->margin = BC_WindowBase::get_resources()->popupmenu_margin;
-
+ this->margin = margin >= 0 ? margin :
+ BC_WindowBase::get_resources()->popupmenu_margin;
this->use_title = use_title;
strcpy(this->text, text);
for(int i = 0; i < TOTAL_IMAGES; i++)
highlighted = popup_down = 0;
menu_popup = 0;
icon = 0;
+ this->margin = BC_WindowBase::get_resources()->popupmenu_margin;
this->use_title = use_title;
strcpy(this->text, text);
for(int i = 0; i < TOTAL_IMAGES; i++)
BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
- int64_t default_value,
- int64_t min,
- int64_t max,
- int x,
- int y,
- int text_w)
+ int64_t default_value, int64_t min, int64_t max,
+ int x, int y, int text_w)
{
reset();
this->x = x;
}
BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
- int default_value,
- int min,
- int max,
- int x,
- int y,
- int text_w)
+ int default_value, int min, int max,
+ int x, int y, int text_w)
{
reset();
this->x = x;
}
BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
- float default_value_f,
- float min_f,
- float max_f,
- int x,
- int y,
- int text_w)
+ float default_value_f, float min_f, float max_f,
+ int x, int y, int text_w, int precision)
{
reset();
this->x = x;
this->max_f = max_f;
this->default_value_f = default_value_f;
this->text_w = text_w;
+ this->precision = precision;
this->parent_window = parent_window;
use_float = 1;
- precision = 4;
increment = 1;
}
{
public:
BC_TumbleTextBox(BC_WindowBase *parent_window,
- int64_t default_value,
- int64_t min,
- int64_t max,
- int x,
- int y,
- int text_w);
+ int64_t default_value, int64_t min, int64_t max,
+ int x, int y, int text_w);
BC_TumbleTextBox(BC_WindowBase *parent_window,
- int default_value,
- int min,
- int max,
- int x,
- int y,
- int text_w);
+ int default_value, int min, int max,
+ int x, int y, int text_w);
BC_TumbleTextBox(BC_WindowBase *parent_window,
- float default_value,
- float min,
- float max,
- int x,
- int y,
- int text_w);
+ float default_value, float min, float max,
+ int x, int y, int text_w, int precision=4);
virtual ~BC_TumbleTextBox();
int create_objects();
int get_dragging();
wchar_t* get_wkeystring(int *length = 0);
int get_keypress();
+ int get_keysym() { return keysym; }
#ifdef X_HAVE_UTF8_STRING
char* get_keypress_utf8();
#endif
--- /dev/null
+#include "bcsignals.h"
+#include "guicast.h"
+#include "pys_icon_png.h"
+
+class TestList : public BC_ListBox
+{
+public:
+ TestList(int x, int y, int w, int h,
+ ArrayList<BC_ListBoxItem*> *items);
+ int handle_event();
+ int selection_changed();
+};
+
+TestList::TestList(int x, int y, int w, int h,
+ ArrayList<BC_ListBoxItem*> *items)
+ : BC_ListBox(x, y, w, h, LISTBOX_TEXT, items,
+ 0, 0, 1, 0, 0, LISTBOX_SINGLE, ICON_LEFT, 0)
+{
+}
+
+int TestList::handle_event()
+{
+ printf("handle_event\n");
+ return 1;
+}
+
+int TestList::selection_changed()
+{
+ BC_ListBoxItem *item = get_selection(0, 0);
+ printf("selection_changed %s\n", !item ? "<nul>" : item->get_text());
+ return 1;
+}
+
+class TestWindow : public BC_Window
+{
+public:
+ TestWindow() : BC_Window("Test9", 0, 0, 320, 240) {};
+ void create_objects();
+ int keypress_event();
+ BC_ListBox *list;
+ ArrayList<BC_ListBoxItem*> items;
+};
+
+void TestWindow::create_objects()
+{
+ lock_window("AWindowRemovePluginGUI::create_objects");
+ set_color(BLACK);
+ set_font(LARGEFONT);
+ int x = 10, y = 20;
+ draw_text(x, y, "Hello world");
+ y += 25;
+ BC_Button *ok_button = new BC_OKButton(this);
+ add_subwindow(ok_button);
+ BC_Button *cancel_button = new BC_CancelButton(this);
+ add_subwindow(cancel_button);
+ BC_ListBoxItem *thing;
+ ArrayList<BC_ListBoxItem*> *sublist;
+ items.append(thing = new BC_ListBoxItem("thing 1"));
+ VFrame *pys_icon = new VFramePng(pys_icon_png);
+ thing->set_icon_vframe(pys_icon);
+ int pw = pys_icon->get_w(), ph = pys_icon->get_h();
+ BC_Pixmap *pys_picon = new BC_Pixmap(this, pw, ph);
+ pys_picon->draw_vframe(pys_icon, 0, 0, pw, pw, 0, 0);
+ thing->set_icon(pys_picon);
+ sublist = thing->new_sublist(1);
+ BC_ListBoxItem *fish, *cat, *hat;
+ sublist->append(fish = new BC_ListBoxItem("fish"));
+ ArrayList<BC_ListBoxItem*> *fish_list = fish->new_sublist(1);
+ fish_list->append(new BC_ListBoxItem("green"));
+ fish_list->append(new BC_ListBoxItem("eggs"));
+ fish_list->append(new BC_ListBoxItem("ham"));
+ sublist->append(cat = new BC_ListBoxItem("cat"));
+ ArrayList<BC_ListBoxItem*> *cat_list = cat->new_sublist(1);
+ cat_list->append(new BC_ListBoxItem("videos"));
+ sublist->append(hat = new BC_ListBoxItem("hat"));
+ ArrayList<BC_ListBoxItem*> *hat_list = hat->new_sublist(1);
+ hat_list->append(new BC_ListBoxItem("bonnet"));
+ hat_list->append(new BC_ListBoxItem("cap"));
+ hat_list->append(new BC_ListBoxItem("sombrero"));
+ items.append(thing = new BC_ListBoxItem("thing 2"));
+ int lw = get_w()-x-10, lh = ok_button->get_y() - y - 5;
+ add_subwindow(list = new TestList(x, y, lw, lh, &items));
+ show_window();
+ unlock_window();
+}
+
+int TestWindow::keypress_event()
+{
+ switch( get_keypress() ) {
+ case 'v':
+ switch( list->get_format() ) {
+ case LISTBOX_TEXT:
+ list->update_format(LISTBOX_ICONS, 1);
+ break;
+ case LISTBOX_ICONS:
+ list->update_format(LISTBOX_ICONS_PACKED, 1);
+ break;
+ case LISTBOX_ICONS_PACKED:
+ list->update_format(LISTBOX_ICON_LIST, 1);
+ break;
+ case LISTBOX_ICON_LIST:
+ list->update_format(LISTBOX_TEXT, 1);
+ break;
+ }
+ break;
+ }
+ return 1;
+}
+
+int main()
+{
+ new BC_Signals;
+ TestWindow window;
+ window.create_objects();
+ window.run_window();
+}
+
rumbler \
scale \
scaleratio \
+ sketcher \
seltempavg \
shapewipe \
sharpen \
rotate \
rumbler \
scale \
+ sketcher \
shapewipe \
sharpen \
shiftinterlace \
void CriKey::update_gui()
{
if( !thread ) return;
- if( !load_configuration() ) return;
thread->window->lock_window("CriKey::update_gui");
CriKeyWindow *window = (CriKeyWindow*)thread->window;
- window->update_gui();
- window->flush();
+ if( load_configuration() ) {
+ window->update_gui();
+ window->flush();
+ }
thread->window->unlock_window();
}
CriKeyWindow::~CriKeyWindow()
{
+ delete point_x;
+ delete point_y;
}
void CriKeyWindow::create_objects()
pending_config = 0;
plugin->send_configure_change();
}
-int CriKeyWindow::check_configure_change(int ret)
+
+int CriKeyWindow::grab_event(XEvent *event)
{
+ int ret = do_grab_event(event);
if( pending_config && !grab_event_count() )
send_configure_change();
return ret;
}
-int CriKeyWindow::grab_event(XEvent *event)
+int CriKeyWindow::do_grab_event(XEvent *event)
{
switch( event->type ) {
case ButtonPress: break;
case ButtonRelease: break;
case MotionNotify: break;
default:
- return check_configure_change(0);
+ return 0;
}
MWindow *mwindow = plugin->server->mwindow;
if( !dragging ) {
if( cx < 0 || cx >= mwindow->theme->ccanvas_w ||
cy < 0 || cy >= mwindow->theme->ccanvas_h )
- return check_configure_change(0);
+ return 0;
}
switch( event->type ) {
case ButtonPress:
- if( dragging ) return check_configure_change(0);
+ if( dragging ) return 0;
if( event->xbutton.button == WHEEL_UP ) return threshold->wheel_event(1);
if( event->xbutton.button == WHEEL_DOWN ) return threshold->wheel_event(-1);
dragging = event->xbutton.state & ShiftMask ? -1 : 1;
break;
case ButtonRelease:
- if( !dragging ) return check_configure_change(0);
+ if( !dragging ) return 0;
dragging = 0;
return 1;
case MotionNotify:
- if( !dragging ) return check_configure_change(0);
+ if( !dragging ) return 0;
break;
default:
- return check_configure_change(0);
+ return 0;
}
float cursor_x = cx, cursor_y = cy;
if( hot_point >= 0 && sz > 0 ) {
CriKeyPoint *pt = points[hot_point];
point_list->set_point(hot_point, PT_X, pt->x = output_x);
+ point_list->set_point(hot_point, PT_Y, pt->y = output_y);
for( int i=0; i<sz; ++i ) {
pt = points[i];
pt->e = i==hot_point ? !pt->e : 0;
}
last_x = output_x; last_y = output_y;
- if( !grab_event_count() )
- send_configure_change();
- else
- pending_config = 1;
+ pending_config = 1;
return 1;
}
void update_gui();
void start_color_thread();
int grab_event(XEvent *event);
+ int do_grab_event(XEvent *event);
void done_event(int result);
- int check_configure_change(int ret);
void send_configure_change();
CriKey *plugin;
--- /dev/null
+include ../../plugin_defs
+
+OBJS := \
+ $(OBJDIR)/sketcher.o \
+ $(OBJDIR)/sketcherwindow.o
+
+PLUGIN = sketcher
+
+include ../../plugin_config
+
+$(OBJDIR)/sketcher.o: sketcher.C
+$(OBJDIR)/sketcherwindow.o: sketcherwindow.C
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include<stdio.h>
+#include<stdint.h>
+#include<math.h>
+#include<string.h>
+
+#include "arraylist.h"
+#include "bccmodels.h"
+#include "bccolors.h"
+#include "clip.h"
+#include "edlsession.h"
+#include "filexml.h"
+#include "sketcher.h"
+#include "sketcherwindow.h"
+#include "language.h"
+#include "vframe.h"
+
+void SketcherPoint::init(int id, int x, int y)
+{
+ this->id = id;
+ this->x = x; this->y = y;
+}
+SketcherPoint::SketcherPoint(int id)
+{
+ init(id, 0, 0);
+}
+SketcherPoint::SketcherPoint(int id, int x, int y)
+{
+ init(id, x, y);
+}
+SketcherPoint::~SketcherPoint()
+{
+}
+SketcherPoint::SketcherPoint(SketcherPoint &pt)
+{
+ copy_from(pt);
+}
+int SketcherPoint::equivalent(SketcherPoint &that)
+{
+ return this->id == that.id &&
+ this->x == that.x &&
+ this->y == that.y ? 1 : 0;
+}
+void SketcherPoint::copy_from(SketcherPoint &that)
+{
+ this->id = that.id;
+ this->x = that.x; this->y = that.y;
+}
+void SketcherPoint::save_data(FileXML &output)
+{
+ char point[BCSTRLEN];
+ sprintf(point,"/POINT_%d",id);
+ output.tag.set_title(point+1);
+ output.tag.set_property("X", x);
+ output.tag.set_property("Y", y);
+ output.append_tag();
+ output.tag.set_title(point+0);
+ output.append_tag();
+ output.append_newline();
+}
+void SketcherPoint::read_data(FileXML &input)
+{
+ id = atoi(input.tag.get_title() + 6);
+ x = input.tag.get_property("X", 0.f);
+ y = input.tag.get_property("Y", 0.f);
+}
+
+void SketcherCurve::init(int id, int ty, int radius, int pen, int color)
+{
+ this->id = id;
+ this->ty = ty;
+ this->radius = radius;
+ this->pen = pen;
+ this->color = color;
+}
+SketcherCurve::SketcherCurve(int id)
+{
+ init(id, 0, 1, 0, BLACK);
+}
+SketcherCurve::SketcherCurve(int id, int ty, int radius,int pen, int color)
+{
+ init(id, ty, radius, pen, color);
+}
+SketcherCurve::~SketcherCurve()
+{
+}
+SketcherCurve::SketcherCurve(SketcherCurve &cv)
+{
+ copy_from(cv);
+}
+int SketcherCurve::equivalent(SketcherCurve &that)
+{
+ if( this->id != that.id ) return 0;
+ if( this->ty != that.ty ) return 0;
+ if( this->radius != that.radius ) return 0;
+ if( this->pen != that.pen ) return 0;
+ if( this->color != that.color ) return 0;
+ int n = this->points.size();
+ if( n != that.points.size() ) return 0;
+ for( int i=0; i<n; ++i ) {
+ if( !points[i]->equivalent(*that.points[i]) ) return 0;
+ }
+ return 1;
+}
+void SketcherCurve::copy_from(SketcherCurve &that)
+{
+ this->id = that.id;
+ this->ty = that.ty;
+ this->radius = that.radius;
+ this->pen = that.pen;
+ this->color = that.color;
+ int m = points.size(), n = that.points.size();
+ while( m > n ) points.remove_object_number(--m);
+ while( m < n ) { points.append(new SketcherPoint()); ++m; }
+ for( int i=0; i<n; ++i ) points[i]->copy_from(*that.points[i]);
+}
+void SketcherCurve::save_data(FileXML &output)
+{
+ this->ty = ty;
+ this->pen = pen; this->color = color;
+ char curve[BCSTRLEN];
+ sprintf(curve,"/CURVE_%d",id);
+ output.tag.set_title(curve+1);
+ output.tag.set_property("TYPE", ty);
+ output.tag.set_property("RADIUS", radius);
+ output.tag.set_property("PEN", pen);
+ output.tag.set_property("COLOR", color);
+ output.append_tag();
+ output.append_newline();
+ for( int i=0,n=points.size(); i<n; ++i )
+ points[i]->save_data(output);
+ output.tag.set_title(curve+0);
+ output.append_tag();
+ output.append_newline();
+}
+void SketcherCurve::read_data(FileXML &input)
+{
+ id = atoi(input.tag.get_title() + 6);
+ ty = input.tag.get_property("TYPE", 0);
+ radius = input.tag.get_property("RADIUS", 1.);
+ pen = input.tag.get_property("PEN", 0);
+ color = input.tag.get_property("COLOR", BLACK);
+}
+
+int Sketcher::new_curve(int ty, int radius, int pen, int color)
+{
+ SketcherCurves &curves = config.curves;
+ int k = curves.size(), id = 1;
+ for( int i=k; --i>=0; ) {
+ int n = config.curves[i]->id;
+ if( n >= id ) id = n + 1;
+ }
+ SketcherCurve *cv = new SketcherCurve(id, ty, radius, pen, color);
+ curves.append(cv);
+ config.cv_selected = k;
+ return k;
+}
+
+int Sketcher::new_curve()
+{
+ return new_curve(0, 1, 0, BLACK);
+}
+
+int Sketcher::new_point(SketcherCurve *cv, int x, int y)
+{
+ int k = cv->points.size(), id = 1;
+ for( int i=k; --i>=0; ) {
+ int n = cv->points[i]->id;
+ if( n >= id ) id = n + 1;
+ }
+ SketcherPoint *pt = new SketcherPoint(id, x, y);
+ cv->points.append(pt);
+ return k;
+}
+
+int Sketcher::new_point()
+{
+ int ci = config.cv_selected;
+ if( ci < 0 || ci >= config.curves.size() )
+ return -1;
+ SketcherCurve *cv = config.curves[ci];
+ EDLSession *session = get_edlsession();
+ int x = !session ? 0.f : session->output_w / 2.f;
+ int y = !session ? 0.f : session->output_h / 2.f;
+ return new_point(cv, x, y);
+}
+
+REGISTER_PLUGIN(Sketcher)
+
+SketcherConfig::SketcherConfig()
+{
+ drag = 1;
+ cv_selected = 0;
+ pt_selected = 0;
+}
+SketcherConfig::~SketcherConfig()
+{
+}
+
+int SketcherConfig::equivalent(SketcherConfig &that)
+{
+ if( this->drag != that.drag ) return 0;
+ if( this->cv_selected != that.cv_selected ) return 0;
+ if( this->pt_selected != that.pt_selected ) return 0;
+ if( this->curves.size() != that.curves.size() ) return 0;
+ for( int i=0, n=curves.size(); i<n; ++i ) {
+ if( !curves[i]->equivalent(*that.curves[i]) ) return 0;
+ }
+ return 1;
+}
+
+void SketcherConfig::copy_from(SketcherConfig &that)
+{
+ this->drag = that.drag;
+ this->cv_selected = that.cv_selected;
+ this->pt_selected = that.pt_selected;
+ int m = curves.size(), n = that.curves.size();
+ while( m > n ) curves.remove_object_number(--m);
+ while( m < n ) { curves.append(new SketcherCurve()); ++m; }
+ for( int i=0; i<n; ++i ) curves[i]->copy_from(*that.curves[i]);
+}
+
+void SketcherConfig::interpolate(SketcherConfig &prev, SketcherConfig &next,
+ long prev_frame, long next_frame, long current_frame)
+{
+ this->cv_selected = prev.cv_selected;
+ this->pt_selected = prev.pt_selected;
+ this->drag = prev.drag;
+
+ double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+ double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+
+ curves.remove_all_objects();
+ int prev_cv_sz = prev.curves.size();
+ int next_cv_sz = next.curves.size();
+ for( int i=0; i<prev_cv_sz; ++i ) {
+ SketcherCurve *pcv = prev.curves[i], *ncv = 0;
+ SketcherCurve *cv = curves.append(new SketcherCurve());
+ int k = next_cv_sz; // associated by id in next
+ while( --k >= 0 && pcv->id != (ncv=next.curves[k])->id );
+ if( k >= 0 ) {
+ cv->id = pcv->id;
+ cv->ty = pcv->ty;
+ cv->radius = pcv->radius;
+ cv->pen = pcv->pen;
+ cv->color = pcv->color;
+ int prev_pt_sz = pcv->points.size(), next_pt_sz = ncv->points.size();
+ for( int j=0; j<prev_pt_sz; ++j ) {
+ SketcherPoint &pt = *pcv->points[j], *nt = 0;
+ k = next_pt_sz; // associated by id in next
+ while( --k >= 0 && pt.id != (nt=ncv->points[k])->id );
+ int x = pt.x, y = pt.y;
+ if( k >= 0 ) {
+ x = x * prev_scale + nt->x * next_scale;
+ y = y * prev_scale + nt->y * next_scale;
+ }
+ cv->points.append(new SketcherPoint(pt.id, x, y));
+ }
+ }
+ else
+ cv->copy_from(*pcv);
+ }
+}
+
+void SketcherConfig::limits()
+{
+}
+
+
+Sketcher::Sketcher(PluginServer *server)
+ : PluginVClient(server)
+{
+}
+
+Sketcher::~Sketcher()
+{
+}
+
+const char* Sketcher::plugin_title() { return N_("Sketcher"); }
+int Sketcher::is_realtime() { return 1; }
+
+NEW_WINDOW_MACRO(Sketcher, SketcherWindow);
+LOAD_CONFIGURATION_MACRO(Sketcher, SketcherConfig)
+
+void Sketcher::save_data(KeyFrame *keyframe)
+{
+ FileXML output;
+// cause data to be stored directly in text
+ output.set_shared_output(keyframe->xbuf);
+
+ output.tag.set_title("SKETCHER");
+ output.tag.set_property("DRAG", config.drag);
+ output.tag.set_property("CURVE_SELECTED", config.cv_selected);
+ output.tag.set_property("POINT_SELECTED", config.pt_selected);
+ output.append_tag();
+ output.append_newline();
+ for( int i=0,n=config.curves.size(); i<n; ++i ) {
+ config.curves[i]->save_data(output);
+ }
+ output.tag.set_title("/SKETCHER");
+ output.append_tag();
+ output.append_newline();
+ output.terminate_string();
+}
+
+void Sketcher::read_data(KeyFrame *keyframe)
+{
+ FileXML input;
+ input.set_shared_input(keyframe->xbuf);
+ config.curves.remove_all_objects();
+ int result = 0;
+ SketcherCurve *cv = 0;
+
+ while( !(result=input.read_tag()) ) {
+ if( input.tag.title_is("SKETCHER") ) {
+ config.drag = input.tag.get_property("DRAG", config.drag);
+ config.cv_selected = input.tag.get_property("CV_SELECTED", 0);
+ config.pt_selected = input.tag.get_property("PT_SELECTED", 0);
+ }
+ else if( !strncmp(input.tag.get_title(),"CURVE_",6) ) {
+ cv = new SketcherCurve();
+ cv->read_data(input);
+ config.curves.append(cv);
+ }
+ else if( !strncmp(input.tag.get_title(),"/CURVE_",7) )
+ cv = 0;
+ else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
+ if( cv ) {
+ SketcherPoint *pt = new SketcherPoint();
+ pt->read_data(input);
+ cv->points.append(pt);
+ }
+ else
+ printf("Sketcher::read_data: no curve for point\n");
+ }
+ }
+
+ if( !config.curves.size() ) {
+ new_curve(0, 1, 0, BLACK);
+ }
+ config.limits();
+}
+
+void Sketcher::update_gui()
+{
+ if( !thread ) return;
+ thread->window->lock_window("Sketcher::update_gui");
+ if( load_configuration() ) {
+ SketcherWindow *window = (SketcherWindow*)thread->window;
+ window->update_gui();
+ window->flush();
+ }
+ thread->window->unlock_window();
+}
+
+void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d)
+{
+ int r = d/2+1, x = pt->x, y = pt->y;
+ vfrm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
+ vfrm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
+ vfrm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
+ vfrm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
+ vfrm->set_pixel_color(color);
+ vfrm->draw_x(pt->x, pt->y, d);
+}
+void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color)
+{
+ draw_point(vfrm, pt, color, bmax(w,h)/200 + 2);
+}
+
+
+int SketcherPenSquare::draw_pixel(int x, int y)
+{
+ vfrm->draw_line(x-n, y, x+n, y);
+ for( int i=-n; i<n; ++i )
+ vfrm->draw_line(x-n, y+i, x+n, y+i);
+ return 0;
+}
+int SketcherPenPlus::draw_pixel(int x, int y)
+{
+ if( n > 1 ) {
+ vfrm->draw_line(x-n, y, x+n, y);
+ vfrm->draw_line(x, y-n, x, y+n);
+ }
+ else
+ vfrm->draw_pixel(x, y);
+ return 0;
+}
+int SketcherPenSlant::draw_pixel(int x, int y)
+{
+ vfrm->draw_line(x-n, y+n, x+n, y-n);
+ vfrm->draw_line(x-n+1, y+n, x+n+1, y-n);
+ vfrm->draw_line(x-n, y+n+1, x+n, y-n+1);
+ return 0;
+}
+int SketcherPenXlant::draw_pixel(int x, int y)
+{
+ vfrm->draw_line(x-n, y+n, x+n, y-n);
+ vfrm->draw_line(x-n+1, y+n, x+n+1, y-n);
+ vfrm->draw_line(x-n, y+n+1, x+n, y-n+1);
+ vfrm->draw_line(x-n, y-n, x+n, y+n);
+ vfrm->draw_line(x-n+1, y-n, x+n+1, y+n);
+ vfrm->draw_line(x-n, y-n+1, x+n, y-n+1);
+ return 0;
+}
+
+
+VFrame *SketcherCurve::new_vpen(VFrame *out)
+{
+ switch( pen ) {
+ case PEN_SQUARE: return new SketcherPenSquare(out, radius);
+ case PEN_PLUS: return new SketcherPenPlus(out, radius);
+ case PEN_SLANT: return new SketcherPenSlant(out, radius);
+ case PEN_XLANT: return new SketcherPenXlant(out, radius);
+ }
+ return 0;
+}
+
+void SketcherCurve::draw_line(VFrame *out)
+{
+ SketcherPoint *pt0 = points[0];
+ VFrame *vpen = new_vpen(out);
+ out->set_pixel_color(color);
+ int n = points.size();
+ if( n >= 2 ) {
+ for( int pi=1; pi<n; ++pi ) {
+ SketcherPoint *pt1 = points[pi];
+ vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y);
+ pt0 = pt1;
+ }
+ }
+ else
+ vpen->draw_pixel(pt0->x, pt0->y);
+ delete vpen;
+}
+
+/*
+# python
+from sympy import *
+var("x,y, ax,ay, bx,by, cx,cy, dx,dy")
+
+var("abdx,abdy, acdx,acdy, bddx,bddy, cddx,cddy");
+abdx = bx-ax; abdy = by-ay;
+acdx = cx-ax; acdy = cy-ay;
+bddx = dx-bx; bddy = dy-by;
+cddx = dx-cx; cddy = dy-cy;
+
+var("xc,yc, xd,yd, sx,sy");
+xc = (bx+dx)/2; yc = (by+dy)/2;
+xd = cx-xc; yd = cy-yc;
+ax = xc-xd; ay = yc-yd;
+# line thru b with slope (c-a) intersects line thru c with slope (d-b)
+sx = solve(((x - bx) * acdy/acdx + by) - ((x - cx) * bddy/bddx + cy),x)
+sy = solve(((y - by) * acdx/acdy + bx) - ((y - cy) * bddx/bddy + cx),y)
+
+var("zx,zy, zdx,zdy, sx,sy, px,py, qx,qy");
+# point z = (b+c)/2
+zx = (bx+cx)/2; zy = (by+cy)/2;
+zdx = (abdx+cddx)/2; zdy = (abdy+cddy)/2;
+# line thru z with slope (d-a) intersects line thru b with slope (c-a)
+px = solve(((x-zx)*zdy/zdx + zy) - ((x-bx) * acdy/acdx + by),x);
+py = solve(((y-zy)*zdx/zdy + zx) - ((y-by) * acdx/acdy + bx),y);
+# line thru z with slope (c-a + d-b)/2 intersects line thru c with slope (d-b)
+qx = solve(((x-zx)*zdy/zdx + zy) - ((x-cx) * bddy/bddx + cy),x);
+qy = solve(((y-zy)*zdx/zdy + zx) - ((y-cy) * bddx/bddy + cx),y);
+*/
+
+static void smooth_sxy(
+ float ax, float ay, float bx, float by,
+ float cx, float cy, float dx, float dy,
+ float &sx, float &sy)
+{
+ float acdx = cx-ax, acdy = cy-ay;
+ float bddx = dx-bx, bddy = dy-by;
+ float d = acdx*bddy - acdy*bddx;
+ if( fabsf(d) < 1 ) d = 1;
+ sx = (acdx*bddx*by - acdx*bddx*cy + acdx*bddy*cx - acdy*bddx*bx) / d;
+ sy = (acdx*bddy*by - acdy*bddx*cy - acdy*bddy*bx + acdy*bddy*cx) / d;
+ bclamp(sx, -32767.f, 32767.f);
+ bclamp(sy, -32767.f, 32767.f);
+}
+
+static void smooth_pxy(
+ float ax, float ay, float bx, float by,
+ float cx, float cy, float dx, float dy,
+ float &px, float &py)
+{
+ float abdx = bx - ax, abdy = by - ay;
+ float acdx = cx - ax, acdy = cy - ay;
+ float cddx = dx - cx, cddy = dy - cy;
+ float d = (2*(abdx*acdy - abdy*acdx - acdx*cddy + acdy*cddx));
+ if( fabsf(d) < 1 ) d = 1;
+ px = (-abdx*acdx*by + abdx*acdx*cy + 2*abdx*acdy*bx - abdy*acdx*bx - abdy*acdx*cx -
+ acdx*bx*cddy - acdx*by*cddx + acdx*cddx*cy - acdx*cddy*cx + 2*acdy*bx*cddx) / d;
+ py = (abdx*acdy*by + abdx*acdy*cy - 2*abdy*acdx*by + abdy*acdy*bx - abdy*acdy*cx -
+ 2*acdx*by*cddy + acdy*bx*cddy + acdy*by*cddx + acdy*cddx*cy - acdy*cddy*cx) / d;
+ bclamp(px, -32767.f, 32767.f);
+ bclamp(py, -32767.f, 32767.f);
+}
+static void smooth_qxy(
+ float ax, float ay, float bx, float by,
+ float cx, float cy, float dx, float dy,
+ float &qx, float &qy)
+{
+ float abdx = bx - ax, abdy = by - ay;
+ float bddx = dx - bx, bddy = dy - by;
+ float cddx = dx - cx, cddy = dy - cy;
+ float d = (2*(abdx*bddy - abdy*bddx - bddx*cddy + bddy*cddx));
+ if( fabsf(d) < 1 ) d = 1;
+ qx = (abdx*bddx*by - abdx*bddx*cy + 2*abdx*bddy*cx - abdy*bddx*bx - abdy*bddx*cx -
+ bddx*bx*cddy + bddx*by*cddx - bddx*cddx*cy - bddx*cddy*cx + 2*bddy*cddx*cx) / d;
+ qy = (abdx*bddy*by + abdx*bddy*cy - 2*abdy*bddx*cy - abdy*bddy*bx + abdy*bddy*cx -
+ 2*bddx*cddy*cy - bddy*bx*cddy + bddy*by*cddx + bddy*cddx*cy + bddy*cddy*cx) / d;
+ bclamp(qx, -32767.f, 32767.f);
+ bclamp(qy, -32767.f, 32767.f);
+}
+
+
+static int convex(float ax,float ay, float bx,float by,
+ float cx,float cy, float dx,float dy)
+{
+ float abdx = bx - ax, abdy = by - ay;
+ float acdx = cx - ax, acdy = cy - ay;
+ float bcdx = cx - bx, bcdy = cy - by;
+ float bddx = dx - bx, bddy = dy - by;
+ float abc = abdx*acdy - abdy*acdx;
+ float bcd = bcdx*bddy - bcdy*bddx;
+ float v = abc * bcd;
+ return !v ? 0 : v>0 ? 1 : -1;
+}
+
+void SketcherCurve::draw_smooth(VFrame *out)
+{
+ VFrame *vpen = new_vpen(out);
+ out->set_pixel_color(color);
+ int n = points.size();
+ if( !n ) return;
+ if( n > 2 ) {
+ SketcherPoint *pt0 = points[0], *pt1 = points[1], *pt2 = points[2];
+ float bx = pt0->x, by = pt0->y;
+ float cx = pt1->x, cy = pt1->y;
+ float dx = pt2->x, dy = pt2->y;
+ float xc = (bx+dx)/2.f, yc = (by+dy)/2.f;
+ float xd = cx - xc, yd = cy - yc;
+ float ax = xc - xd, ay = yc - yd;
+ float sx, sy;
+ for( int pi=0,n2=n-2; pi<n2; ++pi ) {
+ float dx = points[pi+2]->x, dy = points[pi+2]->y;
+ if( convex(ax,ay, bx,by, cx,cy, dx,dy) >= 0 ) {
+ smooth_sxy(ax,ay, bx,by, cx,cy, dx,dy, sx, sy);
+ vpen->draw_smooth(bx,by, sx,sy, cx,cy);
+ }
+ else {
+ float zx = (bx+cx)/2.f, zy = (by+cy)/2.f;
+ smooth_pxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy);
+ vpen->draw_smooth(bx,by, sx,sy, zx,zy);
+ smooth_qxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy);
+ vpen->draw_smooth(zx,zy, sx,sy, cx,cy);
+ }
+ ax = bx; ay = by;
+ bx = cx; by = cy;
+ cx = dx; cy = dy;
+ }
+ xc = (ax+cx)/2.f; yc = (ay+cy)/2.f;
+ xd = bx - xc, yd = by - yc;
+ dx = xc - xd, dy = yc - yd;
+ smooth_sxy(ax, ay, bx, by, cx, cy, dx, dy, sx, sy);
+ vpen->draw_smooth(bx, by, sx, sy, cx, cy);
+ }
+ else if( n == 2 ) {
+ SketcherPoint *pt0 = points[0], *pt1 = points[1];
+ vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y);
+ }
+ else if( n > 0 ) {
+ SketcherPoint *pt0 = points[0];
+ vpen->draw_pixel(pt0->x, pt0->y);
+ }
+ delete vpen;
+}
+
+int Sketcher::process_realtime(VFrame *input, VFrame *output)
+{
+ this->input = input; this->output = output;
+ w = output->get_w(); h = output->get_h();
+ if( input != output ) output->transfer_from(input);
+
+ load_configuration();
+
+ for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
+ SketcherCurve *cv = config.curves[ci];
+ int m = cv->points.size();
+ if( !m ) continue;
+ switch( cv->ty ) {
+ case TYP_OFF:
+ break;
+ case TYP_SKETCHER:
+ cv->draw_line(output);
+ break;
+ case TYP_SMOOTH:
+ cv->draw_smooth(output);
+ break;
+ }
+ }
+
+ if( config.drag ) {
+ for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
+ SketcherCurve *cv = config.curves[ci];
+ for( int pi=0,m=cv->points.size(); pi<m; ++pi )
+ draw_point(output, cv->points[pi], cv->color);
+ }
+ }
+
+ return 0;
+}
+
+void SketcherCurves::dump()
+{
+ for( int i=0; i<size(); ++i ) {
+ SketcherCurve *cv = get(i);
+ printf("Curve %d, id=%d, ty=%s, r=%d, pen=%s, color=%02x%02x%02x\n",
+ i, cv->id, cv_type[cv->ty], cv->radius, cv_pen[cv->pen],
+ (cv->color>>16)&0xff, (cv->color>>8)&0xff, (cv->color>>0)&0xff);
+ cv->points.dump();
+ }
+}
+void SketcherPoints::dump()
+{
+ for( int i=0; i<size(); ++i ) {
+ SketcherPoint *pt = get(i);
+ printf(" Pt %d, id=%d, x=%d, y=%d\n", i, pt->id, pt->x, pt->y);
+ }
+}
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+
+#ifndef __SKETCHERS_H__
+#define __SKETCHERS_H__
+
+#include "pluginvclient.h"
+
+class Sketcher;
+
+enum { PT_ID, PT_X, PT_Y, PT_SZ };
+enum { CV_ID, CV_TY, CV_RAD, CV_PEN, CV_CLR, CV_SZ };
+enum { TYP_OFF, TYP_SKETCHER, TYP_SMOOTH, TYP_SZ };
+enum { PEN_SQUARE, PEN_PLUS, PEN_SLANT, PEN_XLANT, PEN_SZ };
+
+class SketcherVPen : public VFrame
+{
+public:
+ SketcherVPen(VFrame *vfrm, int n)
+ : VFrame(vfrm->get_data(), -1, vfrm->get_y()-vfrm->get_data(),
+ vfrm->get_u()-vfrm->get_data(), vfrm->get_v()-vfrm->get_data(),
+ vfrm->get_w(), vfrm->get_h(), vfrm->get_color_model(),
+ vfrm->get_bytes_per_line()) {
+ this->vfrm = vfrm; this->n = n;
+ }
+ virtual int draw_pixel(int x, int y) = 0;
+ VFrame *vfrm;
+ int n;
+};
+
+class SketcherPenSquare : public SketcherVPen
+{
+public:
+ SketcherPenSquare(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+ int draw_pixel(int x, int y);
+};
+class SketcherPenPlus : public SketcherVPen
+{
+public:
+ SketcherPenPlus(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+ int draw_pixel(int x, int y);
+};
+class SketcherPenSlant : public SketcherVPen
+{
+public:
+ SketcherPenSlant(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+ int draw_pixel(int x, int y);
+};
+class SketcherPenXlant : public SketcherVPen
+{
+public:
+ SketcherPenXlant(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+ int draw_pixel(int x, int y);
+};
+
+
+class SketcherPoint
+{
+public:
+ int id;
+ int x, y;
+
+ void init(int id, int x, int y);
+ SketcherPoint(int id, int x, int y);
+ SketcherPoint(int id=-1);
+ SketcherPoint(SketcherPoint &pt);
+ ~SketcherPoint();
+ int equivalent(SketcherPoint &that);
+ void copy_from(SketcherPoint &that);
+ void save_data(FileXML &output);
+ void read_data(FileXML &input);
+};
+class SketcherPoints : public ArrayList<SketcherPoint *>
+{
+public:
+ SketcherPoints() {}
+ ~SketcherPoints() { remove_all_objects(); }
+ void dump();
+};
+
+#define cv_type SketcherCurve::types
+#define cv_pen SketcherCurve::pens
+
+class SketcherCurve
+{
+public:
+ int id, ty, radius, pen, color;
+ static const char *types[TYP_SZ];
+ static const char *pens[PEN_SZ];
+
+ SketcherPoints points;
+
+ void init(int id, int ty, int radius, int pen, int color);
+ SketcherCurve(int id, int ty, int radius, int pen, int color);
+ SketcherCurve(int id=-1);
+ ~SketcherCurve();
+ SketcherCurve(SketcherCurve &cv);
+ int equivalent(SketcherCurve &that);
+ void copy_from(SketcherCurve &that);
+ void save_data(FileXML &output);
+ void read_data(FileXML &input);
+ VFrame *new_vpen(VFrame *out);
+ void draw_line(VFrame *out);
+ void draw_smooth(VFrame *out);
+};
+class SketcherCurves : public ArrayList<SketcherCurve *>
+{
+public:
+ SketcherCurves() {}
+ ~SketcherCurves() { remove_all_objects(); }
+ void dump();
+};
+
+class SketcherConfig
+{
+public:
+ SketcherConfig();
+ ~SketcherConfig();
+
+ SketcherCurves curves;
+ int equivalent(SketcherConfig &that);
+ void copy_from(SketcherConfig &that);
+ void interpolate(SketcherConfig &prev, SketcherConfig &next,
+ long prev_frame, long next_frame, long current_frame);
+ void limits();
+
+ int drag;
+ int cv_selected, pt_selected;
+};
+
+class Sketcher : public PluginVClient
+{
+public:
+ Sketcher(PluginServer *server);
+ ~Sketcher();
+// required for all realtime plugins
+ PLUGIN_CLASS_MEMBERS2(SketcherConfig)
+ int is_realtime();
+ void update_gui();
+ void save_data(KeyFrame *keyframe);
+ void read_data(KeyFrame *keyframe);
+ int new_curve(int ty, int radius, int pen, int color);
+ int new_curve();
+ int new_point(SketcherCurve *cv, int x, int y);
+ int new_point();
+ int process_realtime(VFrame *input, VFrame *output);
+ static void draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d);
+ void draw_point(VFrame *vfrm, SketcherPoint *pt, int color);
+
+ VFrame *input, *output;
+ int w, h, color_model, bpp, comp;
+ int is_yuv, is_float;
+};
+
+#endif
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "automation.h"
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "sketcher.h"
+#include "sketcherwindow.h"
+#include "cstrdup.h"
+#include "cwindow.h"
+#include "cwindowgui.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "keys.h"
+#include "language.h"
+#include "mainerror.h"
+#include "mwindow.h"
+#include "plugin.h"
+#include "pluginserver.h"
+#include "theme.h"
+#include "track.h"
+
+#define COLOR_W 30
+#define COLOR_H 30
+
+const char *SketcherCurve::types[] = {
+ N_("off"),
+ N_("line"),
+ N_("smooth"),
+};
+const char *SketcherCurve::pens[] = {
+ N_("box"),
+ N_("+"),
+ N_("/"),
+ N_("X"),
+};
+
+SketcherCurveTypeItem::SketcherCurveTypeItem(int ty)
+ : BC_MenuItem(_(cv_type[ty]))
+{
+ this->ty = ty;
+}
+int SketcherCurveTypeItem::handle_event()
+{
+ SketcherCurveType *popup = (SketcherCurveType*)get_popup_menu();
+ popup->update(ty);
+ SketcherWindow *gui = popup->gui;
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ cv->ty = ty;
+ gui->curve_list->update(ci);
+ gui->send_configure_change();
+ }
+ return 1;
+}
+
+SketcherCurveType::SketcherCurveType(SketcherWindow *gui, int x, int y, int ty)
+ : BC_PopupMenu(x,y,64,_(cv_type[ty]))
+{
+ this->gui = gui;
+}
+void SketcherCurveType::create_objects()
+{
+ int n = sizeof(cv_type)/sizeof(cv_type[0]);
+ for( int ty=0; ty<n; ++ty )
+ add_item(new SketcherCurveTypeItem(ty));
+}
+void SketcherCurveType::update(int ty)
+{
+ set_text(_(cv_type[ty]));
+}
+
+
+SketcherCurvePenItem::SketcherCurvePenItem(int pen)
+ : BC_MenuItem(_(cv_pen[pen]))
+{
+ this->pen = pen;
+}
+int SketcherCurvePenItem::handle_event()
+{
+ SketcherCurvePen *popup = (SketcherCurvePen*)get_popup_menu();
+ popup->update(pen);
+ SketcherWindow *gui = popup->gui;
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ cv->pen = pen;
+ gui->curve_list->update(ci);
+ gui->send_configure_change();
+ }
+ return 1;
+}
+
+SketcherCurvePen::SketcherCurvePen(SketcherWindow *gui, int x, int y, int pen)
+ : BC_PopupMenu(x,y,72,_(cv_pen[pen]))
+{
+ this->gui = gui;
+}
+void SketcherCurvePen::create_objects()
+{
+ int n = sizeof(cv_pen)/sizeof(cv_pen[0]);
+ for( int pen=0; pen<n; ++pen )
+ add_item(new SketcherCurvePenItem(pen));
+}
+void SketcherCurvePen::update(int pen)
+{
+ set_text(_(cv_pen[pen]));
+}
+
+
+SketcherCurveColor::SketcherCurveColor(SketcherWindow *gui, int x, int y, int w)
+ : BC_Button(x, y, w, vframes)
+{
+ this->gui = gui;
+ this->color = BLACK;
+ for( int i=0; i<3; ++i ) {
+ vframes[i] = new VFrame(w, w, BC_RGBA8888);
+ vframes[i]->clear_frame();
+ }
+}
+
+SketcherCurveColor::~SketcherCurveColor()
+{
+ for( int i=0; i<3; ++i )
+ delete vframes[i];
+}
+
+void SketcherCurveColor::set_color(int color)
+{
+ this->color = color;
+ int r = (color>>16) & 0xff;
+ int g = (color>>8) & 0xff;
+ int b = (color>>0) & 0xff;
+ for( int i=0; i<3; ++i ) {
+ VFrame *vframe = vframes[i];
+ int ww = vframe->get_w(), hh = vframe->get_h();
+ int cx = (ww+1)/2, cy = hh/2;
+ double cc = (cx*cx + cy*cy) / 4.;
+ uint8_t *bp = vframe->get_data(), *dp = bp;
+ uint8_t *ep = dp + vframe->get_data_size();
+ int rr = r, gg = g, bb = b;
+ int bpl = vframe->get_bytes_per_line();
+ switch( i ) {
+ case BUTTON_UP:
+ break;
+ case BUTTON_UPHI:
+ if( (rr+=48) > 0xff ) rr = 0xff;
+ if( (gg+=48) > 0xff ) gg = 0xff;
+ if( (bb+=48) > 0xff ) bb = 0xff;
+ break;
+ case BUTTON_DOWNHI:
+ if( (rr-=48) < 0x00 ) rr = 0x00;
+ if( (gg-=48) < 0x00 ) gg = 0x00;
+ if( (bb-=48) < 0x00 ) bb = 0x00;
+ break;
+ }
+ while( dp < ep ) {
+ int yy = (dp-bp) / bpl, xx = ((dp-bp) % bpl) >> 2;
+ int dy = cy - yy, dx = cx - xx;
+ double s = dx*dx + dy*dy - cc;
+ double ss = s < 0 ? 1 : s >= cc ? 0 : 1 - s/cc;
+ int aa = ss * 0xff;
+ *dp++ = rr; *dp++ = gg; *dp++ = bb; *dp++ = aa;
+ }
+ }
+ set_images(vframes);
+}
+
+void SketcherCurveColor::update_gui(int color)
+{
+ set_color(color);
+ draw_face();
+}
+
+int SketcherCurveColor::handle_event()
+{
+ gui->start_color_thread(this);
+ return 1;
+}
+
+SketcherCurveColorPicker::SketcherCurveColorPicker(SketcherWindow *gui, SketcherCurveColor *color_button)
+ : ColorPicker(0, _("Color"))
+{
+ this->gui = gui;
+ this->color_button = color_button;
+ this->color = 0;
+ color_update = new SketcherCurveColorThread(this);
+}
+
+SketcherCurveColorPicker::~SketcherCurveColorPicker()
+{
+ delete color_update;
+}
+
+void SketcherCurveColorPicker::start(int color)
+{
+ start_window(color, 0, 1);
+ color_update->start();
+}
+
+void SketcherCurveColorPicker::handle_done_event(int result)
+{
+ color_update->stop();
+ gui->lock_window("SketcherCurveColorPicker::handle_done_event");
+ if( result ) color = orig_color;
+ color_button->update_gui(color);
+ gui->unlock_window();
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ cv->color = color;
+ gui->curve_list->update(ci);
+ gui->send_configure_change();
+ }
+}
+
+int SketcherCurveColorPicker::handle_new_color(int color, int alpha)
+{
+ this->color = color;
+ color_update->update_lock->unlock();
+ return 1;
+}
+
+void SketcherCurveColorPicker::update_gui()
+{
+ gui->lock_window("SketcherCurveColorPicker::update_gui");
+ color_button->update_gui(color);
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 ) {
+ SketcherCurve *cv = config.curves[ci];
+ cv->color = color;
+ gui->curve_list->update(ci);
+ gui->send_configure_change();
+ }
+ gui->unlock_window();
+}
+
+SketcherCurveColorThread::SketcherCurveColorThread(SketcherCurveColorPicker *color_picker)
+ : Thread(1, 0, 0)
+{
+ this->color_picker = color_picker;
+ this->update_lock = new Condition(0,"SketcherCurveColorThread::update_lock");
+ done = 1;
+}
+
+SketcherCurveColorThread::~SketcherCurveColorThread()
+{
+ stop();
+ delete update_lock;
+}
+
+void SketcherCurveColorThread::start()
+{
+ if( done ) {
+ done = 0;
+ Thread::start();
+ }
+}
+
+void SketcherCurveColorThread::stop()
+{
+ if( !done ) {
+ done = 1;
+ update_lock->unlock();
+ join();
+ }
+}
+
+void SketcherCurveColorThread::run()
+{
+ while( !done ) {
+ update_lock->lock("SketcherCurveColorThread::run");
+ if( done ) break;
+ color_picker->update_gui();
+ }
+}
+
+
+SketcherNum::SketcherNum(SketcherWindow *gui, int x, int y, int output,
+ int mn, int mx)
+ : BC_TumbleTextBox(gui, output, mn, mx, x, y, 64)
+{
+ this->gui = gui;
+ set_increment(1);
+}
+
+SketcherNum::~SketcherNum()
+{
+}
+
+int SketcherPointX::handle_event()
+{
+ if( !SketcherNum::handle_event() ) return 0;
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ SketcherPointList *point_list = gui->point_list;
+ int hot_point = point_list->get_selection_number(0, 0);
+ SketcherPoints &points = cv->points;
+ if( hot_point >= 0 && hot_point < points.size() ) {
+ int v = atoi(get_text());
+ points[hot_point]->x = v;
+ point_list->set_point(hot_point, PT_X, v);
+ point_list->update_list(hot_point);
+ gui->send_configure_change();
+ }
+ }
+ return 1;
+}
+int SketcherPointY::handle_event()
+{
+ if( !SketcherNum::handle_event() ) return 0;
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ SketcherPointList *point_list = gui->point_list;
+ int hot_point = point_list->get_selection_number(0, 0);
+ SketcherPoints &points = cv->points;
+ if( hot_point >= 0 && hot_point < points.size() ) {
+ int v = atoi(get_text());
+ points[hot_point]->y = v;
+ point_list->set_point(hot_point, PT_Y, v);
+ point_list->update_list(hot_point);
+ gui->send_configure_change();
+ }
+ }
+ return 1;
+}
+
+int SketcherCurveRadius::handle_event()
+{
+ if( !SketcherNum::handle_event() ) return 0;
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ int v = atoi(get_text());
+ cv->radius = v;
+ gui->curve_list->update(ci);
+ gui->send_configure_change();
+ }
+ return 1;
+}
+
+
+SketcherWindow::SketcherWindow(Sketcher *plugin)
+ : PluginClientWindow(plugin, 380, 580, 380, 580, 0)
+{
+ this->plugin = plugin;
+ this->title_type = 0; this->curve_type = 0;
+ this->title_pen = 0; this->curve_pen = 0;
+ this->title_color = 0; this->curve_color = 0;
+ this->color_picker = 0; this->new_points = 0;
+ this->new_curve = 0; this->del_curve = 0;
+ this->curve_up = 0; this->curve_dn = 0;
+ this->title_x = 0; this->point_x = 0;
+ this->title_y = 0; this->point_y = 0;
+ this->new_point = 0; this->del_point = 0;
+ this->point_up = 0; this->point_dn = 0;
+ this->drag = 0; this->dragging = 0;
+ this->last_x = 0; this->last_y = 0;
+ this->point_list = 0; this->pending_config = 0;
+}
+
+SketcherWindow::~SketcherWindow()
+{
+ delete curve_radius;
+ delete point_x;
+ delete point_y;
+ delete color_picker;
+}
+
+void SketcherWindow::create_objects()
+{
+ int x = 10, y = 10, x1, y1;
+ int margin = plugin->get_theme()->widget_border;
+ BC_Title *title;
+ int ci = plugin->config.cv_selected;
+ if( ci < 0 || ci >= plugin->config.curves.size() )
+ ci = plugin->new_curve(0, 1, 0, BLACK);
+ SketcherCurve *cv = plugin->config.curves[ci];
+ add_subwindow(reset_curves = new SketcherResetCurves(this, plugin, x1=x, y+3));
+ x1 += reset_curves->get_w() + 2*margin;
+ const char *curve_text = _("Curve");
+ add_subwindow(title_radius = new BC_Title(x1, y, _("Width:")));
+ x1 += title_radius->get_w() + margin;
+ curve_radius = new SketcherCurveRadius(this, x1, y, cv->radius);
+ curve_radius->create_objects();
+ y += reset_curves->get_h() + 2*margin;
+ x1 = get_w()-x - BC_Title::calculate_w(this, curve_text, LARGEFONT);
+ y1 = y-margin - BC_Title::calculate_h(this, curve_text, LARGEFONT);
+ add_subwindow(title = new BC_Title(x1, y1, curve_text, LARGEFONT,
+ get_resources()->menu_highlighted_fontcolor));
+ add_subwindow(curve_list = new SketcherCurveList(this, plugin, x, y));
+ y += curve_list->get_h() + margin;
+ add_subwindow(title_type = new BC_Title(x, y, _("Type:")));
+ x1 = x + title_type->get_w() + margin;
+ add_subwindow(curve_type = new SketcherCurveType(this, x1, y, cv->ty));
+ curve_type->create_objects();
+ x1 += curve_type->get_w() + margin;
+ add_subwindow(new_curve = new SketcherNewCurve(this, plugin, x1, y));
+ x1 += new_curve->get_w() + margin;
+ add_subwindow(curve_up = new SketcherCurveUp(this, x1, y));
+ x1 += curve_up->get_w() + 2*margin;
+ add_subwindow(title_color = new BC_Title(x1, y, _("Color:")));
+ y += curve_type->get_h() + margin;
+
+ add_subwindow(title_pen = new BC_Title(x, y, _("Pen:")));
+ x1 = x + title_pen->get_w() + margin;
+ add_subwindow(curve_pen = new SketcherCurvePen(this, x1, y, cv->pen));
+ curve_pen->create_objects();
+ x1 += curve_pen->get_w() + margin;
+ add_subwindow(del_curve = new SketcherDelCurve(this, plugin, x1, y));
+ x1 += del_curve->get_w() + margin;
+ add_subwindow(curve_dn = new SketcherCurveDn(this, x1, y));
+ x1 += curve_dn->get_w() + 2*margin;
+ add_subwindow(curve_color = new SketcherCurveColor(this, x1, y, COLOR_W));
+ curve_color->create_objects();
+ curve_color->set_color(cv->color);
+ curve_color->draw_face();
+ y += COLOR_H + margin;
+ curve_list->update(ci);
+
+ BC_Bar *bar;
+ add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
+ y += bar->get_h() + 2*margin;
+
+ int pi = plugin->config.pt_selected;
+ SketcherPoint *pt = pi >= 0 && pi < cv->points.size() ? cv->points[pi] : 0;
+ add_subwindow(reset_points = new SketcherResetPoints(this, plugin, x1=x, y+3));
+ x1 += reset_points->get_w() + 2*margin;
+ add_subwindow(drag = new SketcherDrag(this, x1, y));
+ y += drag->get_h() + margin;
+ if( plugin->config.drag ) {
+ if( !grab(plugin->server->mwindow->cwindow->gui) )
+ eprintf("drag enabled, but compositor already grabbed\n");
+ }
+ const char *point_text = _("Point");
+ x1 = get_w()-x - BC_Title::calculate_w(this, point_text, LARGEFONT);
+ y1 = y-margin - BC_Title::calculate_h(this, point_text, LARGEFONT);
+ add_subwindow(title = new BC_Title(x1, y1, point_text, LARGEFONT,
+ get_resources()->menu_highlighted_fontcolor));
+ add_subwindow(point_list = new SketcherPointList(this, plugin, x, y));
+
+ y += point_list->get_h() + margin;
+ add_subwindow(title_x = new BC_Title(x, y, _("X:")));
+ x1 = x + title_x->get_w() + margin;
+ point_x = new SketcherPointX(this, x1, y, !pt ? 0.f : pt->x);
+ point_x->create_objects();
+ x1 += point_x->get_w() + 2*margin;
+ add_subwindow(new_point = new SketcherNewPoint(this, plugin, x1, y));
+ x1 += new_point->get_w() + margin;
+ add_subwindow(point_up = new SketcherPointUp(this, x1, y));
+ y += point_x->get_h() + margin;
+ add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
+ x1 = x + title_y->get_w() + margin;
+ point_y = new SketcherPointY(this, x1, y, !pt ? 0.f : pt->y);
+ point_y->create_objects();
+ x1 += point_y->get_w() + 2*margin;
+ add_subwindow(del_point = new SketcherDelPoint(this, plugin, x1, y));
+ x1 += del_point->get_w() + margin;
+ add_subwindow(point_dn = new SketcherPointDn(this, x1, y));
+ y += point_y->get_h() + margin + 10;
+ point_list->update(pi);
+
+ add_subwindow(notes0 = new BC_Title(x, y,
+ _("\n"
+ "LMB=\n"
+ "Alt+LMB=\n"
+ "MMB=\n"
+ "DEL=\n")));
+ add_subwindow(notes1 = new BC_Title(x+80, y,
+ _(" No Shift\n"
+ "select point\n"
+ "drag curve\n"
+ "next curve type\n"
+ "deletes point\n")));
+ add_subwindow(notes2 = new BC_Title(x+200, y,
+ _(" Shift\n"
+ "append new points\n"
+ "drag image\n"
+ "append new curve\n"
+ "delete curve\n")));
+ show_window(1);
+}
+
+void SketcherWindow::send_configure_change()
+{
+ pending_config = 0;
+ plugin->send_configure_change();
+}
+
+int SketcherWindow::grab_event(XEvent *event)
+{
+ int ret = do_grab_event(event);
+ if( pending_config && !grab_event_count() )
+ send_configure_change();
+ return ret;
+}
+
+int SketcherWindow::do_grab_event(XEvent *event)
+{
+ switch( event->type ) {
+ case ButtonPress: break;
+ case ButtonRelease: break;
+ case MotionNotify: break;
+ case KeyPress:
+ if( keysym_lookup(event) > 0 ) {
+ switch( get_keysym() ) {
+ case XK_Delete:
+ return (event->xkey.state & ShiftMask) ?
+ del_curve->handle_event() :
+ del_point->handle_event() ;
+ }
+ } // fall thru
+ default:
+ return 0;
+ }
+
+ MWindow *mwindow = plugin->server->mwindow;
+ CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
+ CWindowCanvas *canvas = cwindow_gui->canvas;
+ int cx, cy; cwindow_gui->get_relative_cursor(cx, cy);
+ cx -= mwindow->theme->ccanvas_x;
+ cy -= mwindow->theme->ccanvas_y;
+
+ if( !dragging ) {
+ if( cx < 0 || cx >= mwindow->theme->ccanvas_w ||
+ cy < 0 || cy >= mwindow->theme->ccanvas_h )
+ return 0;
+ }
+
+ switch( event->type ) {
+ case ButtonPress:
+ if( dragging ) return 0;
+ dragging = event->xbutton.state & Mod1Mask ? -1 : 1; // alt_down
+ break;
+ case ButtonRelease:
+ case MotionNotify:
+ if( !dragging ) return 0;
+ break;
+ default:
+ return 0;
+ }
+
+
+ int ci = plugin->config.cv_selected;
+ if( ci < 0 || ci >= plugin->config.curves.size() )
+ return 1;
+
+ float cursor_x = cx, cursor_y = cy;
+ canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
+ int64_t position = plugin->get_source_position();
+ float projector_x, projector_y, projector_z;
+ Track *track = plugin->server->plugin->track;
+ int track_w = track->track_w, track_h = track->track_h;
+ track->automation->get_projector(
+ &projector_x, &projector_y, &projector_z,
+ position, PLAY_FORWARD);
+ projector_x += mwindow->edl->session->output_w / 2;
+ projector_y += mwindow->edl->session->output_h / 2;
+ float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
+ float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
+ SketcherCurve *cv = plugin->config.curves[ci];
+ SketcherPoints &points = cv->points;
+ int state = event->xmotion.state;
+
+ switch( event->type ) {
+ case ButtonPress: {
+ if( dragging < 0 ) break;
+ int hot_point = -1;
+ int button_no = event->xbutton.button;
+ if( button_no == LEFT_BUTTON ) {
+// create new point string
+ if( (state & ShiftMask) ) {
+ ++new_points;
+ hot_point = plugin->new_point(cv, output_x, output_y);
+ point_list->update(hot_point);
+ }
+ else {
+// select point
+ int sz = points.size();
+ int last_point = hot_point;
+ if( sz > 0 ) {
+ SketcherPoint *pt = points[hot_point=0];
+ double dist = DISTANCE(output_x,output_y, pt->x,pt->y);
+ for( int i=1; i<sz; ++i ) {
+ pt = points[i];
+ double d = DISTANCE(output_x,output_y, pt->x,pt->y);
+ if( d >= dist ) continue;
+ dist = d; hot_point = i;
+ }
+ pt = points[hot_point];
+ float px = (pt->x - track_w / 2) * projector_z + projector_x;
+ float py = (pt->y - track_h / 2) * projector_z + projector_y;
+ dist = DISTANCE(px, py, cursor_x,cursor_y);
+ if( dist >= HANDLE_W ) hot_point = -1;
+ }
+ if( hot_point != last_point ) {
+ SketcherPoint *pt = 0;
+ if( hot_point >= 0 && hot_point < sz ) {
+ pt = points[hot_point];
+ point_list->set_point(hot_point, PT_X, pt->x = output_x);
+ point_list->set_point(hot_point, PT_Y, pt->y = output_y);
+ }
+ point_list->update_list(hot_point);
+ point_x->update(pt ? pt->x : 0.f);
+ point_y->update(pt ? pt->y : 0.f);
+ }
+ }
+ }
+ else if( button_no == MIDDLE_BUTTON ) {
+ if( (state & ShiftMask) ) {
+ int ci = plugin->new_curve(cv->ty, cv->radius, cv->pen, cv->color);
+ curve_list->update(ci);
+ point_list->update(-1);
+ }
+ else {
+ int ty = cv->ty + 1;
+ if( ty >= TYP_SZ ) ty = 0;
+ cv->ty = ty;
+ curve_type->update(ty);
+ curve_list->update(ci);
+ }
+ }
+ break; }
+ case MotionNotify: {
+ int hot_point = point_list->get_selection_number(0, 0);
+ if( dragging < 0 ) {
+ SketcherCurves &curves = plugin->config.curves;
+ int dx = round(output_x - last_x);
+ int dy = round(output_y - last_y);
+ int mnc = (state & ShiftMask) || ci<0 ? 0 : ci;
+ int mxc = (state & ShiftMask) ? curves.size() : ci+1;
+ for( int i=mnc; i<mxc; ++i ) {
+ SketcherCurve *crv = plugin->config.curves[i];
+ int pts = crv->points.size();
+ for( int k=0; k<pts; ++k ) {
+ SketcherPoint *pt = crv->points[k];
+ pt->x += dx; pt->y += dy;
+ }
+ }
+ SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ?
+ points[hot_point] : 0;
+ point_x->update(pt ? pt->x : 0.f);
+ point_y->update(pt ? pt->y : 0.f);
+ point_list->update(hot_point);
+ break;
+ }
+ if( (state & Button1Mask) ) {
+ SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ?
+ points[hot_point] : 0;
+ if( pt && pt->x == output_x && pt->y == output_y ) break;
+ if( new_points ) {
+ if( pt ) {
+ float frac_w = DISTANCE(pt->x, pt->y, output_x, output_y) / get_w();
+ if( frac_w < 0.01 ) break; // 1 percent w
+ }
+ if( (state & ShiftMask) ) {
+ ++new_points;
+ hot_point = plugin->new_point(cv, output_x, output_y);
+ point_list->update(hot_point);
+ }
+ }
+ else if( pt ) {
+ point_list->set_point(hot_point, PT_X, pt->x = output_x);
+ point_list->set_point(hot_point, PT_Y, pt->y = output_y);
+ point_list->update_list(hot_point);
+ point_x->update(pt->x);
+ point_y->update(pt->y);
+ }
+ }
+ break; }
+ case ButtonRelease: {
+ new_points = 0;
+ dragging = 0;
+ break; }
+ }
+
+ last_x = output_x; last_y = output_y;
+ pending_config = 1;
+ return 1;
+}
+
+int SketcherWindow::keypress_event()
+{
+ int key = get_keypress();
+ switch( key ) {
+ case DELETE: return shift_down() ?
+ del_curve->handle_event() :
+ del_point->handle_event() ;
+ }
+ return 0;
+}
+
+void SketcherWindow::done_event(int result)
+{
+ ungrab(client->server->mwindow->cwindow->gui);
+}
+
+void SketcherWindow::start_color_thread(SketcherCurveColor *color_button)
+{
+ unlock_window();
+ delete color_picker;
+ color_picker = new SketcherCurveColorPicker(this, color_button);
+ int color = BLACK, ci = plugin->config.cv_selected;
+ if( ci >= 0 && ci < plugin->config.curves.size() ) {
+ SketcherCurve *cv = plugin->config.curves[ci];
+ color = cv->color;
+ }
+ color_picker->start(color);
+ lock_window("SketcherWindow::start_color_thread");
+}
+
+
+SketcherCurveList::SketcherCurveList(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
+{
+ this->gui = gui;
+ this->plugin = plugin;
+ titles[CV_ID] = _("id"); widths[CV_ID] = 64;
+ titles[CV_TY] = _("type"); widths[CV_TY] = 64;
+ titles[CV_RAD] = _("radius"); widths[CV_RAD] = 64;
+ titles[CV_PEN] = _("pen"); widths[CV_PEN] = 64;
+ titles[CV_CLR] = _("color"); widths[CV_CLR] = 64;
+}
+SketcherCurveList::~SketcherCurveList()
+{
+ clear();
+}
+void SketcherCurveList::clear()
+{
+ for( int i=CV_SZ; --i>=0; )
+ cols[i].remove_all_objects();
+}
+
+int SketcherCurveList::column_resize_event()
+{
+ for( int i=CV_SZ; --i>=0; )
+ widths[i] = get_column_width(i);
+ return 1;
+}
+
+int SketcherCurveList::handle_event()
+{
+ int ci = get_selection_number(0, 0);
+ set_selected(ci);
+ gui->point_list->update(0);
+ gui->send_configure_change();
+ return 1;
+}
+
+int SketcherCurveList::selection_changed()
+{
+ handle_event();
+ return 1;
+}
+
+void SketcherCurveList::set_selected(int k)
+{
+ int ci = -1;
+ if( k >= 0 && k < plugin->config.curves.size() ) {
+ SketcherCurve *cv = plugin->config.curves[k];
+ gui->curve_type->update(cv->ty);
+ gui->curve_radius->update(cv->radius);
+ gui->curve_pen->update(cv->pen);
+ gui->curve_color->update_gui(cv->color);
+ ci = k;
+ }
+ plugin->config.cv_selected = ci;
+ update_selection(&cols[0], ci);
+ update_list(-1);
+}
+
+void SketcherCurveList::update_list(int k)
+{
+ int xpos = get_xposition(), ypos = get_yposition();
+ if( k < 0 ) k = get_selection_number(0, 0);
+ update_selection(&cols[0], k);
+ BC_ListBox::update(&cols[0], &titles[0],&widths[0],CV_SZ, xpos,ypos,k);
+ center_selection();
+}
+
+void SketcherCurveList::update(int k)
+{
+ clear();
+ SketcherCurves &curves = plugin->config.curves;
+ int sz = curves.size();
+ for( int i=0; i<sz; ++i ) {
+ SketcherCurve *cv = curves[i];
+ char itxt[BCSTRLEN]; sprintf(itxt,"%d", cv->id);
+ char ttxt[BCSTRLEN]; sprintf(ttxt,"%s", cv_type[cv->ty]);
+ char rtxt[BCSTRLEN]; sprintf(rtxt,"%d", cv->radius);
+ char ptxt[BCSTRLEN]; sprintf(ptxt,"%s", cv_pen[cv->pen]);
+ int color = cv->color;
+ int r = (color>>16)&0xff, g = (color>>8)&0xff, b = (color>>0)&0xff;
+ char ctxt[BCSTRLEN]; sprintf(ctxt,"#%02x%02x%02x", r, g, b);
+ add_curve(itxt, ttxt, rtxt, ptxt, ctxt);
+ }
+ set_selected(k);
+}
+
+void SketcherCurveList::add_curve(const char *id, const char *type,
+ const char *radius, const char *pen, const char *color)
+{
+ cols[CV_ID].append(new BC_ListBoxItem(id));
+ cols[CV_TY].append(new BC_ListBoxItem(type));
+ cols[CV_RAD].append(new BC_ListBoxItem(radius));
+ cols[CV_PEN].append(new BC_ListBoxItem(pen));
+ cols[CV_CLR].append(new BC_ListBoxItem(color));
+}
+
+SketcherNewCurve::SketcherNewCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, _("New"))
+{
+ this->gui = gui;
+ this->plugin = plugin;
+}
+SketcherNewCurve::~SketcherNewCurve()
+{
+}
+int SketcherNewCurve::handle_event()
+{
+ int ty = 1, pen = 0, color = 0, radius = 1;
+ int ci = plugin->config.cv_selected;
+ if( ci >= 0 && ci < plugin->config.curves.size() ) {
+ SketcherCurve *cv = plugin->config.curves[ci];
+ ty = cv->ty; pen = cv->pen;
+ color = cv->color; radius = cv->radius;
+ }
+ int k = plugin->new_curve(ty, radius, pen, color);
+ gui->curve_list->update(k);
+ gui->point_list->update(-1);
+ gui->send_configure_change();
+ return 1;
+}
+
+SketcherDelCurve::SketcherDelCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, C_("Del"))
+{
+ this->gui = gui;
+ this->plugin = plugin;
+}
+SketcherDelCurve::~SketcherDelCurve()
+{
+}
+int SketcherDelCurve::handle_event()
+{
+ int hot_curve = gui->curve_list->get_selection_number(0, 0);
+ SketcherCurves &curves = plugin->config.curves;
+ if( hot_curve >= 0 && hot_curve < curves.size() ) {
+ curves.remove_object_number(hot_curve);
+ if( --hot_curve < 0 )
+ hot_curve = plugin->new_curve(0, 1, 0, BLACK);
+ gui->curve_list->update(hot_curve);
+ gui->point_list->update(-1);
+ gui->send_configure_change();
+ }
+ return 1;
+}
+
+SketcherCurveUp::SketcherCurveUp(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Up"))
+{
+ this->gui = gui;
+}
+SketcherCurveUp::~SketcherCurveUp()
+{
+}
+
+int SketcherCurveUp::handle_event()
+{
+ SketcherCurves &curves = gui->plugin->config.curves;
+ int hot_curve = gui->curve_list->get_selection_number(0, 0);
+
+ if( hot_curve > 0 && hot_curve < curves.size() ) {
+ SketcherCurve *&cv0 = curves[hot_curve];
+ SketcherCurve *&cv1 = curves[--hot_curve];
+ SketcherCurve *t = cv0; cv0 = cv1; cv1 = t;
+ gui->curve_list->update(hot_curve);
+ }
+ gui->send_configure_change();
+ return 1;
+}
+
+SketcherCurveDn::SketcherCurveDn(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Dn"))
+{
+ this->gui = gui;
+}
+SketcherCurveDn::~SketcherCurveDn()
+{
+}
+
+int SketcherCurveDn::handle_event()
+{
+ SketcherCurves &curves = gui->plugin->config.curves;
+ int hot_curve = gui->curve_list->get_selection_number(0, 0);
+
+ if( hot_curve >= 0 && hot_curve < curves.size()-1 ) {
+ SketcherCurve *&cv0 = curves[hot_curve];
+ SketcherCurve *&cv1 = curves[++hot_curve];
+ SketcherCurve *t = cv0; cv0 = cv1; cv1 = t;
+ gui->curve_list->update(hot_curve);
+ }
+ gui->send_configure_change();
+ return 1;
+}
+
+
+SketcherPointList::SketcherPointList(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
+{
+ this->gui = gui;
+ this->plugin = plugin;
+ titles[PT_X] = _("X"); widths[PT_X] = 90;
+ titles[PT_Y] = _("Y"); widths[PT_Y] = 90;
+ titles[PT_ID] = _("ID"); widths[PT_ID] = 50;
+}
+SketcherPointList::~SketcherPointList()
+{
+ clear();
+}
+void SketcherPointList::clear()
+{
+ for( int i=PT_SZ; --i>=0; )
+ cols[i].remove_all_objects();
+}
+
+int SketcherPointList::column_resize_event()
+{
+ for( int i=PT_SZ; --i>=0; )
+ widths[i] = get_column_width(i);
+ return 1;
+}
+
+int SketcherPointList::handle_event()
+{
+ int pi = get_selection_number(0, 0);
+ set_selected(pi);
+ gui->send_configure_change();
+ return 1;
+}
+
+int SketcherPointList::selection_changed()
+{
+ handle_event();
+ return 1;
+}
+
+void SketcherPointList::add_point(const char *id, const char *xp, const char *yp)
+{
+ cols[PT_ID].append(new BC_ListBoxItem(id));
+ cols[PT_X].append(new BC_ListBoxItem(xp));
+ cols[PT_Y].append(new BC_ListBoxItem(yp));
+}
+
+void SketcherPointList::set_point(int i, int c, int v)
+{
+ char stxt[BCSTRLEN];
+ sprintf(stxt,"%d",v);
+ set_point(i,c,stxt);
+}
+void SketcherPointList::set_point(int i, int c, const char *cp)
+{
+ cols[c].get(i)->set_text(cp);
+}
+
+void SketcherPointList::set_selected(int k)
+{
+ SketcherPoint *pt = 0;
+ int ci = plugin->config.cv_selected, pi = -1;
+ if( ci >= 0 && ci < plugin->config.curves.size() ) {
+ SketcherCurve *cv = plugin->config.curves[ci];
+ pt = k >= 0 && k < cv->points.size() ? cv->points[pi=k] : 0;
+ }
+ gui->point_x->update(pt ? pt->x : 0.f);
+ gui->point_y->update(pt ? pt->y : 0.f);
+ plugin->config.pt_selected = pi;
+ update_selection(&cols[0], pi);
+ update_list(k);
+}
+void SketcherPointList::update_list(int k)
+{
+ int xpos = get_xposition(), ypos = get_yposition();
+ if( k < 0 ) k = get_selection_number(0, 0);
+ update_selection(&cols[0], k);
+ BC_ListBox::update(&cols[0], &titles[0],&widths[0],PT_SZ, xpos,ypos,k);
+ center_selection();
+}
+void SketcherPointList::update(int k)
+{
+ clear();
+ int ci = plugin->config.cv_selected, sz = 0;
+ if( ci >= 0 && ci < plugin->config.curves.size() ) {
+ SketcherCurve *cv = plugin->config.curves[ci];
+ SketcherPoints &points = cv->points;
+ sz = points.size();
+ for( int i=0; i<sz; ++i ) {
+ SketcherPoint *pt = points[i];
+ char itxt[BCSTRLEN]; sprintf(itxt,"%d", pt->id);
+ char xtxt[BCSTRLEN]; sprintf(xtxt,"%d", pt->x);
+ char ytxt[BCSTRLEN]; sprintf(ytxt,"%d", pt->y);
+ add_point(itxt, xtxt, ytxt);
+ }
+ }
+ set_selected(k);
+}
+
+void SketcherWindow::update_gui()
+{
+ SketcherConfig &config = plugin->config;
+ int ci = config.cv_selected;
+ int pi = config.pt_selected;
+ curve_list->update(ci);
+ point_list->update(pi);
+ SketcherCurve *cv = ci >= 0 ? config.curves[ci] : 0;
+ curve_radius->update(cv ? cv->radius : 1);
+ curve_type->update(cv ? cv->ty : TYP_OFF);
+ curve_pen->update(cv ? cv->pen : PEN_SQUARE);
+ curve_color->set_color(cv ? cv->color : BLACK);
+ SketcherPoint *pt = pi >= 0 ? cv->points[pi] : 0;
+ point_x->update(pt ? pt->x : 0);
+ point_y->update(pt ? pt->y : 0);
+ drag->update(plugin->config.drag);
+}
+
+
+SketcherPointUp::SketcherPointUp(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Up"))
+{
+ this->gui = gui;
+}
+SketcherPointUp::~SketcherPointUp()
+{
+}
+
+int SketcherPointUp::handle_event()
+{
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci < 0 || ci >= config.curves.size() )
+ return 1;
+ SketcherCurve *cv = config.curves[ci];
+ SketcherPoints &points = cv->points;
+ int sz = points.size();
+ int hot_point = gui->point_list->get_selection_number(0, 0);
+
+ if( sz > 1 && hot_point > 0 ) {
+ SketcherPoint *&pt0 = points[hot_point];
+ SketcherPoint *&pt1 = points[--hot_point];
+ SketcherPoint *t = pt0; pt0 = pt1; pt1 = t;
+ gui->point_list->update(hot_point);
+ }
+ gui->send_configure_change();
+ return 1;
+}
+
+SketcherPointDn::SketcherPointDn(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Dn"))
+{
+ this->gui = gui;
+}
+SketcherPointDn::~SketcherPointDn()
+{
+}
+
+int SketcherPointDn::handle_event()
+{
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci < 0 || ci >= config.curves.size() )
+ return 1;
+ SketcherCurve *cv = config.curves[ci];
+ SketcherPoints &points = cv->points;
+ int sz = points.size();
+ int hot_point = gui->point_list->get_selection_number(0, 0);
+ if( sz > 1 && hot_point < sz-1 ) {
+ SketcherPoint *&pt0 = points[hot_point];
+ SketcherPoint *&pt1 = points[++hot_point];
+ SketcherPoint *t = pt0; pt0 = pt1; pt1 = t;
+ gui->point_list->update(hot_point);
+ }
+ gui->send_configure_change();
+ return 1;
+}
+
+SketcherDrag::SketcherDrag(SketcherWindow *gui, int x, int y)
+ : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
+{
+ this->gui = gui;
+}
+int SketcherDrag::handle_event()
+{
+ CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
+ int value = get_value();
+ if( value ) {
+ if( !gui->grab(cwindow_gui) ) {
+ update(value = 0);
+ flicker(10,50);
+ }
+ }
+ else
+ gui->ungrab(cwindow_gui);
+ gui->plugin->config.drag = value;
+ gui->send_configure_change();
+ return 1;
+}
+
+SketcherNewPoint::SketcherNewPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, _("New"))
+{
+ this->gui = gui;
+ this->plugin = plugin;
+}
+SketcherNewPoint::~SketcherNewPoint()
+{
+}
+int SketcherNewPoint::handle_event()
+{
+ int k = plugin->new_point();
+ gui->point_list->update(k);
+ gui->send_configure_change();
+ return 1;
+}
+
+SketcherDelPoint::SketcherDelPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, C_("Del"))
+{
+ this->gui = gui;
+ this->plugin = plugin;
+}
+SketcherDelPoint::~SketcherDelPoint()
+{
+}
+int SketcherDelPoint::handle_event()
+{
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ SketcherPoints &points = cv->points;
+ int hot_point = gui->point_list->get_selection_number(0, 0);
+ if( hot_point >= 0 && hot_point < points.size() ) {
+ points.remove_object_number(hot_point);
+ gui->point_list->update(--hot_point);
+ gui->send_configure_change();
+ }
+ }
+ return 1;
+}
+
+SketcherResetCurves::SketcherResetCurves(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+ this->gui = gui;
+ this->plugin = plugin;
+}
+SketcherResetCurves::~SketcherResetCurves()
+{
+}
+int SketcherResetCurves::handle_event()
+{
+ SketcherConfig &config = plugin->config;
+ config.curves.remove_all_objects();
+ int ci = plugin->new_curve(0, 1, 0, BLACK);
+ gui->curve_list->update(ci);
+ gui->point_list->update(-1);
+ gui->send_configure_change();
+ return 1;
+}
+
+SketcherResetPoints::SketcherResetPoints(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+ this->gui = gui;
+ this->plugin = plugin;
+}
+SketcherResetPoints::~SketcherResetPoints()
+{
+}
+int SketcherResetPoints::handle_event()
+{
+ SketcherConfig &config = gui->plugin->config;
+ int ci = config.cv_selected;
+ if( ci >= 0 && ci < config.curves.size() ) {
+ SketcherCurve *cv = config.curves[ci];
+ cv->points.remove_all_objects();
+ gui->point_list->update(-1);
+ gui->send_configure_change();
+ }
+ return 1;
+}
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2015 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __CRIKEYWINDOW_H__
+#define __CRIKEYWINDOW_H__
+
+#include "guicast.h"
+#include "colorpicker.h"
+
+class Sketcher;
+class SketcherNum;
+class SketcherCurveTypeItem;
+class SketcherCurveType;
+class SketcherCurvePenItem;
+class SketcherCurvePen;
+class SketcherCurveColor;
+class SketcherCurveColorPicker;
+class SketcherCurveColorThread;
+class SketcherNewCurve;
+class SketcherDelCurve;
+class SketcherCurveUp;
+class SketcherCurveDn;
+class SketcherCurveRadius;
+class SketcherCurveList;
+class SketcherPointX;
+class SketcherPointY;
+class SketcherDrag;
+class SketcherPointList;
+class SketcherNewPoint;
+class SketcherDelPoint;
+class SketcherPointUp;
+class SketcherPointDn;
+class SketcherResetCurves;
+class SketcherResetPoints;
+class SketcherWindow;
+
+class SketcherNum : public BC_TumbleTextBox
+{
+public:
+ SketcherWindow *gui;
+
+ SketcherNum(SketcherWindow *gui, int x, int y, int output,
+ int mn=-32767, int mx=32767);
+ ~SketcherNum();
+ int update(int v) { return BC_TumbleTextBox::update((int64_t)v); }
+};
+
+class SketcherCurveTypeItem : public BC_MenuItem
+{
+public:
+ SketcherCurveTypeItem(int ty);
+ int handle_event();
+ int ty;
+};
+
+class SketcherCurveType : public BC_PopupMenu
+{
+public:
+ SketcherCurveType(SketcherWindow *gui, int x, int y, int ty);
+ void create_objects();
+ void update(int ty);
+
+ SketcherWindow *gui;
+};
+
+class SketcherCurvePenItem : public BC_MenuItem
+{
+public:
+ SketcherCurvePenItem(int pen);
+ int handle_event();
+ int pen;
+};
+
+class SketcherCurvePen : public BC_PopupMenu
+{
+public:
+ SketcherCurvePen(SketcherWindow *gui, int x, int y, int pen);
+ void create_objects();
+ void update(int ty);
+
+ SketcherWindow *gui;
+};
+
+class SketcherCurveColor : public BC_Button
+{
+public:
+ SketcherCurveColor(SketcherWindow *gui, int x, int y, int w);
+ ~SketcherCurveColor();
+
+ void set_color(int color);
+ void update_gui(int color);
+ int handle_event();
+
+ int color;
+ VFrame *vframes[3];
+ SketcherWindow *gui;
+};
+
+class SketcherCurveColorPicker : public ColorPicker
+{
+public:
+ SketcherCurveColorPicker(SketcherWindow *gui, SketcherCurveColor *curve_color);
+ ~SketcherCurveColorPicker();
+ void start(int color);
+ int handle_new_color(int color, int alpha);
+ void update_gui();
+ void handle_done_event(int result);
+
+ SketcherWindow *gui;
+ int color;
+ SketcherCurveColor *color_button;
+ SketcherCurveColorThread *color_update;
+};
+
+class SketcherCurveColorThread : public Thread
+{
+public:
+ SketcherCurveColorThread(SketcherCurveColorPicker *color_picker);
+ ~SketcherCurveColorThread();
+
+ void start();
+ void stop();
+ void run();
+
+ SketcherCurveColorPicker *color_picker;
+ int done;
+ Condition *update_lock;
+};
+
+class SketcherNewCurve : public BC_GenericButton
+{
+public:
+ SketcherNewCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherNewCurve();
+
+ int handle_event();
+
+ SketcherWindow *gui;
+ Sketcher *plugin;
+};
+
+class SketcherDelCurve : public BC_GenericButton
+{
+public:
+ SketcherDelCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherDelCurve();
+
+ int handle_event();
+
+ Sketcher *plugin;
+ SketcherWindow *gui;
+};
+
+class SketcherCurveUp : public BC_GenericButton
+{
+public:
+ SketcherCurveUp(SketcherWindow *gui, int x, int y);
+ ~SketcherCurveUp();
+
+ int handle_event();
+
+ SketcherWindow *gui;
+};
+
+class SketcherCurveDn : public BC_GenericButton
+{
+public:
+ SketcherCurveDn(SketcherWindow *gui, int x, int y);
+ ~SketcherCurveDn();
+
+ int handle_event();
+
+ SketcherWindow *gui;
+};
+
+class SketcherCurveRadius : public SketcherNum
+{
+public:
+ SketcherCurveRadius(SketcherWindow *gui, int x, int y, float output)
+ : SketcherNum(gui, x, y, output, 0, 255) {}
+ ~SketcherCurveRadius() {}
+
+ int handle_event();
+};
+
+class SketcherCurveList : public BC_ListBox
+{
+public:
+ SketcherCurveList(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherCurveList();
+
+ int handle_event();
+ int selection_changed();
+ int column_resize_event();
+ ArrayList<BC_ListBoxItem*> cols[CV_SZ];
+ void clear();
+ void add_curve(const char *id, const char *type,
+ const char *radius, const char *pen, const char *color);
+ void del_curve(int i);
+ void set_selected(int k);
+ void update(int k);
+ void update_list(int k);
+
+ SketcherWindow *gui;
+ Sketcher *plugin;
+ const char *titles[CV_SZ];
+ int widths[CV_SZ];
+};
+
+
+class SketcherPointX : public SketcherNum
+{
+public:
+ SketcherPointX(SketcherWindow *gui, int x, int y, float output)
+ : SketcherNum(gui, x, y, output) {}
+ ~SketcherPointX() {}
+
+ int handle_event();
+};
+class SketcherPointY : public SketcherNum
+{
+public:
+ SketcherPointY(SketcherWindow *gui, int x, int y, float output)
+ : SketcherNum(gui, x, y, output) {}
+ ~SketcherPointY() {}
+
+ int handle_event();
+};
+
+
+class SketcherDrag : public BC_CheckBox
+{
+public:
+ SketcherDrag(SketcherWindow *gui, int x, int y);
+
+ int handle_event();
+ SketcherWindow *gui;
+};
+
+
+class SketcherPointList : public BC_ListBox
+{
+public:
+ SketcherPointList(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherPointList();
+
+ int handle_event();
+ int selection_changed();
+ int column_resize_event();
+ ArrayList<BC_ListBoxItem*> cols[PT_SZ];
+ void clear();
+ void add_point(const char *id, const char *xp, const char *yp);
+ void set_point(int i, int c, int v);
+ void set_point(int i, int c, const char *cp);
+ void set_selected(int k);
+ void update(int k);
+ void update_list(int k);
+
+
+ SketcherWindow *gui;
+ Sketcher *plugin;
+ const char *titles[PT_SZ];
+ int widths[PT_SZ];
+};
+
+class SketcherNewPoint : public BC_GenericButton
+{
+public:
+ SketcherNewPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherNewPoint();
+
+ int handle_event();
+
+ SketcherWindow *gui;
+ Sketcher *plugin;
+};
+
+class SketcherDelPoint : public BC_GenericButton
+{
+public:
+ SketcherDelPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherDelPoint();
+
+ int handle_event();
+
+ Sketcher *plugin;
+ SketcherWindow *gui;
+};
+
+class SketcherPointUp : public BC_GenericButton
+{
+public:
+ SketcherPointUp(SketcherWindow *gui, int x, int y);
+ ~SketcherPointUp();
+
+ int handle_event();
+
+ SketcherWindow *gui;
+};
+
+class SketcherPointDn : public BC_GenericButton
+{
+public:
+ SketcherPointDn(SketcherWindow *gui, int x, int y);
+ ~SketcherPointDn();
+
+ int handle_event();
+
+ SketcherWindow *gui;
+};
+
+class SketcherResetCurves : public BC_GenericButton
+{
+public:
+ SketcherResetCurves(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherResetCurves();
+
+ int handle_event();
+
+ Sketcher *plugin;
+ SketcherWindow *gui;
+};
+
+class SketcherResetPoints : public BC_GenericButton
+{
+public:
+ SketcherResetPoints(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+ ~SketcherResetPoints();
+
+ int handle_event();
+
+ Sketcher *plugin;
+ SketcherWindow *gui;
+};
+
+
+class SketcherWindow : public PluginClientWindow
+{
+public:
+ SketcherWindow(Sketcher *plugin);
+ ~SketcherWindow();
+
+ void create_objects();
+ void update_gui();
+ void start_color_thread(SketcherCurveColor *curve_color);
+ int grab_event(XEvent *event);
+ int do_grab_event(XEvent *event);
+ void done_event(int result);
+ void send_configure_change();
+ int keypress_event();
+
+ Sketcher *plugin;
+
+ BC_Title *title_type, *title_pen;
+ BC_Title *title_color, *title_radius;
+ SketcherCurveType *curve_type;
+ SketcherCurvePen *curve_pen;
+ SketcherCurveColor *curve_color;
+ SketcherCurveColorPicker *color_picker;
+ SketcherNewCurve *new_curve;
+ SketcherDelCurve *del_curve;
+ SketcherCurveUp *curve_up;
+ SketcherCurveDn *curve_dn;
+ SketcherCurveRadius *curve_radius;
+ SketcherCurveList *curve_list;
+ SketcherResetCurves *reset_curves;
+
+ BC_Title *title_x, *title_y;
+ SketcherPointX *point_x;
+ SketcherPointY *point_y;
+ SketcherNewPoint *new_point;
+ SketcherDelPoint *del_point;
+ SketcherPointUp *point_up;
+ SketcherPointDn *point_dn;
+ int dragging, pending_config;
+ int new_points;
+ float last_x, last_y;
+ SketcherDrag *drag;
+ SketcherPointList *point_list;
+ SketcherResetPoints *reset_points;
+ BC_Title *notes0, *notes1, *notes2;
+};
+#endif
+
int TitleColorButton::handle_event()
{
window->color_thread->start_window(client->config.color,
- client->config.alpha);
+ client->config.alpha, 1);
return 1;
}
TitleOutlineColorButton::TitleOutlineColorButton(TitleMain *client, TitleWindow *window, int x, int y)
int TitleOutlineColorButton::handle_event()
{
window->outline_color_thread->start_window(client->config.outline_color,
- client->config.outline_alpha);
+ client->config.outline_alpha, 1);
return 1;
}
return 1;
}
+void TitleColorThread::handle_done_event(int result)
+{
+ if( result ) {
+ client->config.color = orig_color;
+ client->config.alpha = orig_alpha;
+ handle_new_color(orig_color, orig_alpha);
+ window->update_color();
+ window->send_configure_change();
+ }
+}
+
TitleDrag::TitleDrag(TitleMain *client, TitleWindow *window, int x, int y)
: DragCheckBox(client->server->mwindow, x, y, _("Drag"), &client->config.drag,
client->config.title_x, client->config.title_y,
}
void TitleColorPopup::handle_done_event(int result)
{
- if( result ) return;
- char txt[BCSTRLEN]; sprintf(txt, "<%s #%06x>", _(KW_COLOR), color_value);
- window->insert_ibeam(txt);
+ if( !result ) {
+ char txt[BCSTRLEN]; sprintf(txt, "<%s #%06x>", _(KW_COLOR), color_value);
+ window->insert_ibeam(txt);
+ }
}
TitlePngPopup::TitlePngPopup(TitleMain *client, TitleWindow *window)
public:
TitleColorThread(TitleMain *client, TitleWindow *window, int is_outline);
virtual int handle_new_color(int output, int alpha);
+ void handle_done_event(int result);
TitleMain *client;
TitleWindow *window;
int is_outline;
diff -urN a/libavformat/avformat.h b/libavformat/avformat.h
---- a/libavformat/avformat.h 2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/avformat.h 2018-04-24 11:02:20.777232001 -0600
+--- a/libavformat/avformat.h 2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/avformat.h 2018-11-08 07:25:17.066799941 -0700
@@ -487,6 +487,9 @@
The user or muxer can override this through
AVFormatContext.avoid_negative_ts
int flags;
diff -urN a/libavformat/dv.c b/libavformat/dv.c
---- a/libavformat/dv.c 2018-04-13 17:34:28.000000000 -0600
-+++ b/libavformat/dv.c 2018-04-24 11:02:20.778232001 -0600
+--- a/libavformat/dv.c 2018-11-01 12:34:26.000000000 -0600
++++ b/libavformat/dv.c 2018-11-08 07:25:17.066799941 -0700
@@ -632,6 +632,7 @@
AVInputFormat ff_dv_demuxer = {
.name = "dv",
.read_probe = dv_probe,
.read_header = dv_read_header,
diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
---- a/libavformat/matroskadec.c 2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/matroskadec.c 2018-04-24 11:02:20.779232001 -0600
-@@ -4026,6 +4026,7 @@
+--- a/libavformat/matroskadec.c 2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/matroskadec.c 2018-11-08 07:25:17.067799930 -0700
+@@ -4030,6 +4030,7 @@
AVInputFormat ff_matroska_demuxer = {
.name = "matroska,webm",
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
.extensions = "mkv,mk3d,mka,mks",
.priv_data_size = sizeof(MatroskaDemuxContext),
.read_probe = matroska_probe,
-@@ -4039,6 +4040,7 @@
+@@ -4043,6 +4044,7 @@
AVInputFormat ff_webm_dash_manifest_demuxer = {
.name = "webm_dash_manifest",
.long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
.read_header = webm_dash_manifest_read_header,
.read_packet = webm_dash_manifest_read_packet,
diff -urN a/libavformat/utils.c b/libavformat/utils.c
---- a/libavformat/utils.c 2018-04-20 04:02:58.000000000 -0600
-+++ b/libavformat/utils.c 2018-04-24 11:02:20.780232001 -0600
-@@ -2471,6 +2471,13 @@
+--- a/libavformat/utils.c 2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/utils.c 2018-11-08 07:25:17.069799908 -0700
+@@ -2472,6 +2472,13 @@
return seek_frame_byte(s, stream_index, timestamp, flags);
}
diff -urN a/libavformat/avformat.h b/libavformat/avformat.h
---- a/libavformat/avformat.h 2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/avformat.h 2018-04-24 11:02:20.777232001 -0600
+--- a/libavformat/avformat.h 2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/avformat.h 2018-11-08 07:25:17.066799941 -0700
@@ -487,6 +487,9 @@
The user or muxer can override this through
AVFormatContext.avoid_negative_ts
int flags;
diff -urN a/libavformat/dv.c b/libavformat/dv.c
---- a/libavformat/dv.c 2018-04-13 17:34:28.000000000 -0600
-+++ b/libavformat/dv.c 2018-04-24 11:02:20.778232001 -0600
+--- a/libavformat/dv.c 2018-11-01 12:34:26.000000000 -0600
++++ b/libavformat/dv.c 2018-11-08 07:25:17.066799941 -0700
@@ -632,6 +632,7 @@
AVInputFormat ff_dv_demuxer = {
.name = "dv",
.read_probe = dv_probe,
.read_header = dv_read_header,
diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
---- a/libavformat/matroskadec.c 2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/matroskadec.c 2018-04-24 11:02:20.779232001 -0600
-@@ -4026,6 +4026,7 @@
+--- a/libavformat/matroskadec.c 2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/matroskadec.c 2018-11-08 07:25:17.067799930 -0700
+@@ -4030,6 +4030,7 @@
AVInputFormat ff_matroska_demuxer = {
.name = "matroska,webm",
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
.extensions = "mkv,mk3d,mka,mks",
.priv_data_size = sizeof(MatroskaDemuxContext),
.read_probe = matroska_probe,
-@@ -4039,6 +4040,7 @@
+@@ -4043,6 +4044,7 @@
AVInputFormat ff_webm_dash_manifest_demuxer = {
.name = "webm_dash_manifest",
.long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
.read_header = webm_dash_manifest_read_header,
.read_packet = webm_dash_manifest_read_packet,
diff -urN a/libavformat/utils.c b/libavformat/utils.c
---- a/libavformat/utils.c 2018-04-20 04:02:58.000000000 -0600
-+++ b/libavformat/utils.c 2018-04-24 11:02:20.780232001 -0600
-@@ -2471,6 +2471,13 @@
+--- a/libavformat/utils.c 2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/utils.c 2018-11-08 07:25:17.069799908 -0700
+@@ -2472,6 +2472,13 @@
return seek_frame_byte(s, stream_index, timestamp, flags);
}