Linux C编程——为eog image viewer增加坐标和像素颜色显示功能

2020-07-15 14:37:07 浏览数 (1)

好用的看图工具对做图片相关的算法验证很有帮助。但常常工具并没有我们需要的功能。今天我就分享一个工作中遇到的例子。

eog(eye of gnome)是gnome桌面下常用的看图工具,放大时可以禁用插值平滑算法,眼睛看到的更为真实。但eog缺少一个功能,鼠标在图片上移动时希望在状态栏能够显示以下的信息:

1.    显示鼠标当前位置在图片中的行列值, 2.    显示鼠标所处像素的RGB颜色值。

第一步:下载源码:

ftp://ftp.gnome.org/mirror/gnome.org/desktop/2.28/2.28.2/sources/eog-2.28.2.tar.gz

第二步:研究分析源码,确定修改方案

我们主要修改两个地方:

  • 修改响应鼠标事件的函数:
    • 获取鼠标相对于eog图片显示窗口的坐标。
    • 根据图片的长宽、放大倍数、图片第一个点的偏移量等计算鼠标所处的行、列值。
    • 根据行、列值从GdkPixbuf里取当前像素。
    • 把行、列、红、绿、蓝分别保存到scroll_view中。
    • 发送更新状态栏的消息。
  • 修改更新状态栏的函数:
    • 从scroll_view中获得行、列、红、绿、蓝等信息。
    • 显示到状态栏。

第三步:修改源码

  1. 定义全局变量
代码语言:txt复制
struct_EogScrollViewPrivate {
      //…
      int row, col;  //在scroll_view里增加全局变量,用来保存信息
      int r, g, b;
}
  1. 修改鼠标响应事件函数
代码语言:txt复制
static gboolean

eog_scroll_view_motion_event (GtkWidget _widget, GdkEventMotion_ event, gpointer data)
{
      EogScrollView *view;
      EogScrollViewPrivate *priv;
      gintx, y;
      GdkModifierTypemods;

  //增加定义局部变量

      int scaled_width, scaled_height;
      int width, height;
      int ximage, yimage;
      int wimage, himage;
      int row, col;
      int r, g, b;
      row= 0;
      col= 0;
      r= 0;
      g= 0;
      b= 0;
      ximage= 0;
      yimage= 0;
      wimage= 0;
      himage= 0;

      view= EOG_SCROLL_VIEW (data);
      priv= view->priv;

      //获得鼠标在图片显示窗口中的坐标
      if(event->is_hint)
        gdk_window_get_pointer (GTK_WIDGET(priv->display)->window, &x, &y, &mods);
      else{
        x = event->x;
        y = event->y;
      }

      //非拖拽事件,即鼠标移动事件
      if(!priv->dragging){
        //计算图片缩放后的大小,单位像素
        compute_scaled_size (view, priv->zoom,&scaled_width, &scaled_height);

        //当前图片窗口的大小
        width = GTK_WIDGET(priv->display)->allocation.width;
        height = GTK_WIDGET(priv->display)->allocation.height;

        //图像本身的像素大小
        himage = gdk_pixbuf_get_height(priv->pixbuf);
        wimage = gdk_pixbuf_get_width(priv->pixbuf);

        //如果缩放后小于显示窗口,
        if(scaled_width < width &&scaled_height < height)
        {
          //图片左上角的坐标
          yimage = (height - scaled_height) / 2;
          ximage = (width - scaled_width) / 2;

          //计算鼠标位置的行和列
          row = (y - yimage) / priv->zoom;
          col = (x - ximage) / priv->zoom;

          //如果鼠标不在图片范围内,则显示0行0列
          if(y < yimage || y > (yimage  scaled_height) ||
             x < ximage || x > (ximage  scaled_width))
          {
            row = 0;
            col = 0;
          }
        }

        //水平缩放不超过显示窗口,但垂直缩放超过了显示窗口
        else if(scaled_width < width)
        {
          ximage = (width - scaled_width) / 2;
          yimage = 0 - priv->yofs;
          row = (y - yimage) / priv->zoom;
          col = (x - ximage) / priv->zoom;

          if(x < ximage || x >= (ximage  scaled_width))
          {
            row = 0;
            col = 0;
          }
        }

        //水平缩放超过显示窗口,垂直缩放不超过显示窗口

        else if(scaled_height < height)
        {
          yimage = (height - scaled_height) / 2;
          ximage = 0 - priv->xofs;
          row = (y - yimage) / priv->zoom;
          col = (x - ximage) / priv->zoom;
          if(y < yimage || y >= (yimage  scaled_height))
          {
            row = 0;
            col = 0;
          }
        }

       //水平和垂直缩放都超过显示窗口
        else
        {
          row = (y   priv->yofs) / priv->zoom;
          col = (x   priv->xofs) / priv->zoom;
        }      

        //获取图片像素指针:pixbuf
        if (row >=0 && row < himage&&
            col >=0 && col < wimage&&
            gdk_pixbuf_get_colorspace(priv->pixbuf) == GDK_COLORSPACE_RGB &&
            !gdk_pixbuf_get_has_alpha(priv->pixbuf) &&
            gdk_pixbuf_get_bits_per_sample(priv->pixbuf) == 8)
        {
          guchar *pixels;
          int rowstride, n_channels;
          rowstride = gdk_pixbuf_get_rowstride(priv->pixbuf);
          n_channels = gdk_pixbuf_get_n_channels(priv->pixbuf);

          //获取第row行col列的像素值
          pixels = (gdk_pixbuf_get_pixels(priv->pixbuf)
                   row * rowstride
                   col * n_channels);

          //获取r,g,b值
          r = pixels0;
          g = pixels1;
          b = pixels2;
        }

        if(scaled_width < width &&scaled_height < height)
        {
          //clear if the pointer is out of range
          if(y < yimage || y > (yimage  scaled_height) ||
             x < ximage || x > (ximage  scaled_width))
          {
            r = 0;
            g = 0;
            b = 0;
          }
        }

        else if(scaled_width < width)
        {
          if(x < ximage || x >= (ximage  scaled_width))
          {
            r = 0;
            g = 0;
            b = 0;
          }
        }

        else if(scaled_height < height)
        {
          if(y < yimage || y >= (yimage  scaled_height))
          {
            r = 0;
            g = 0;
            b = 0;
          }
        }

      //保存到scroll_view的全局变量里
      view->priv= row;
      view->priv= col;
      view->r= r;
      view->g= g;
      view->b= b;

      //触发更新状态栏的消息SIGNAL_ZOOM_CHANGED
  g_signal_emit (view, view_signalsSIGNAL_ZOOM_CHANGED, 0, priv->zoom);
        return TRUE;
      }

      //dragimage
      drag_to(view, x, y);
      returnTRUE;
}

3.修改更新状态栏函数

代码语言:txt复制
static void
update_status_bar (EogWindow *window)
{
         EogWindowPrivate*priv;
         char*str = NULL;
         introw;
         intcol;
         intr;
         intg;
         intb;      

         g_return_if_fail(EOG_IS_WINDOW (window));
         eog_debug(DEBUG_WINDOW);
         priv= window->priv;

         if(priv->image != NULL &&
             eog_image_has_data (priv->image,EOG_IMAGE_DATA_ALL)) {
                   intzoom, width, height;
                   goffsetbytes = 0;
                   zoom= floor (100 * eog_scroll_view_get_zoom (EOG_SCROLL_VIEW (priv->view))  0.5);
                   eog_image_get_size(priv->image, &width, &height);
                   bytes= eog_image_get_bytes (priv->image);
                   if((width > 0) && (height > 0)) {
                            char*size_string;
                            size_string= g_format_size_for_display (bytes);

                            //从scroll_view中取出row,col,r,g,b
                            row=  (EOG_SCROLL_VIEW (priv->view))->row;
                            col= (EOG_SCROLL_VIEW (priv->view))->col;
                            r= (EOG_SCROLL_VIEW (priv->view))->r;
                            g= (EOG_SCROLL_VIEW (priv->view))->g;
                            b= (EOG_SCROLL_VIEW (priv->view))->b; 

                            //格式化准备显示在状态栏的字符串
                            str= g_strdup_printf (ngettext("%i x %i pixel %s    %i%%    %i,%iRGB(%i,%i,%i)(x,x,x)",
                                                                 "%ix %i pixels  %s    %i%%   %i,%i RGB(%i,%i,%i)(x,x,x)", height),
                                                      width, height, size_string,zoom,
                                                      (height - row - 1), col, r, g, b, r, g,b
                                                      );

                            g_free(size_string);
                   }

                   update_image_pos(window);
         }

  gtk_statusbar_pop(GTK_STATUSBAR (priv->statusbar),
                               priv->image_info_message_cid); 

         //把str字符串显示到状态栏
  gtk_statusbar_push(GTK_STATUSBAR (priv->statusbar),
                                priv->image_info_message_cid, str ? str: "");
         g_free(str);
}

附件两张行列计算的关系图:

  1. 图片缩小的情况:
  1. 图片放大的情况:

第四步:最终显示效果

如图,当鼠标移动时,状态栏的行、列、红、绿、蓝等信息都实时更新。

总结:

本文的目的并不是想教会大家如何编写Linux C的软件,只是给大家提出一种解决问题的方法。数字验证工程师往往需要多方面的技能,如软件编程、数据库、FPGA、甚至是板级的原理图、PCB……

0 人点赞