diff --git a/common/utils/T/tracer/Makefile.remote b/common/utils/T/tracer/Makefile.remote
index c539a2c3886d2cdf72a69c87559c51cde2aaf210..be8a77250cd7328137f05411b2f6ba68cf91c9ba 100644
--- a/common/utils/T/tracer/Makefile.remote
+++ b/common/utils/T/tracer/Makefile.remote
@@ -3,7 +3,7 @@ CFLAGS=-Wall -g -pthread -DT_TRACER -I.
 
 #CFLAGS += -O3 -ffast-math -fomit-frame-pointer
 
-LIBS=-lX11 -lm
+LIBS=-lX11 -lm -lpng
 
 tracer_remote: remote_old.o plot.o database.o gui.o utils.o gui/gui.a
 	$(CC) $(CFLAGS) -o tracer_remote $^ $(LIBS)
diff --git a/common/utils/T/tracer/gui/Makefile b/common/utils/T/tracer/gui/Makefile
index fb3a9486d832355c2508755c0c4abd21c1e6992e..370020a8b56a3634931ba8d170974b8893361c13 100644
--- a/common/utils/T/tracer/gui/Makefile
+++ b/common/utils/T/tracer/gui/Makefile
@@ -3,7 +3,7 @@ CFLAGS=-Wall -g -pthread
 
 OBJS=init.o loop.o toplevel_window.o x.o container.o widget.o \
      gui.o label.o event.o xy_plot.o textlist.o notify.o positioner.o \
-     timeline.o space.o
+     timeline.o space.o image.o
 
 gui.a: $(OBJS)
 	ar cr gui.a $(OBJS)
diff --git a/common/utils/T/tracer/gui/gui.h b/common/utils/T/tracer/gui/gui.h
index 208a3c2a5474c81fe61b7a90bdc9693bed44df0f..8ab0f063739d543c8b5f35e084f2ca4205d2ae5c 100644
--- a/common/utils/T/tracer/gui/gui.h
+++ b/common/utils/T/tracer/gui/gui.h
@@ -34,6 +34,7 @@ widget *new_textlist(gui *gui, int width, int nlines, int background_color);
 widget *new_timeline(gui *gui, int width, int number_of_sublines,
     int subline_height);
 widget *new_space(gui *gui, int width, int height);
+widget *new_image(gui *gui, unsigned char *data, int length);
 
 void label_set_clickable(gui *gui, widget *label, int clickable);
 
diff --git a/common/utils/T/tracer/gui/gui_defs.h b/common/utils/T/tracer/gui/gui_defs.h
index dc3559c82c7e14ac685bcfaf93e9e47d39832a8a..ec72bb0e2bb14e8951f8bf7e5d3acda960109de8 100644
--- a/common/utils/T/tracer/gui/gui_defs.h
+++ b/common/utils/T/tracer/gui/gui_defs.h
@@ -31,7 +31,7 @@ extern int volatile gui_logd;
 
 enum widget_type {
   TOPLEVEL_WINDOW, CONTAINER, POSITIONER, TEXT_LIST, XY_PLOT, BUTTON, LABEL,
-  TIMELINE, SPACE
+  TIMELINE, SPACE, IMAGE
 };
 
 struct widget_list;
@@ -154,6 +154,13 @@ struct space_widget {
   int wanted_height;
 };
 
+struct image_widget {
+  struct widget common;
+  int width;
+  int height;
+  void *x;             /* opaque X data (type x_image), used in x.c */
+};
+
 /*************************************************************************/
 /*                             events                                    */
 /*************************************************************************/
diff --git a/common/utils/T/tracer/gui/image.c b/common/utils/T/tracer/gui/image.c
new file mode 100644
index 0000000000000000000000000000000000000000..70618239aa66800b64bbd669e7740b4654aec4da
--- /dev/null
+++ b/common/utils/T/tracer/gui/image.c
@@ -0,0 +1,112 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <png.h>
+
+static void paint(gui *_gui, widget *_w)
+{
+  struct gui *g = _gui;
+  struct image_widget *w = _w;
+  x_draw_image(g->x, g->xwin, w->x, w->common.x, w->common.y);
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct image_widget *w = _w;
+  LOGD("HINTS image %p\n", w);
+  *width = w->width;
+  *height = w->height;
+}
+
+struct png_reader {
+  unsigned char *data;
+  int size;
+  int pos;
+};
+
+static void png_readfn(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+  struct png_reader *r = png_get_io_ptr(png_ptr);
+  if (length > r->size - r->pos) png_error(png_ptr, "bad png image");
+  memcpy(data, r->data + r->pos, length);
+  r->pos += length;
+}
+
+static void load_image(struct gui *g, struct image_widget *w,
+    unsigned char *data, int length)
+{
+  png_structp png_ptr;
+  png_infop info_ptr;
+  png_bytepp image;
+  int width, height, bit_depth, color_type, channels;
+  unsigned char *img_data;
+  struct png_reader r;
+  int i;
+
+  /* unpack PNG data */
+
+  r.data = data;
+  r.size = length;
+  r.pos = 0;
+
+  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  if (png_ptr == NULL) abort();
+
+  info_ptr = png_create_info_struct(png_ptr);
+  if (info_ptr == NULL) abort();
+
+  if (setjmp(png_jmpbuf(png_ptr))) abort();
+
+  png_set_read_fn(png_ptr, &r, png_readfn);
+
+  png_read_png(png_ptr, info_ptr,
+      PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_PACKING |
+      PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_BGR, NULL);
+
+  image = png_get_rows(png_ptr, info_ptr);
+
+  width = png_get_image_width(png_ptr, info_ptr);
+  height = png_get_image_height(png_ptr, info_ptr);
+  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+  color_type = png_get_color_type(png_ptr, info_ptr);
+  channels = png_get_channels(png_ptr, info_ptr);
+
+  if (width < 1 || width > 1000 || height < 1 || height > 1000 ||
+      bit_depth != 8 || color_type != PNG_COLOR_TYPE_RGBA || channels != 4)
+    { printf("bad image\n"); abort(); }
+
+  img_data = malloc(4 * width * height); if (img_data == NULL) abort();
+  for (i = 0; i < height; i++)
+    memcpy(img_data+i*4*width, image[i], width*4);
+
+  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+  /* create the X image */
+  w->x = x_create_image(g->x, img_data, width, height);
+
+  free(img_data);
+
+  w->width = width;
+  w->height = height;
+}
+
+widget *new_image(gui *_gui, unsigned char *data, int length)
+{
+  struct gui *g = _gui;
+  struct image_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, IMAGE, sizeof(struct image_widget));
+
+  load_image(g, w, data, length);
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+
+  gunlock(g);
+
+  return w;
+}
diff --git a/common/utils/T/tracer/gui/widget.c b/common/utils/T/tracer/gui/widget.c
index 149f4be7f593d2e5ce5eac32499416af4c4361bb..362d2e1e4898c39960033fe206518d902a3ba897 100644
--- a/common/utils/T/tracer/gui/widget.c
+++ b/common/utils/T/tracer/gui/widget.c
@@ -265,7 +265,7 @@ void widget_dirty(gui *_gui, widget *_this)
 
 static const char *names[] = {
   "TOPLEVEL_WINDOW", "CONTAINER", "POSITIONER", "TEXT_LIST",
-  "XY_PLOT", "BUTTON", "LABEL", "TIMELINE", "SPACE"
+  "XY_PLOT", "BUTTON", "LABEL", "TIMELINE", "SPACE", "IMAGE"
 };
 const char *widget_name(enum widget_type type)
 {
@@ -279,6 +279,7 @@ const char *widget_name(enum widget_type type)
   case LABEL:
   case TIMELINE:
   case SPACE:
+  case IMAGE:
     return names[type];
   }
   return "UNKNOWN (error)";
diff --git a/common/utils/T/tracer/gui/x.c b/common/utils/T/tracer/gui/x.c
index 0a2d1450398ac58220026e96b5c13ee97dc766f2..5fb41b4f9249d55f1bb81c277c7969a31ab358c6 100644
--- a/common/utils/T/tracer/gui/x.c
+++ b/common/utils/T/tracer/gui/x.c
@@ -2,6 +2,7 @@
 #include "x_defs.h"
 #include "gui_defs.h"
 #include <X11/Xlib.h>
+#include <X11/Xutil.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -111,6 +112,52 @@ x_window *x_create_window(x_connection *_x, int width, int height,
   return ret;
 }
 
+x_image *x_create_image(x_connection *_x, unsigned char *data,
+    int width, int height)
+{
+  struct x_connection *x = _x;
+  struct x_image *ret;
+  XImage *ximage;
+  XVisualInfo *vs;
+  XVisualInfo template;
+  int nvs;
+  Visual *v;
+
+  ret = calloc(1, sizeof(struct x_image)); if (ret == NULL) OOM;
+
+  template.class = TrueColor;
+  template.depth = 24;
+  template.red_mask = 0xff0000;
+  template.green_mask = 0x00ff00;
+  template.blue_mask = 0x0000ff;
+  template.bits_per_rgb = 8;
+
+  vs = XGetVisualInfo(x->d, VisualDepthMask | VisualClassMask |
+      VisualRedMaskMask | VisualGreenMaskMask | VisualBlueMaskMask |
+      VisualBitsPerRGBMask, &template, &nvs);
+  if (vs == NULL || nvs == 0) ERR("no good visual found\n");
+  v = vs[0].visual;
+  XFree(vs);
+
+  ximage = XCreateImage(x->d, v, 24, ZPixmap, 0,
+      (char*)data, width, height, 32, 0);
+  if (ximage == NULL) ERR("image creation failed\n");
+
+  ret->p = XCreatePixmap(x->d, DefaultRootWindow(x->d), width, height, 24);
+
+  XPutImage(x->d, ret->p, DefaultGC(x->d, DefaultScreen(x->d)),
+      ximage, 0, 0, 0, 0, width, height);
+
+  /* TODO: be sure it's fine to set data to NULL */
+  ximage->data = NULL;
+  XDestroyImage(ximage);
+
+  ret->width = width;
+  ret->height = height;
+
+  return ret;
+}
+
 static struct toplevel_window_widget *find_x_window(struct gui *g, Window id)
 {
   struct widget_list *cur;
@@ -312,6 +359,16 @@ void x_draw_clipped_string(x_connection *_c, x_window *_w, int color,
   XSetClipMask(c->d, c->colors[color], None);
 }
 
+void x_draw_image(x_connection *_c, x_window *_w, x_image *_img, int x, int y)
+{
+  struct x_connection *c = _c;
+  struct x_window *w = _w;
+  struct x_image *img = _img;
+
+  XCopyArea(c->d, img->p, w->p, DefaultGC(c->d, DefaultScreen(c->d)),
+      0, 0, img->width, img->height, x, y);
+}
+
 void x_draw(x_connection *_c, x_window *_w)
 {
   struct x_connection *c = _c;
diff --git a/common/utils/T/tracer/gui/x.h b/common/utils/T/tracer/gui/x.h
index c1f8d33d89e8831c8a1882ecbf737763c02191fb..aca9868ae0ed568b41ee065a0eb8696774c2c8ca 100644
--- a/common/utils/T/tracer/gui/x.h
+++ b/common/utils/T/tracer/gui/x.h
@@ -5,12 +5,16 @@
 
 typedef void x_connection;
 typedef void x_window;
+typedef void x_image;
 
 x_connection *x_open(void);
 
 x_window *x_create_window(x_connection *x, int width, int height,
     char *title);
 
+x_image *x_create_image(x_connection *x, unsigned char *data,
+    int width, int height);
+
 int x_connection_fd(x_connection *x);
 
 void x_flush(x_connection *x);
@@ -42,6 +46,8 @@ void x_draw_clipped_string(x_connection *_c, x_window *_w, int color,
     int x, int y, const char *t,
     int clipx, int clipy, int clipwidth, int clipheight);
 
+void x_draw_image(x_connection *c, x_window *w, x_image *img, int x, int y);
+
 /* specials functions to plot many points
  * you call several times x_add_point() then x_plot_points()
  */
diff --git a/common/utils/T/tracer/gui/x_defs.h b/common/utils/T/tracer/gui/x_defs.h
index 41817d06ceb95ebb1dc1ec6ad11dcd1c0ad72fbf..e5874a1a3bc53ee0d21f064c34566bb5704b6bb4 100644
--- a/common/utils/T/tracer/gui/x_defs.h
+++ b/common/utils/T/tracer/gui/x_defs.h
@@ -23,4 +23,10 @@ struct x_window {
   int resize, new_width, new_height;
 };
 
+struct x_image {
+  Pixmap p;
+  int width;
+  int height;
+};
+
 #endif /* _X_DEFS_H_ */