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

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

在第一部分,我们展示了使用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 voidrun (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.\n", 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 voidblur (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 voidblur (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);

}

第三部分

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

gimp 架构_[翻译]如何编写GIMP插件(二)相关推荐

  1. gimp 架构_常见GUI 框架

    1:wxWidgets wxWidgets是一个开源的跨平台的C++构架库(framework),它可以提供 GUI(图形用户界面)和其它工具.目前的2.x版本支持所有版本的Windows.带GTK+ ...

  2. [译] 编写AndroidStudio插件(四):集成Jira

    原文:Write an Android Studio Plugin Part 4: Jira Integration 作者:Marcos Holgado 译者:却把清梅嗅 <编写AndroidS ...

  3. gazebo入门_【ROS-Gazebo】仿真插件编写教程(1)——概述

    前言 本系列教程的主要是对 Gazebo的官网教程 的翻译与理解.之前查找国内的中文资料,发现并没有关于如何编写Gazebo插件的教程.据我猜测,大概是因为这个技能属于"两不管"地 ...

  4. maven插件编写_编写Maven插件的提示

    maven插件编写 最近,我花了很多时间为Maven编写插件或在其中工作. 它们简单,有趣且有趣. 我以为我会分享一些技巧,使编写它们时的生活更轻松. 提示1:将任务与Mojo分开 最初,您将把moj ...

  5. eclipse 插件教程_编写Eclipse插件教程–第1部分

    eclipse 插件教程 Eclipse是三个最受欢迎的Java开发IDE之一. 其成功的原因之一是其可扩展性. 对于任何知道该怎么做并且已经做到的人来说,编写eclipse插件都可以非常轻松快捷. ...

  6. GIMP学习_菜单01:文件菜单

    操作系统:win10 GIMP版本:2.10.22 目录 一.新建文件 模板: 图像大小 高级选项 二.作为图层打开 三.保存/另存为/保存为副本 四.恢复 五.导出/导出为 文件菜单里各项和常见的软 ...

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

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

  8. skywalking原理_Skywalking系列博客6手把手教你编写 Skywalking 插件

    点击上方 IT牧场 ,选择 置顶或者星标技术干货每日送达! 前置知识 在正式进入编写环节之前,建议先花一点时间了解下javaagent(这是JDK 5引入的一个玩意儿,最好了解下其工作原理):另外,S ...

  9. 构建maven项目插件_如何构建一个Maven插件

    构建maven项目插件 使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护. 今天尝试Okta. 由于其插件生态系统的普 ...

  10. grunt 插件_从Grunt测试Grunt插件

    grunt 插件 编写针对grunt插件的测试结果比预期的要简单. 我需要运行多个任务配置,并想通过在主目录中键入grunt test来调用它们. 在第一个任务失败后,咕声通常会退出. 这使得不可能在 ...

最新文章

  1. thymeleaf模板的使用(转)
  2. php上传文件自动删除,jsp-解决文件上传后重启Tomcat时文件自动删除问题
  3. Python制作植物大战僵尸小游戏
  4. Latex快速入门系列 -- 在TexStudio中正确插入参考文献的基本操作
  5. java hashcode返回值_Java HashMap返回值未根据我对equals和hashcode的理解进行确认
  6. Python学习(14)--内置函数
  7. java swing双缓冲_java中的双缓冲技术
  8. WebSphere的管理员界面
  9. 砸金蛋html5小游戏设计总结
  10. 关于HTML预处理器Pug的使用文档
  11. 云服务器+Dock+搭建个人博客网站
  12. [转]一个IT人的辞职信:怀着梦想去远行!
  13. aspcms cookies欺骗和后台无验证注入
  14. linux中括号的用法,全面梳理linux下shell中各种括号的作用和用法
  15. JAVA学习笔记(第五章 接口与继承)
  16. oracle xla相关,【EBS】XLA_GLT表的清理
  17. keepalived实现mycat高可用问题排查;道路坎坷,布满荆棘,定让你大吃一惊!
  18. 美DARPA支持研发用于救灾与高风险环境的微型机器人
  19. 实现图片跟随鼠标移动
  20. raw图的存储格式和读取方式

热门文章

  1. VSCode更改默认浏览器
  2. 【附源码】计算机毕业设计java-志愿者管理系统设计与实现
  3. dell服务器设置bios设置u盘启动不了系统,详解戴尔通过BIOS设置U盘启动的技巧
  4. win7系统dhcp服务器设置方法,win7设定固定ip和同时支持dhcp的方法
  5. Edge浏览器无法打开网页
  6. CSS中media的最全用法格式总结!
  7. opencv Library QUIRC is not linked解决办法
  8. MATLAB课设代做在哪儿找,代写Grid World作业、代做CID留学生作业、代写Matlab课程设计、代做Matlab编程实验作业...
  9. Android 长按Menu键的监听
  10. 根据ip查询真实地址