$ 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);