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

《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()的函数原形

  1. static void run (const gchar *name,
  2. gint nparams,
  3. const GimpParam *param,
  4. gint *nreturn_vals,
  5. GimpParam **return_vals);

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

获取数据

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

  1. GimpDrawable *gimp_drawable_get (gint32 drawable_id);

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

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

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

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

  1. void gimp_pixel_rgn_get_pixel (GimpPixelRgn *pr,
  2. guchar *buf,
  3. gint x,
  4. gint y);
  5. void gimp_pixel_rgn_get_row (GimpPixelRgn *pr,
  6. guchar *buf,
  7. gint x,
  8. gint y,
  9. gint width);
  10. void gimp_pixel_rgn_get_col (GimpPixelRgn *pr,
  11. guchar *buf,
  12. gint x,
  13. gint y,
  14. gint height);
  15. void gimp_pixel_rgn_get_rect (GimpPixelRgn *pr,
  16. guchar *buf,
  17. gint x,
  18. gint y,
  19. gint width,
  20. gint height);
  21. void gimp_pixel_rgn_set_pixel (GimpPixelRgn *pr,
  22. const guchar *buf,
  23. gint x,
  24. gint y);
  25. void gimp_pixel_rgn_set_row (GimpPixelRgn *pr,
  26. const guchar *buf,
  27. gint x,
  28. gint y,
  29. gint width);
  30. void gimp_pixel_rgn_set_col (GimpPixelRgn *pr,
  31. const guchar *buf,
  32. gint x,
  33. gint y,
  34. gint height);
  35. void gimp_pixel_rgn_set_rect (GimpPixelRgn *pr,
  36. const guchar *buf,
  37. gint x,
  38. gint y,
  39. gint width,
  40. gint height);

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

更新数据

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

  1. gimp_displays_flush ();
  2. gimp_drawable_detach (drawable);

实现blur()函数

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

  1. static void
  2. run (const gchar *name,
  3. gint nparams,
  4. const GimpParam *param,
  5. gint *nreturn_vals,
  6. GimpParam **return_vals)
  7. {
  8. static GimpParam values[];
  9. GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  10. GimpRunMode run_mode;
  11. GimpDrawable *drawable;
  12.  
  13. /* Setting mandatory output values */
  14. *nreturn_vals = ;
  15. *return_vals = values;
  16.  
  17. values[].type = GIMP_PDB_STATUS;
  18. values[].data.d_status = status;
  19.  
  20. /* Getting run_mode - we won't display a dialog if
  21. * we are in NONINTERACTIVE mode */
  22. run_mode = param[].data.d_int32;
  23.  
  24. /* Get the specified drawable */
  25. drawable = gimp_drawable_get (param[].data.d_drawable);
  26.  
  27. gimp_progress_init ("My Blur...");
  28.  
  29. /* Let's time blur
  30. *
  31. * GTimer timer = g_timer_new time ();
  32. */
  33.  
  34. blur (drawable);
  35.  
  36. /* g_print ("blur() took %g seconds.\n", g_timer_elapsed (timer));
  37. * g_timer_destroy (timer);
  38. */
  39.  
  40. gimp_displays_flush ();
  41. gimp_drawable_detach (drawable);
  42. }

这里我们需要解释一些行,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是用来写的,其它的组合很少使用。

  1. static void
  2. blur (GimpDrawable *drawable)
  3. {
  4. gint i, j, k, channels;
  5. gint x1, y1, x2, y2;
  6. GimpPixelRgn rgn_in, rgn_out;
  7. guchar output[];
  8.  
  9. /* Gets upper left and lower right coordinates,
  10. * and layers number in the image */
  11. gimp_drawable_mask_bounds (drawable->drawable_id,
  12. &x1, &y1,
  13. &x2, &y2);
  14. channels = gimp_drawable_bpp (drawable->drawable_id);
  15.  
  16. /* Initialises two PixelRgns, one to read original data,
  17. * and the other to write output data. That second one will
  18. * be merged at the end by the call to
  19. * gimp_drawable_merge_shadow() */
  20. gimp_pixel_rgn_init (&rgn_in,
  21. drawable,
  22. x1, y1,
  23. x2 - x1, y2 - y1,
  24. FALSE, FALSE);
  25. gimp_pixel_rgn_init (&rgn_out,
  26. drawable,
  27. x1, y1,
  28. x2 - x1, y2 - y1,
  29. TRUE, TRUE);
  30.  
  31. for (i = x1; i < x2; i++)
  32. {
  33. for (j = y1; j < y2; j++)
  34. {
  35. guchar pixel[][];
  36.  
  37. /* Get nine pixels */
  38. gimp_pixel_rgn_get_pixel (&rgn_in,
  39. pixel[],
  40. MAX (i - , x1),
  41. MAX (j - , y1));
  42. gimp_pixel_rgn_get_pixel (&rgn_in,
  43. pixel[],
  44. MAX (i - , x1),
  45. j);
  46. gimp_pixel_rgn_get_pixel (&rgn_in,
  47. pixel[],
  48. MAX (i - , x1),
  49. MIN (j + , y2 - ));
  50.  
  51. gimp_pixel_rgn_get_pixel (&rgn_in,
  52. pixel[],
  53. i,
  54. MAX (j - , y1));
  55. gimp_pixel_rgn_get_pixel (&rgn_in,
  56. pixel[],
  57. i,
  58. j);
  59. gimp_pixel_rgn_get_pixel (&rgn_in,
  60. pixel[],
  61. i,
  62. MIN (j + , y2 - ));
  63.  
  64. gimp_pixel_rgn_get_pixel (&rgn_in,
  65. pixel[],
  66. MIN (i + , x2 - ),
  67. MAX (j - , y1));
  68. gimp_pixel_rgn_get_pixel (&rgn_in,
  69. pixel[],
  70. MIN (i + , x2 - ),
  71. j);
  72. gimp_pixel_rgn_get_pixel (&rgn_in,
  73. pixel[],
  74. MIN (i + , x2 - ),
  75. MIN (j + , y2 - ));
  76.  
  77. /* For each layer, compute the average of the
  78. * nine */
  79. for (k = ; k < channels; k++)
  80. {
  81. int tmp, sum = ;
  82. for (tmp = ; tmp < ; tmp++)
  83. sum += pixel[tmp][k];
  84. output[k] = sum / ;
  85. }
  86.  
  87. gimp_pixel_rgn_set_pixel (&rgn_out,
  88. output,
  89. i, j);
  90. }
  91.  
  92. if (i % == )
  93. gimp_progress_update ((gdouble) (i - x1) / (gdouble) (x2 - x1));
  94. }
  95.  
  96. /* Update the modified region */
  97. gimp_drawable_flush (drawable);
  98. gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
  99. gimp_drawable_update (drawable->drawable_id,
  100. x1, y1,
  101. x2 - x1, y2 - y1);
  102. }

按行处理

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

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

  1. static void
  2. blur (GimpDrawable *drawable)
  3. {
  4. gint i, j, k, channels;
  5. gint x1, y1, x2, y2;
  6. GimpPixelRgn rgn_in, rgn_out;
  7. guchar *row1, *row2, *row3;
  8. guchar *outrow;
  9.  
  10. gimp_drawable_mask_bounds (drawable->drawable_id,
  11. &x1, &y1,
  12. &x2, &y2);
  13. channels = gimp_drawable_bpp (drawable->drawable_id);
  14.  
  15. gimp_pixel_rgn_init (&rgn_in,
  16. drawable,
  17. x1, y1,
  18. x2 - x1, y2 - y1,
  19. FALSE, FALSE);
  20. gimp_pixel_rgn_init (&rgn_out,
  21. drawable,
  22. x1, y1,
  23. x2 - x1, y2 - y1,
  24. TRUE, TRUE);
  25.  
  26. /* Initialise enough memory for row1, row2, row3, outrow */
  27. row1 = g_new (guchar, channels * (x2 - x1));
  28. row2 = g_new (guchar, channels * (x2 - x1));
  29. row3 = g_new (guchar, channels * (x2 - x1));
  30. outrow = g_new (guchar, channels * (x2 - x1));
  31.  
  32. for (i = y1; i < y2; i++)
  33. {
  34. /* Get row i-1, i, i+1 */
  35. gimp_pixel_rgn_get_row (&rgn_in,
  36. row1,
  37. x1, MAX (y1, i - ),
  38. x2 - x1);
  39. gimp_pixel_rgn_get_row (&rgn_in,
  40. row2,
  41. x1, i,
  42. x2 - x1);
  43. gimp_pixel_rgn_get_row (&rgn_in,
  44. row3,
  45. x1, MIN (y2 - , i + ),
  46. x2 - x1);
  47.  
  48. for (j = x1; j < x2; j++)
  49. {
  50. /* For each layer, compute the average of the nine
  51. * pixels */
  52. for (k = ; k < channels; k++)
  53. {
  54. int sum = ;
  55. sum = row1[channels * MAX ((j - - x1), ) + k] +
  56. row1[channels * (j - x1) + k] +
  57. row1[channels * MIN ((j + - x1), x2 - x1 - ) + k] +
  58. row2[channels * MAX ((j - - x1), ) + k] +
  59. row2[channels * (j - x1) + k] +
  60. row2[channels * MIN ((j + - x1), x2 - x1 - ) + k] +
  61. row3[channels * MAX ((j - - x1), ) + k] +
  62. row3[channels * (j - x1) + k] +
  63. row3[channels * MIN ((j + - x1), x2 - x1 - ) + k];
  64. outrow[channels * (j - x1) + k] = sum / ;
  65. }
  66.  
  67. }
  68.  
  69. gimp_pixel_rgn_set_row (&rgn_out,
  70. outrow,
  71. x1, i,
  72. x2 - x1);
  73.  
  74. if (i % == )
  75. gimp_progress_update ((gdouble) (i - y1) / (gdouble) (y2 - y1));
  76. }
  77.  
  78. g_free (row1);
  79. g_free (row2);
  80. g_free (row3);
  81. g_free (outrow);
  82.  
  83. gimp_drawable_flush (drawable);
  84. gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
  85. gimp_drawable_update (drawable->drawable_id,
  86. x1, y1,
  87. x2 - x1, y2 - y1);
  88. }

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

第三部分

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

[翻译]如何编写GIMP插件(二)的更多相关文章

  1. [翻译]如何编写GIMP插件(一)

    近期想尝试编写gimp插件,在gimp官网看到了三篇简明教程,顺便翻译了下,由于本人英文,计算机知识有限,文中难免有warning,error出现,欢迎指正. <How to write a G ...

  2. Go - 如何编写 ProtoBuf 插件 (三) ?

    目录 前言 演示代码 小结 推荐阅读 前言 上篇文章<Go - 如何编写 ProtoBuf 插件 (二) >,分享了基于 自定义选项 定义了 interceptor 插件,然后在 hell ...

  3. (翻译)编写属于你的jQuery插件

    Writing Your Own jQuery Plugins 原文地址:http://blog.teamtreehouse.com/writing-your-own-jquery-plugins j ...

  4. Go - 如何编写 ProtoBuf 插件(二)?

    目录 前言 定义插件 使用插件 获取自定义选项 小结 推荐阅读 前言 上篇文章<Go - 如何编写 ProtoBuf 插件 (一) >,分享了使用 proto3 的 自定义选项 可以实现插 ...

  5. 使用Qt编写模块化插件式应用程序

    动态链接库技术使软件工程师们兽血沸腾,它使得应用系统(程序)可以以二进制模块的形式灵活地组建起来.比起源码级别的模块化,二进制级别的模块划分使得各模块更加独立,各模块可以分别编译和链接,模块的升级不会 ...

  6. Gimp插件Hello world注释

    前一阵翻译gimp官网的编写插件教程,本打算继续翻译第二部分,但是感觉第一个例子还不是很懂,翻译第二部分有点理解不能,所以就读了一下源码,记录如下 #include <libgimp/gimp. ...

  7. 编写jQuery插件--实现返回顶部插件

    国庆过去一周多了,作为IT界的具有严重’工作狂‘性质的宅人,居然还没走出玩耍的心情,拖了程序猿的脚后跟了.最近工作不顺,心情不佳,想吐槽下公司,想了还是厚道点,以彼之道还施彼身,觉得自己也和他们同流合 ...

  8. Lua编写wireshark插件初探——解析Websocket上的MQTT协议

    一.背景 最近在做物联网流量分析时发现, App在使用MQTT协议时往往通过SSL+WebSocket+MQTT这种方式与服务器通信,在使用SSL中间人截获数据后,Wireshark不能自动解析出MQ ...

  9. 金蝶K3 wise 插件二次开发与配置

    金蝶K3 wise 插件二次开发与配置 开发环境:K/3 Wise 13.0.K/3 Bos开发平台.Visual Basic 6.0 目录 一.二次开发插件编程二.代码演示三.配置插件四.测试插件五 ...

随机推荐

  1. NET 中的多线程

    NET 中的多线程 为什么使用多线程 使用户界面能够随时相应用户输入 当某个应用程序在进行大量运算时候,为了保证应用程序能够随时相应客户的输入,这个时候我们往往需要让大量运算和相应用户输入这两个行为在 ...

  2. 一张漫画说尽IT开发过程

  3. 策略模式设计模式(Strategy)摘录

    23种子GOF设计模式一般分为三类:创建模式.结构模型.行为模式. 创建模式抽象的实例.一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将 ...

  4. Android SDCard Mount 流程分析

    前段时间对Android 的SDCard unmount 流程进行了几篇简短的分析,由于当时只是纸上谈兵,没有实际上的跟进,可能会有一些误导人或者小错误.今天重新梳理了头绪,针对mount的流程再重新 ...

  5. oracle HA 高可用性具体解释(之中的一个)

    oracle HA 高可用性具体解释(之二,深入解析TAF,以及HA框架) :http://blog.csdn.net/panfelix/article/details/38436197 一.HA F ...

  6. JQuery插件开发初探——结构熟悉

    工作之余,对Jquery插件做了一点尝试,想着之前总用别人写的插件,自己要是也写一个用岂不是很cool.于是说干就干,动手开始写. 首先是模仿,从一个简单的功能进行入手,了解一下插件开发的流程和结构. ...

  7. Android——保存并读取文件

    Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,仅仅能被应用本身訪问,在该模式下,写入的内容会覆盖原文件的内容,假设想把新写入的内容追加到原文件里.能够使用Contex ...

  8. 纯js客服插件集qq、旺旺、skype、百度hi、msn

    原文 纯js客服插件集qq.旺旺.skype.百度hi.msn 客服插件,集qq.旺旺.skype.百度hi.msn 等 即时通讯工具,并可自己添加支持的通讯工具,极简主义,用法自己琢磨.我的博客 h ...

  9. ORACLE 动态注册和静态注册的区别(转)

    1, oracle 10g 用netca方式建立的都默认为动态注册方式2,如果想改为静态注册的方式则在listener.ora 中加入如下内容即可 SID_LIST_LISTENER = (SID_L ...

  10. UVA 10537 - The Toll! Revisited(dijstra扩张)

    UVA 10537 - The Toll! Revisited option=com_onlinejudge&Itemid=8&page=show_problem&catego ...