zoukankan      html  css  js  c++  java
  • [翻译]如何编写GIMP插件(二)

    写在前面: 本人翻译并不专业,甚至英语不好,翻译内容仅供参考。由于博主是边学边翻译,所以不能保证翻译的准确性和正确性,如果可以,请查看原版学习,本文仅作学习记录之用。

    《How to write a GIMP plug-in, part II》

    原文:http://developer.gimp.org/writing-a-plug-in/2/index.html

    作者:Dave Neary

    在第一部分,我们展示了使用gimp去构建一个插件所需要的基本要素,现在为我们的插件提供一个简单且有用的算法。

    介绍

    我们将要实现一个简单模糊,它将默认安装在GIMP的“滤镜 – 模糊 – 模糊”中。 这个算法非常简单,把每个像素值替换为它周围像素的平均值即可,例如,我们举一个最简单的案例,一个3×3的矩阵,值为1到9,那么中心值则为5,9个元素就是它的邻里

    使用这个方法,边缘差异会导致出现一个模糊的结果,可以采用不同的半径,使用(2r+1)x(2r+1)矩阵。

    图像结构

    我们在上个例子中写过一个run()函数,但是没有什么功能,现在我们再看一下run()的函数原形

    static void run (const gchar     *name,
                     gint             nparams,
                     const GimpParam *param,
                     gint            *nreturn_vals,
                     GimpParam      **return_vals);

    前三个输入参数中的第一个是运行模式,还有一个是图像的标识符,另一个是活动并可拖拽的(图层或是遮盖) ,在GIMP中的图像是一个结构体,它包含指南,图层,遮盖,以及任何和图像相关的数据。“可拖拽”经常用在GIMP内部结构,它是一个可修改的对象,所以,图层,图层遮盖,选区全都是“可拖拽”的

     

    获取数据

    为了从标识符中获取GimpDrawable结构体,我们需要使用gimp_drawable_get()函数:

    GimpDrawable *gimp_drawable_get (gint32 drawable_id);

    使用这个结构体,我们可以通过GimpPixelRgn结构去访问可拖拽的数据,并且可以查看可拖拽类型(RGB,灰度)。完整的关于GimpDrawable函数列表可以在官方API中查询

    两个对插件来说非常重要的函数是gimp_drawable_mask_bounds() 和 gimp_pixel_rgn_init()。第一个是用来为可拖拽对象的活动选区做限制,第二个是用来初始化GimpPixelRgn

    当我们初始化完GimpPixelRgn后,我们可以通过不同的方式来访问数据,如以像素,以矩形,以圆,以柱方式访问,最好的方法取决于你打算使用的算法。此外,GIMP使用tile-based结构,载入/载出数据开销是很大的,所以如非必要,我们不要经常的使用它们。

    主函数获取和设置的函数如下:

    void gimp_pixel_rgn_get_pixel (GimpPixelRgn *pr,
                                   guchar       *buf,
                                   gint          x,
                                   gint          y);
    void gimp_pixel_rgn_get_row   (GimpPixelRgn *pr,
                                   guchar       *buf,
                                   gint          x,
                                   gint          y,
                                   gint          width);
    void gimp_pixel_rgn_get_col   (GimpPixelRgn *pr,
                                   guchar       *buf,
                                   gint          x,
                                   gint          y,
                                   gint          height);
    void gimp_pixel_rgn_get_rect  (GimpPixelRgn *pr,
                                   guchar       *buf,
                                   gint          x,
                                   gint          y,
                                   gint          width,
                                   gint          height);
    void gimp_pixel_rgn_set_pixel (GimpPixelRgn *pr,
                                   const guchar *buf,
                                   gint          x,
                                   gint          y);
    void gimp_pixel_rgn_set_row   (GimpPixelRgn *pr,
                                   const guchar *buf,
                                   gint          x,
                                   gint          y,
                                   gint          width);
    void gimp_pixel_rgn_set_col   (GimpPixelRgn *pr,
                                   const guchar *buf,
                                   gint          x,
                                   gint          y,
                                   gint          height);
    void gimp_pixel_rgn_set_rect  (GimpPixelRgn *pr,
                                   const guchar *buf,
                                   gint          x,
                                   gint          y,
                                   gint          width,
                                   gint          height);

    这里还有其它方法可以访问图像数据(甚至用的更多),它允许管理数据在tile级别,稍后我们将会详细的讨论它。

    更新数据

    插件更新图像数据后必须提交数据到内核,告诉它应该更新显示。这是通过如下两个函数实现的:

    gimp_displays_flush ();
    gimp_drawable_detach (drawable);

    实现blur()函数

    为了尝试不同的方法,我们使用blur函数,下面是run函数实现

    static void
    run (const gchar      *name,
         gint              nparams,
         const GimpParam  *param,
         gint             *nreturn_vals,
         GimpParam       **return_vals)
    {
      static GimpParam  values[1];
      GimpPDBStatusType status = GIMP_PDB_SUCCESS;
      GimpRunMode       run_mode;
      GimpDrawable     *drawable;
     
      /* Setting mandatory output values */
      *nreturn_vals = 1;
      *return_vals  = values;
     
      values[0].type = GIMP_PDB_STATUS;
      values[0].data.d_status = status;
     
      /* Getting run_mode - we won't display a dialog if
       * we are in NONINTERACTIVE mode */
      run_mode = param[0].data.d_int32;
     
      /*  Get the specified drawable  */
      drawable = gimp_drawable_get (param[2].data.d_drawable);
     
      gimp_progress_init ("My Blur...");
     
      /* Let's time blur
       *
       *   GTimer timer = g_timer_new time ();
       */
     
      blur (drawable);
     
      /*   g_print ("blur() took %g seconds.
    ", g_timer_elapsed (timer));
       *   g_timer_destroy (timer);
       */
     
      gimp_displays_flush ();
      gimp_drawable_detach (drawable);
    }

    这里我们需要解释一些行,gimp_progress_init()函数的作用是为我们的插件初始化进度条(参数就是进度条上面显示的字符串)。稍后会在blur中调用gimp_progress_update()函数,根据参数百分比打印进度条。运行模式(run_mode)决定插件是否以图形界面开始。它可以是GIMP_RUN_INTERACTIVE, GIMP_RUN_NONINTERACTIVE 或 GIMP_RUN_WITH_LAST_VALS中的一个值。它们分别是“交互式运行”,“脚本运行”,“重复上次的滤镜”。

    关于模糊算法本身,如下的版本使用的是gimp_pixel_rgn_(get|set)_pixel()函数,我们暂时还没有对它进行说明。

    gimp_drawable_mask_bounds()是用来计算滤镜影响的范围,不包括任何非活动区域,通过这种方式限制处理可以提高性能。

    gimp_pixel_rgn_init()根据指定参数初始化像素区域,两个bool变量存储着GimpPixelRgn的行为,如果dirty变量和shadow变量都为FALSE,那么这个GimpPixelRgn是用来读取的,如果都为TRUE,那么这个GimpPixelRgn是用来写的,其它的组合很少使用。

    static void
    blur (GimpDrawable *drawable)
    {
      gint         i, j, k, channels;
      gint         x1, y1, x2, y2;
      GimpPixelRgn rgn_in, rgn_out;
      guchar       output[4];
     
      /* Gets upper left and lower right coordinates,
       * and layers number in the image */
      gimp_drawable_mask_bounds (drawable->drawable_id,
                                 &x1, &y1,
                                 &x2, &y2);
      channels = gimp_drawable_bpp (drawable->drawable_id);
     
      /* Initialises two PixelRgns, one to read original data,
       * and the other to write output data. That second one will
       * be merged at the end by the call to
       * gimp_drawable_merge_shadow() */
      gimp_pixel_rgn_init (&rgn_in,
                           drawable,
                           x1, y1,
                           x2 - x1, y2 - y1,
                           FALSE, FALSE);
      gimp_pixel_rgn_init (&rgn_out,
                           drawable,
                           x1, y1,
                           x2 - x1, y2 - y1,
                           TRUE, TRUE);
     
      for (i = x1; i < x2; i++)
        {
          for (j = y1; j < y2; j++)
            {
              guchar pixel[9][4];
     
              /* Get nine pixels */
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[0],
                                        MAX (i - 1, x1),
                                        MAX (j - 1, y1));
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[1],
                                        MAX (i - 1, x1),
                                        j);
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[2],
                                        MAX (i - 1, x1),
                                        MIN (j + 1, y2 - 1));
     
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[3],
                                        i,
                                        MAX (j - 1, y1));
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[4],
                                        i,
                                        j);
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[5],
                                        i,
                                        MIN (j + 1, y2 - 1));
     
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[6],
                                        MIN (i + 1, x2 - 1),
                                        MAX (j - 1, y1));
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[7],
                                        MIN (i + 1, x2 - 1),
                                        j);
              gimp_pixel_rgn_get_pixel (&rgn_in,
                                        pixel[8],
                                        MIN (i + 1, x2 - 1),
                                        MIN (j + 1, y2 - 1));
     
              /* For each layer, compute the average of the
               * nine */
              for (k = 0; k < channels; k++)
                {
                  int tmp, sum = 0;
                  for (tmp = 0; tmp < 9; tmp++)
                    sum += pixel[tmp][k];
                  output[k] = sum / 9;
                }
     
              gimp_pixel_rgn_set_pixel (&rgn_out,
                                        output,
                                        i, j);
            }
     
          if (i % 10 == 0)
            gimp_progress_update ((gdouble) (i - x1) / (gdouble) (x2 - x1));
        }
     
      /*  Update the modified region */
      gimp_drawable_flush (drawable);
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
      gimp_drawable_update (drawable->drawable_id,
                            x1, y1,
                            x2 - x1, y2 - y1);
    }

    按行处理

    我们的函数有一个致命的缺点:性能,一个300×300的选区,在我的K6-2 350MHz处理需要12分钟,同样的选区,使用高斯模糊则仅仅需要3秒钟。

    如果我们使用gimp_pixel_rgn_(get|set)_row()函数处理,那么速度会快很多,使用blur() v2版本,处理300×300的选区所需时间从760秒降到6秒钟。

    static void
    blur (GimpDrawable *drawable)
    {
      gint         i, j, k, channels;
      gint         x1, y1, x2, y2;
      GimpPixelRgn rgn_in, rgn_out;
      guchar      *row1, *row2, *row3;
      guchar      *outrow;
     
      gimp_drawable_mask_bounds (drawable->drawable_id,
                                 &x1, &y1,
                                 &x2, &y2);
      channels = gimp_drawable_bpp (drawable->drawable_id);
     
      gimp_pixel_rgn_init (&rgn_in,
                           drawable,
                           x1, y1,
                           x2 - x1, y2 - y1,
                           FALSE, FALSE);
      gimp_pixel_rgn_init (&rgn_out,
                           drawable,
                           x1, y1,
                           x2 - x1, y2 - y1,
                           TRUE, TRUE);
     
      /* Initialise enough memory for row1, row2, row3, outrow */
      row1 = g_new (guchar, channels * (x2 - x1));
      row2 = g_new (guchar, channels * (x2 - x1));
      row3 = g_new (guchar, channels * (x2 - x1));
      outrow = g_new (guchar, channels * (x2 - x1));
     
      for (i = y1; i < y2; i++)
        {
          /* Get row i-1, i, i+1 */
          gimp_pixel_rgn_get_row (&rgn_in,
                                  row1,
                                  x1, MAX (y1, i - 1),
                                  x2 - x1);
          gimp_pixel_rgn_get_row (&rgn_in,
                                  row2,
                                  x1, i,
                                  x2 - x1);
          gimp_pixel_rgn_get_row (&rgn_in,
                                  row3,
                                  x1, MIN (y2 - 1, i + 1),
                                  x2 - x1);
     
          for (j = x1; j < x2; j++)
            {
              /* For each layer, compute the average of the nine
               * pixels */
              for (k = 0; k < channels; k++)
                {
                  int sum = 0;
                  sum = row1[channels * MAX ((j - 1 - x1), 0) + k]           +
                        row1[channels * (j - x1) + k]                        +
                        row1[channels * MIN ((j + 1 - x1), x2 - x1 - 1) + k] +
                        row2[channels * MAX ((j - 1 - x1), 0) + k]           +
                        row2[channels * (j - x1) + k]                        +
                        row2[channels * MIN ((j + 1 - x1), x2 - x1 - 1) + k] +
                        row3[channels * MAX ((j - 1 - x1), 0) + k]           +
                        row3[channels * (j - x1) + k]                        +
                        row3[channels * MIN ((j + 1 - x1), x2 - x1 - 1) + k];
                  outrow[channels * (j - x1) + k] = sum / 9;
                }
     
           }
     
           gimp_pixel_rgn_set_row (&rgn_out,
                                   outrow,
                                   x1, i,
                                   x2 - x1);
     
           if (i % 10 == 0)
                gimp_progress_update ((gdouble) (i - y1) / (gdouble) (y2 - y1));
      }
     
      g_free (row1);
      g_free (row2);
      g_free (row3);
      g_free (outrow);
     
      gimp_drawable_flush (drawable);
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
      gimp_drawable_update (drawable->drawable_id,
                            x1, y1,
                            x2 - x1, y2 - y1);
    }

    来看一下完整的blur1.cblur2.c

    第三部分

    我们将改造我们的插件,使其可以根据我们提供的参数来处理

  • 相关阅读:
    Java代码中,如何监控Mysql的binlog?
    springcloud dashboard控制面板上的各项指标详解(图文并茂)
    matlab练习程序(图优化)
    C# class模板文件路径
    PostgreSQL安全加固
    linux下安装7z命令及7z命令的使用
    python中for......else......的使用
    docker端口映射或启动容器时报错 driver failed programming external connectivity on endpoint quirky_allen
    python:关于py文件之间相互import的问题
    nginx 80端口跳转到443
  • 原文地址:https://www.cnblogs.com/ishell/p/4240173.html
Copyright © 2011-2022 走看看