$ git clone http://tcclient.ion.nu/tc_client.git
commit 0eb1f608ed1cfdf7f561800a1f2f925a2f51c078
Author: Alicia <...>
Date: Mon May 2 22:18:25 2016 +0200
tc_client-gtk: added horizontal and vertical flip as postprocessing options.
diff --git a/ChangeLog b/ChangeLog
index b563d52..2c63734 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,7 @@ modbot: only use youtube-dl's 'ytsearch:' for --get-id, and only if it doesn't a
tc_client-gtk: handle only one sendmessage event at a time, and don't send empty lines.
tc_client-gtk: added postprocessing options for cams upon right-click, starting with brightness adjustment.
tc_client-gtk: moved encoding out of the cam thread, allowing postprocessing to be applied to the broadcasted frame.
+tc_client-gtk: added horizontal and vertical flip as postprocessing options.
tc_client-gtk and camviewer: fixed compatibility with newer libavutil.
0.38:
Handle multi-line messages.
diff --git a/gtkgui.glade b/gtkgui.glade
index 11054af..71be1c7 100644
--- a/gtkgui.glade
+++ b/gtkgui.glade
@@ -497,7 +497,7 @@
<object class="GtkMenuItem" id="cam_menu_colors">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">Adjust color levels</property>
+ <property name="label" translatable="yes">Adjust image</property>
<property name="use_underline">True</property>
</object>
</child>
@@ -1228,6 +1228,47 @@
<property name="position">4</property>
</packing>
</child>
+ <child>
+ <object class="GtkSeparator" id="separator1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="camcolors_flip_horizontal">
+ <property name="label" translatable="yes">Flip horizontally</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="camcolors_flip_vertical">
+ <property name="label" translatable="yes">Flip vertically</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">7</property>
+ </packing>
+ </child>
</object>
</child>
</object>
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index 175dbc9..4c7d336 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -578,7 +578,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
struct SwsContext* swsctx=sws_getContext(cam->frame->width, cam->frame->height, cam->frame->format, camsize_scale.width, camsize_scale.height, AV_PIX_FMT_RGB24, 0, 0, 0, 0);
sws_scale(swsctx, (const uint8_t*const*)cam->frame->data, cam->frame->linesize, 0, cam->frame->height, cam->dstframe->data, cam->dstframe->linesize);
sws_freeContext(swsctx);
- camera_postproc(cam, cam->dstframe->data[0], camsize_scale.width*camsize_scale.height);
+ camera_postproc(cam, cam->dstframe->data[0], camsize_scale.width, camsize_scale.height);
GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(cam->cam));
GdkPixbuf* gdkframe=gdk_pixbuf_new_from_data(cam->dstframe->data[0], GDK_COLORSPACE_RGB, 0, 8, camsize_scale.width, camsize_scale.height, cam->dstframe->linesize[0], freebuffer, 0);
@@ -1035,6 +1035,8 @@ int main(int argc, char** argv)
g_signal_connect(gtk_builder_get_object(gui, "camcolors_min_brightness"), "value-changed", G_CALLBACK(camcolors_adjust_min), 0);
g_signal_connect(gtk_builder_get_object(gui, "camcolors_max_brightness"), "value-changed", G_CALLBACK(camcolors_adjust_max), 0);
g_signal_connect(gtk_builder_get_object(gui, "camcolors_auto"), "toggled", G_CALLBACK(camcolors_toggle_auto), 0);
+ g_signal_connect(gtk_builder_get_object(gui, "camcolors_flip_horizontal"), "toggled", G_CALLBACK(camcolors_toggle_flip), 0);
+ g_signal_connect(gtk_builder_get_object(gui, "camcolors_flip_vertical"), "toggled", G_CALLBACK(camcolors_toggle_flip), (void*)1);
// Populate saved channels
GtkWidget* startbox=GTK_WIDGET(gtk_builder_get_object(gui, "startbox"));
int channelcount=config_get_int("channelcount");
diff --git a/utilities/gtk/gui.c b/utilities/gtk/gui.c
index 08f674b..4f25dca 100644
--- a/utilities/gtk/gui.c
+++ b/utilities/gtk/gui.c
@@ -513,3 +513,17 @@ void camcolors_toggle_auto(GtkToggleButton* button, void* x)
if(!cam){return;}
cam->postproc.autoadjust=gtk_toggle_button_get_active(button);
}
+
+void camcolors_toggle_flip(GtkToggleButton* button, void* vertical)
+{
+ if(!menu_context_cam){return;}
+ struct camera* cam=camera_find(menu_context_cam);
+ if(!cam){return;}
+ char v=gtk_toggle_button_get_active(button);
+ if(vertical)
+ {
+ cam->postproc.flip_vertical=v;
+ }else{
+ cam->postproc.flip_horizontal=v;
+ }
+}
diff --git a/utilities/gtk/gui.h b/utilities/gtk/gui.h
index a2b46ae..4c90984 100644
--- a/utilities/gtk/gui.h
+++ b/utilities/gtk/gui.h
@@ -43,5 +43,6 @@ extern void gui_show_camcolors(GtkMenuItem* menuitem, void* x);
extern void camcolors_adjust_min(GtkAdjustment* adjustment, void* x);
extern void camcolors_adjust_max(GtkAdjustment* adjustment, void* x);
extern void camcolors_toggle_auto(GtkToggleButton* button, void* x);
+extern void camcolors_toggle_flip(GtkToggleButton* button, void* vertical);
extern GtkBuilder* gui;
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index 4aa9766..5135f3c 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -171,6 +171,8 @@ struct camera* camera_new(const char* nick, const char* id)
cam->postproc.min_brightness=0;
cam->postproc.max_brightness=255;
cam->postproc.autoadjust=0;
+ cam->postproc.flip_horizontal=0;
+ cam->postproc.flip_vertical=0;
return cam;
}
@@ -220,7 +222,7 @@ gboolean cam_encode(GIOChannel* iochannel, GIOCondition condition, gpointer data
av_image_alloc(cam->frame->data, cam->frame->linesize, camsize_out.width, camsize_out.height, cam->frame->format, 1);
}
g_io_channel_read_chars(iochannel, (void*)cam->frame->data[0], camsize_out.width*camsize_out.height*3, 0, 0);
- camera_postproc(cam, cam->frame->data[0], cam->frame->width*cam->frame->height);
+ camera_postproc(cam, cam->frame->data[0], cam->frame->width, cam->frame->height);
// Update our local display
GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(cam->cam));
GdkPixbuf* gdkframe=gdk_pixbuf_new_from_data(cam->frame->data[0], GDK_COLORSPACE_RGB, 0, 8, cam->frame->width, cam->frame->height, cam->frame->linesize[0], 0, 0);
@@ -377,27 +379,56 @@ const char* camselect_file(void)
return file;
}
-void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int count)
+void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int width, unsigned int height)
{
- if(cam->postproc.min_brightness==0 && cam->postproc.max_brightness==255 && !cam->postproc.autoadjust){return;}
- unsigned char min=255;
- unsigned char max=0;
- unsigned int i;
- for(i=0; i<count*3; ++i)
+ if(cam->postproc.min_brightness!=0 || cam->postproc.max_brightness!=255 || cam->postproc.autoadjust)
{
+ unsigned char min=255;
+ unsigned char max=0;
+ unsigned int count=width*height;
+ unsigned int i;
+ for(i=0; i<count*3; ++i)
+ {
+ if(cam->postproc.autoadjust)
+ {
+ if(buf[i]<min){min=buf[i];}
+ if(buf[i]>max){max=buf[i];}
+ }
+ double v=((double)buf[i]-cam->postproc.min_brightness)*255/(cam->postproc.max_brightness-cam->postproc.min_brightness);
+ if(v<0){v=0;}
+ if(v>255){v=255;}
+ buf[i]=v;
+ }
if(cam->postproc.autoadjust)
{
- if(buf[i]<min){min=buf[i];}
- if(buf[i]>max){max=buf[i];}
+ cam->postproc.min_brightness=min;
+ cam->postproc.max_brightness=max;
}
- double v=((double)buf[i]-cam->postproc.min_brightness)*255/(cam->postproc.max_brightness-cam->postproc.min_brightness);
- if(v<0){v=0;}
- if(v>255){v=255;}
- buf[i]=v;
}
- if(cam->postproc.autoadjust)
+ if(cam->postproc.flip_horizontal)
{
- cam->postproc.min_brightness=min;
- cam->postproc.max_brightness=max;
+ unsigned int x;
+ unsigned int y;
+ for(y=0; y<height; ++y)
+ for(x=0; x<width/2; ++x)
+ {
+ unsigned char pixel[3];
+ memcpy(pixel, &buf[(y*width+x)*3], 3);
+ memcpy(&buf[(y*width+x)*3], &buf[(y*width+(width-x-1))*3], 3);
+ memcpy(&buf[(y*width+(width-x-1))*3], pixel, 3);
+ }
+ }
+ if(cam->postproc.flip_vertical)
+ {
+ unsigned int x;
+ unsigned int y;
+ for(y=0; y<height/2; ++y)
+ for(x=0; x<width; ++x)
+ {
+ unsigned char pixel[3];
+ memcpy(pixel, &buf[(y*width+x)*3], 3);
+ memcpy(&buf[(y*width+x)*3], &buf[((height-y-1)*width+x)*3], 3);
+ memcpy(&buf[((height-y-1)*width+x)*3], pixel, 3);
+ }
}
}
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index 6426b89..3a0a701 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -33,6 +33,8 @@ struct camera
double min_brightness;
double max_brightness;
char autoadjust;
+ char flip_horizontal;
+ char flip_vertical;
} postproc;
};
struct size
@@ -68,4 +70,4 @@ extern void camselect_change(GtkComboBox* combo, AVCodec* vencoder);
extern gboolean camselect_cancel(GtkWidget* widget, void* x1, void* x2);
extern void camselect_accept(GtkWidget* widget, AVCodec* vencoder);
extern const char* camselect_file(void);
-extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int count);
+extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int width, unsigned int height);