Checking in debug_view, some extra missing utils, and the y2016 target_sender code.

Change-Id: I241947265da8f332c39862f4d0ddcdc2d29c7b68
diff --git a/aos/vision/debug/debug_viewer.cc b/aos/vision/debug/debug_viewer.cc
new file mode 100644
index 0000000..331f733
--- /dev/null
+++ b/aos/vision/debug/debug_viewer.cc
@@ -0,0 +1,166 @@
+#include "aos/vision/debug/debug_viewer.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "aos/vision/image/image_types.h"
+
+namespace aos {
+namespace vision {
+
+template <typename T, gboolean (T::*DrawMethod)(cairo_t *cr)>
+gboolean DrawCallback(GtkWidget *, cairo_t *cr, gpointer data) {
+  return ((*((T *)data)).*DrawMethod)(cr);
+}
+
+template <typename T, gboolean (T::*DrawMethod)(cairo_t *cr)>
+void g_draw_signal_connect(GtkWidget *widget, T *obj) {
+  gboolean (*fnptr)(GtkWidget *, cairo_t *, gpointer) =
+      DrawCallback<T, DrawMethod>;
+  g_signal_connect(widget, "draw", G_CALLBACK(fnptr), obj);
+}
+
+struct DebugViewer::Internals {
+  Internals(bool flip) : flip_(flip) {}
+
+  gboolean Draw(cairo_t *cr) {
+    needs_draw = false;
+    cairo_scale(cr, scale_factor, scale_factor);
+    if (pixbuf != nullptr) {
+      cairo_save(cr);
+      if (flip_) {
+        cairo_translate(cr, ptr.fmt().w, ptr.fmt().h);
+        cairo_scale(cr, -1, -1);
+      }
+      gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0);
+      cairo_paint(cr);
+      cairo_restore(cr);
+    }
+
+    int w = ptr.fmt().w;
+    int h = ptr.fmt().h;
+    if (overlays) {
+      for (const auto &ov : *overlays) {
+        cairo_save(cr);
+        CairoRender render(cr);
+        // move the drawing to match the window size
+        ov->Draw(&render, w, h);
+        cairo_restore(cr);
+      }
+    }
+
+    return FALSE;
+  }
+
+  GdkPixbuf *pixbuf = nullptr;
+  GtkWidget *drawing_area = nullptr;
+  ImagePtr ptr;
+  bool needs_draw = true;
+  GtkWidget *window;
+  std::vector<OverlayBase *> *overlays = nullptr;
+  double scale_factor;
+
+  // flip the image rows on drawing
+  bool flip_ = false;
+
+  // clear per frame
+  bool clear_per_frame_ = true;
+};
+
+void DebugViewer::SetOverlays(std::vector<OverlayBase *> *overlays) {
+  self->overlays = overlays;
+}
+
+void DebugViewer::Redraw() {
+  if (!self->needs_draw) {
+    gtk_widget_queue_draw(self->drawing_area);
+    self->needs_draw = true;
+  }
+}
+
+void DebugViewer::UpdateImage(ImagePtr ptr) {
+  if (ptr.data() != self->ptr.data()) {
+    int w = ptr.fmt().w;
+    int h = ptr.fmt().h;
+    self->pixbuf = gdk_pixbuf_new_from_data(
+        (const unsigned char *)ptr.data(), GDK_COLORSPACE_RGB, FALSE, 8,
+        ptr.fmt().w, ptr.fmt().h, 3 * ptr.fmt().w, NULL, NULL);
+    self->ptr = ptr;
+
+    gtk_window_set_default_size(GTK_WINDOW(self->window), w * scale_factor,
+                                h * scale_factor);
+
+    gtk_widget_set_size_request(self->drawing_area, w * scale_factor,
+                                h * scale_factor);
+    window_height_ = h;
+    window_width_ = w;
+  }
+}
+
+void DebugViewer::MoveTo(int x, int y) {
+  gtk_window_move(GTK_WINDOW(self->window), x, y);
+}
+
+void DebugViewer::SetScale(double scale_factor_inp) {
+  int w = window_width_;
+  int h = window_height_;
+
+  scale_factor = scale_factor_inp;
+  self->scale_factor = scale_factor;
+
+  gtk_window_resize(GTK_WINDOW(self->window), w * scale_factor,
+                    h * scale_factor);
+
+  gtk_widget_set_size_request(self->drawing_area, w * scale_factor,
+                              h * scale_factor);
+}
+
+gboolean debug_viewer_key_press_event(GtkWidget * /*widget*/,
+                                      GdkEventKey *event, gpointer user_data) {
+  auto &key_press_cb =
+      reinterpret_cast<DebugViewer *>(user_data)->key_press_event;
+  if (key_press_cb) key_press_cb(event->keyval);
+  return FALSE;
+}
+
+DebugViewer::DebugViewer(bool flip) : self(new Internals(flip)) {
+  self->scale_factor = scale_factor;
+  GtkWidget *window;
+  auto drawing_area = self->drawing_area = gtk_drawing_area_new();
+  gtk_widget_set_size_request(drawing_area, window_width_ * scale_factor,
+                              window_height_ * scale_factor);
+  gtk_widget_add_events(drawing_area, GDK_KEY_PRESS_MASK);
+
+  g_draw_signal_connect<DebugViewer::Internals, &DebugViewer::Internals::Draw>(
+      drawing_area, self.get());
+
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  self->window = window;
+  g_signal_connect(window, "key-press-event",
+                   G_CALLBACK(debug_viewer_key_press_event), this);
+  gtk_window_set_title(GTK_WINDOW(window), "Window");
+  gtk_window_set_default_size(GTK_WINDOW(window), window_width_ * scale_factor,
+                              window_height_ * scale_factor);
+
+  gtk_container_add(GTK_CONTAINER(window), drawing_area);
+  gtk_widget_show_all(window);
+}
+DebugViewer::~DebugViewer() {}
+
+void CairoRender::Text(int x, int y, int /*text_x*/, int /*text_y*/,
+                       const std::string &text) {
+  auto *pango_lay = pango_cairo_create_layout(cr_);
+  cairo_move_to(cr_, x, y);
+  pango_layout_set_text(pango_lay, text.data(), text.size());
+  pango_cairo_show_layout(cr_, pango_lay);
+  g_object_unref(pango_lay);
+}
+
+}  // namespace vision
+}  // namespace aos