blob: 331f733ae62ab9385b983719d8f5343c6c82c8a2 [file] [log] [blame]
Parker Schuh2cd173d2017-01-28 00:12:01 -08001#include "aos/vision/debug/debug_viewer.h"
2
3#include <gdk-pixbuf/gdk-pixbuf.h>
4#include <gtk/gtk.h>
5#include <poll.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <functional>
9#include <memory>
10#include <vector>
11
12#include "aos/vision/image/image_types.h"
13
14namespace aos {
15namespace vision {
16
17template <typename T, gboolean (T::*DrawMethod)(cairo_t *cr)>
18gboolean DrawCallback(GtkWidget *, cairo_t *cr, gpointer data) {
19 return ((*((T *)data)).*DrawMethod)(cr);
20}
21
22template <typename T, gboolean (T::*DrawMethod)(cairo_t *cr)>
23void g_draw_signal_connect(GtkWidget *widget, T *obj) {
24 gboolean (*fnptr)(GtkWidget *, cairo_t *, gpointer) =
25 DrawCallback<T, DrawMethod>;
26 g_signal_connect(widget, "draw", G_CALLBACK(fnptr), obj);
27}
28
29struct DebugViewer::Internals {
30 Internals(bool flip) : flip_(flip) {}
31
32 gboolean Draw(cairo_t *cr) {
33 needs_draw = false;
34 cairo_scale(cr, scale_factor, scale_factor);
35 if (pixbuf != nullptr) {
36 cairo_save(cr);
37 if (flip_) {
38 cairo_translate(cr, ptr.fmt().w, ptr.fmt().h);
39 cairo_scale(cr, -1, -1);
40 }
41 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0);
42 cairo_paint(cr);
43 cairo_restore(cr);
44 }
45
46 int w = ptr.fmt().w;
47 int h = ptr.fmt().h;
48 if (overlays) {
49 for (const auto &ov : *overlays) {
50 cairo_save(cr);
51 CairoRender render(cr);
52 // move the drawing to match the window size
53 ov->Draw(&render, w, h);
54 cairo_restore(cr);
55 }
56 }
57
58 return FALSE;
59 }
60
61 GdkPixbuf *pixbuf = nullptr;
62 GtkWidget *drawing_area = nullptr;
63 ImagePtr ptr;
64 bool needs_draw = true;
65 GtkWidget *window;
66 std::vector<OverlayBase *> *overlays = nullptr;
67 double scale_factor;
68
69 // flip the image rows on drawing
70 bool flip_ = false;
71
72 // clear per frame
73 bool clear_per_frame_ = true;
74};
75
76void DebugViewer::SetOverlays(std::vector<OverlayBase *> *overlays) {
77 self->overlays = overlays;
78}
79
80void DebugViewer::Redraw() {
81 if (!self->needs_draw) {
82 gtk_widget_queue_draw(self->drawing_area);
83 self->needs_draw = true;
84 }
85}
86
87void DebugViewer::UpdateImage(ImagePtr ptr) {
88 if (ptr.data() != self->ptr.data()) {
89 int w = ptr.fmt().w;
90 int h = ptr.fmt().h;
91 self->pixbuf = gdk_pixbuf_new_from_data(
92 (const unsigned char *)ptr.data(), GDK_COLORSPACE_RGB, FALSE, 8,
93 ptr.fmt().w, ptr.fmt().h, 3 * ptr.fmt().w, NULL, NULL);
94 self->ptr = ptr;
95
96 gtk_window_set_default_size(GTK_WINDOW(self->window), w * scale_factor,
97 h * scale_factor);
98
99 gtk_widget_set_size_request(self->drawing_area, w * scale_factor,
100 h * scale_factor);
101 window_height_ = h;
102 window_width_ = w;
103 }
104}
105
106void DebugViewer::MoveTo(int x, int y) {
107 gtk_window_move(GTK_WINDOW(self->window), x, y);
108}
109
110void DebugViewer::SetScale(double scale_factor_inp) {
111 int w = window_width_;
112 int h = window_height_;
113
114 scale_factor = scale_factor_inp;
115 self->scale_factor = scale_factor;
116
117 gtk_window_resize(GTK_WINDOW(self->window), w * scale_factor,
118 h * scale_factor);
119
120 gtk_widget_set_size_request(self->drawing_area, w * scale_factor,
121 h * scale_factor);
122}
123
124gboolean debug_viewer_key_press_event(GtkWidget * /*widget*/,
125 GdkEventKey *event, gpointer user_data) {
126 auto &key_press_cb =
127 reinterpret_cast<DebugViewer *>(user_data)->key_press_event;
128 if (key_press_cb) key_press_cb(event->keyval);
129 return FALSE;
130}
131
132DebugViewer::DebugViewer(bool flip) : self(new Internals(flip)) {
133 self->scale_factor = scale_factor;
134 GtkWidget *window;
135 auto drawing_area = self->drawing_area = gtk_drawing_area_new();
136 gtk_widget_set_size_request(drawing_area, window_width_ * scale_factor,
137 window_height_ * scale_factor);
138 gtk_widget_add_events(drawing_area, GDK_KEY_PRESS_MASK);
139
140 g_draw_signal_connect<DebugViewer::Internals, &DebugViewer::Internals::Draw>(
141 drawing_area, self.get());
142
143 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
144 self->window = window;
145 g_signal_connect(window, "key-press-event",
146 G_CALLBACK(debug_viewer_key_press_event), this);
147 gtk_window_set_title(GTK_WINDOW(window), "Window");
148 gtk_window_set_default_size(GTK_WINDOW(window), window_width_ * scale_factor,
149 window_height_ * scale_factor);
150
151 gtk_container_add(GTK_CONTAINER(window), drawing_area);
152 gtk_widget_show_all(window);
153}
154DebugViewer::~DebugViewer() {}
155
156void CairoRender::Text(int x, int y, int /*text_x*/, int /*text_y*/,
157 const std::string &text) {
158 auto *pango_lay = pango_cairo_create_layout(cr_);
159 cairo_move_to(cr_, x, y);
160 pango_layout_set_text(pango_lay, text.data(), text.size());
161 pango_cairo_show_layout(cr_, pango_lay);
162 g_object_unref(pango_lay);
163}
164
165} // namespace vision
166} // namespace aos