写在前面: 本人翻译并不专业,甚至英语不好,翻译内容仅供参考。由于博主是边学边翻译,所以不能保证翻译的准确性和正确性,如果可以,请查看原版学习,本文仅作学习记录之用。
《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[];
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
GimpRunMode run_mode;
GimpDrawable *drawable; /* Setting mandatory output values */
*nreturn_vals = ;
*return_vals = values; values[].type = GIMP_PDB_STATUS;
values[].data.d_status = status; /* Getting run_mode - we won't display a dialog if
* we are in NONINTERACTIVE mode */
run_mode = param[].data.d_int32; /* Get the specified drawable */
drawable = gimp_drawable_get (param[].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 void
blur (GimpDrawable *drawable)
{
gint i, j, k, channels;
gint x1, y1, x2, y2;
GimpPixelRgn rgn_in, rgn_out;
guchar output[]; /* 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[][]; /* Get nine pixels */
gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
MAX (i - , x1),
MAX (j - , y1));
gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
MAX (i - , x1),
j);
gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
MAX (i - , x1),
MIN (j + , y2 - )); gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
i,
MAX (j - , y1));
gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
i,
j);
gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
i,
MIN (j + , y2 - )); gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
MIN (i + , x2 - ),
MAX (j - , y1));
gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
MIN (i + , x2 - ),
j);
gimp_pixel_rgn_get_pixel (&rgn_in,
pixel[],
MIN (i + , x2 - ),
MIN (j + , y2 - )); /* For each layer, compute the average of the
* nine */
for (k = ; k < channels; k++)
{
int tmp, sum = ;
for (tmp = ; tmp < ; tmp++)
sum += pixel[tmp][k];
output[k] = sum / ;
} gimp_pixel_rgn_set_pixel (&rgn_out,
output,
i, j);
} if (i % == )
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 - ),
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 - , i + ),
x2 - x1); for (j = x1; j < x2; j++)
{
/* For each layer, compute the average of the nine
* pixels */
for (k = ; k < channels; k++)
{
int sum = ;
sum = row1[channels * MAX ((j - - x1), ) + k] +
row1[channels * (j - x1) + k] +
row1[channels * MIN ((j + - x1), x2 - x1 - ) + k] +
row2[channels * MAX ((j - - x1), ) + k] +
row2[channels * (j - x1) + k] +
row2[channels * MIN ((j + - x1), x2 - x1 - ) + k] +
row3[channels * MAX ((j - - x1), ) + k] +
row3[channels * (j - x1) + k] +
row3[channels * MIN ((j + - x1), x2 - x1 - ) + k];
outrow[channels * (j - x1) + k] = sum / ;
} } gimp_pixel_rgn_set_row (&rgn_out,
outrow,
x1, i,
x2 - x1); if (i % == )
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插件(一)
近期想尝试编写gimp插件,在gimp官网看到了三篇简明教程,顺便翻译了下,由于本人英文,计算机知识有限,文中难免有warning,error出现,欢迎指正. <How to write a G ...
-
Go - 如何编写 ProtoBuf 插件 (三) ?
目录 前言 演示代码 小结 推荐阅读 前言 上篇文章<Go - 如何编写 ProtoBuf 插件 (二) >,分享了基于 自定义选项 定义了 interceptor 插件,然后在 hell ...
-
(翻译)编写属于你的jQuery插件
Writing Your Own jQuery Plugins 原文地址:http://blog.teamtreehouse.com/writing-your-own-jquery-plugins j ...
-
Go - 如何编写 ProtoBuf 插件(二)?
目录 前言 定义插件 使用插件 获取自定义选项 小结 推荐阅读 前言 上篇文章<Go - 如何编写 ProtoBuf 插件 (一) >,分享了使用 proto3 的 自定义选项 可以实现插 ...
-
使用Qt编写模块化插件式应用程序
动态链接库技术使软件工程师们兽血沸腾,它使得应用系统(程序)可以以二进制模块的形式灵活地组建起来.比起源码级别的模块化,二进制级别的模块划分使得各模块更加独立,各模块可以分别编译和链接,模块的升级不会 ...
-
Gimp插件Hello world注释
前一阵翻译gimp官网的编写插件教程,本打算继续翻译第二部分,但是感觉第一个例子还不是很懂,翻译第二部分有点理解不能,所以就读了一下源码,记录如下 #include <libgimp/gimp. ...
-
编写jQuery插件--实现返回顶部插件
国庆过去一周多了,作为IT界的具有严重’工作狂‘性质的宅人,居然还没走出玩耍的心情,拖了程序猿的脚后跟了.最近工作不顺,心情不佳,想吐槽下公司,想了还是厚道点,以彼之道还施彼身,觉得自己也和他们同流合 ...
-
Lua编写wireshark插件初探——解析Websocket上的MQTT协议
一.背景 最近在做物联网流量分析时发现, App在使用MQTT协议时往往通过SSL+WebSocket+MQTT这种方式与服务器通信,在使用SSL中间人截获数据后,Wireshark不能自动解析出MQ ...
-
金蝶K3 wise 插件二次开发与配置
金蝶K3 wise 插件二次开发与配置 开发环境:K/3 Wise 13.0.K/3 Bos开发平台.Visual Basic 6.0 目录 一.二次开发插件编程二.代码演示三.配置插件四.测试插件五 ...
随机推荐
-
实验:传输层:TCP协议
一.概述 TCP和UDP处在同一层——运输层,但是它们有很多的不同.TCP是TCP/IP系列协议中最复杂的部分,它具有以下特点: (1) TCP提供 可靠的 数据传输服务,TCP是 面向连接的 .应用 ...
-
原来样式改变不了(input type=";file";)
label { background-color: #979fa8; color: #fff; display: inline-block; padding: .8rem 4rem; cursor: ...
-
LESS 拓展选择器extend基础用法说明
扩展选择器是less的伪类选择器,他会复制当前选择器,定义新的样式,而原来的不变 nav ul { &:extend(.inline); background: blue; } .inline ...
-
注意map<;>; 的[]
其实在之前一篇关于map的基本操作中已经提到过注意[]操作,这里再强调一下. 先看下面的程序: #include<iostream> #include<map> using n ...
-
用smarty模板做的登录
用smarty模板做的登录和之前我们用php做的登录区别不大 首先要新建一个php文件 一般php文件,要放在这个文件里 它对应的html文件,要放在这个目录里 下面先来做php文件 要先引入入口文件 ...
-
Struts2 2.5.12的问题
使用maven搭建的Struts项目,使用Struts 2.5.12会启动报错,应该不是包冲突的问题,感觉是lang3.jar的问题,换回2.5.10.1就没事了. 这点挺奇怪的,不应该是jar包损坏 ...
-
python笔记:#005#算数运算符
算数运算符 计算机,顾名思义就是负责进行 数学计算 并且 存储计算结果 的电子设备 目标 算术运算符的基本使用 01. 算数运算符 算数运算符是 运算符的一种 是完成基本的算术运算使用的符号,用来处理 ...
-
IE8环境下的上传图片预览
今天做一个需要在IE浏览器上使用的信息录入项目,遇到了图片上传预览的问题,找了一些资料,最后使用了IE自带的滤镜做到了 <!--HTML IE8不支持opacity,只能使用双层,一层背景半透明 ...
-
Java高级框架------Spring(二)
五.如何给Bean的属性赋值(注入) 1. 通过构造方法来赋值 2. 设置注入(通过set方法) 2.1 如果属性是基本类型或String等简单 <bean id="peo" ...
-
并发之volatile关键字
volatile关键字 volatile关键字是什么 在上一章我们讲到了并发的的三个概念,那么今天在讲解下在java中可以保证可见性和有序性的一个关键字. volatile关键字 :当变量的值被该关键 ...