内容


添加您自己的 GIMP 特性

深入 GNU Image Manipulation Program 的代码基

Comments

为何使用 GIMP?

使用免费或开源软件的一个最重要的原因是它为选择该产品的用户提供了一个机会,允许他们添加想要或需要的任何特性。但是,添加特性的能力受到项目大小和复杂性以及可用文档的性质的限制。因此,尽管 GIMP 是最著名和最成功的开源软件项目之一,但它巨大的代码基可能会令人生畏。

要理解本文的内容,您应该熟悉 C 编程语言。您还应该了解或准备好了解 Git 版本控制工具。本文中的示例使用 GIMP V2.7(注意,V2.7 是一个开发分支并不是一个稳定版本)在一个 Linux® 环境中创建。本文使用 2009 年春季发布的 Mandriva Linux 版本,但任一 Linux 版本应该都适用(参见 参考资料)。

您将基于现有绘图工具创建一个工具。本文中创建的工具仅用于指导目的。要使该工具成为一个实际项目并作为 GIMP 发行版一部分,它必须接受 GIMP 核心开发人员和 UI 架构师的全面审查。

开始

要使用 GIMP,首先必须构建 GIMP。尽管仅仅通过解压缩源 tarball 来检查代码比较繁琐,甚至使用针对代码知识库的 Git Web 界面这种简单一些的方法也是如此,但实际构建代码可能会更麻烦。

考虑到源代码的大小,GIMP 受到良好的维护和一致的构建。但是,您仍需处理所需的依赖项问题。当您运行 GIMP 这样的软件时,许多库将自动安装到您的系统上。当您需要构建软件时,除了这些库二进制文件之外,还需要更多信息。大多数 Linux 发行版将这些依赖项同主安装包分开包装。除了这些库之外,您还需要编译器本身和相关的工具集。在最流行的 Linux 发行版中,您只需键入几个命令就可以使一切就绪。

对于 GIMP 源代码本身,您应该从 Git 获取一个开发版本。最好获取一个 Git 树,即使您将在稳定版本的代码上工作。这使您可以轻松地跟踪您对代码的更改。Git 是一个分散的版本控制软件,可用于将 GIMP 代码保持在主知识库上。尽管存在图形界面,它被设计用于命令行使用。

获取 GIMP 源代码

要获取最新的开发版本,只需从命令行键入 git clone git://git.gnome.org/gimp。几分钟后,您将拥有完整的 GMIP 代码。这些代码组织良好,使您可以在超过 3,000 个下载文件中相对轻松地找到方向。

一个 Git 使用说明

在进行更改时,您不必担心会丢失任何原始文件,甚至您的更改,假如您决定保留它们以便进行定制,这要归功于 Git 系统。例如,要以它在 GIMP 知识库中的方式重置文件,只需键入 git checkout <filename>

无论如何,您必须熟悉 Git,即使您不打算回馈您的代码。它能极大地简化您的工作,即使您使用它来跟踪您对代码基的更改。例如,在任何给定时刻,您都能通过键入 git diff 来查看您对代码基的所有更改。

正确使用 Git 允许您创建本地分支,您可以在本地分支中开发不同的特性,并随时使用 GIMP 中的修改来对其进行更新。

构建 GIMP

要获取 GIMP 开发分支 — Git 主分支 — 的一个构建,您需要安装清单 1 中显示的库及其头部的最新版本。这些头部在 Linux 包中通常命令为 *-dev*-devel

在开始之前,您应该查看一下与 GIMP 源文件一起提供的 HACING 文件,它指定项目的开发规则。

清单 1. 构建当前 Git 主分支需要的库
glib 2.24.0
gtk 2.20.0
babl, 0.1.2
gegl, 0.1.2
libpng 1.2.34
cairo 1.6.0
pango 1.20.1
fontconfig 2.2.0
gtkdoc 1.0

除了上述必要库之外,您还将需要以下额外库(见清单 2)。如果它们不存在,那么某些功能将缺失,您必须传递一些选项,以便构建系统忽略它们。例如,如果您的开发包没有安装 libpng,那么您必须使用 autogen.sh --prefix=<prefix> --without-libpng 来调用 autogen.sh。

清单 2. 其他重要库
pygtk 2.10.4
poppler0.4.1
libcurl 7.15.1
dbus_glib 0.70
libhal 0.5.7
exif  0.6.15
lcms 1.16
libpng 1.2.37

确保您的系统上的库为最新版

为使您系统上的库更新到最新版,您的 Linux 发行版的包管理系统能够自动获取需要的库及其 devel 包。

在 Ubuntu 和 Debian 上,只需键入 sudo apt-get build-dep gimp

在 Mandriva Linux 上,可以使用 URPMI 的 --buildrequires 选项:urpmi --buildrequires gimp。但您必须先配置一个源 rpm 知识库。

对于 OpenSuSE,您可能需要参阅关于 zypper 命令的文档。

在 Fedora、Redhat、CentOS、SuSe 以及其他大多数发行版上,您必须手动安装 -devel 包。不用担心忘记其中一个,构建系统将停下来并询问您。这种情况会显示一条误导消息,指出您缺少库 x。但是,快速检查会发现,库 x 已经安装在您的系统上了。pkg-config 命令是跨发行版检查库版本的方法。例如,pkg-config --modversion gtk+-2.0 将显示安装的版本是 GTK+ V2.x。

如果构建系统报告一个旧版本,您的新 GIMP 前缀上将需要一个新版本。如果它报告库丢失,则很可能丢失的是 C 头文件,软件需要使用这些头文件来编译那些库。这些头文件通常位于单独的包中 — 以 -dev-devel 结束的包(具体取决于您的发行版)。也就是说,即使您的系统上安装了 GTK V2.20,也需要安装 gtk2-devel 包。

您的发行版知识库上过期或缺失的库

要在您的 Linux 发行版上构建 GIMP,并非上述所有库都需要最新版。如果您试图构建 GIMP 主分支 — 开发版本,则这尤其正确。但当您试图构建一个稳定的 GIMP 时,这种情况也可能会发生在较旧的系统上。要构建开发 GIMP,通常必须至少从 Git 构建 GEneric Graphics Library (GEGL) 库。

过期或缺失的库应该从每个库的最新稳定 tarball 获取。通常,搜索 Web 时在库 x 上的第一个命中将把您带到每个库的下载页面。您也许更喜欢获取 Git 版本而不是 tarball,这是一个可以接受的实践方法,但最好保守一点,获取每个库的 Git 稳定版本,不使用不稳定的版本。

一个例外是 GEGL 库,它要求您获取 Git 主(不稳定)分支而不是最新的稳定分支来构建 GMIP 主分支。GEGL 正处于替代 GIMP 本身的核心构成和图像操作的过程中。使用 git clone git://git.gnome.org/gegl 检索它的代码。

对于每个必须以这种方式安装的库(和 GIMP 本身),您必须注意不要覆盖安装在您的发行版中的库。要防止这种情况,您应该在另一个目录前缀中构建它们。我通常使用 /opt 前缀。除了将 --prefix 选项传递到配置脚本(autogen.sh)外,完成这个任务还涉及创建以下变量,如清单 3 所示。

清单 3. 构建变量
PREFIX=/opt
export PATH=$PREFIX/bin:$PATH
export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH
export ACLOCAL_FLAGS="-I $PREFIX/share/aclocal"

在以这种方式安装第一个库之前,确保 /opt/share/aclocal 目录已存在;否则就创建该目录。

大部分需要的组件使用 make 系统,可以通过切换到未打包的源代码所在的目录来在您的系统上编译和安装,如清单 4 所示。

清单 4. 使用 make 编译和安装组件
./configure --prefix=<xxx>
make
sudo make install

对于使用 Git 获取的库,使用 ./autogen.sh --prefix=<xxx> 替换清单 4 中的第一条命令 /configure。另外,如果您正在构建 GTK+,应在 autogenconfigure 命令行上包含选项 --with-xinput=yes。这将使卡片机(tablet)和其他对压力敏感的设备使用 GIMP。

编译 GIMP

上述先决条件就绪后,编译 GIMP 就比较简单。设置清单 4 中的环境变量,以便 GIMP 能够获取您选择的不同前缀中的新库,并在该前缀中构建 GIMP。这将避免与系统安装的稳定 GIMP 本身冲突(参见清单 5)。

清单 5. 设置环境变量
PREFIX=/opt
export PATH=$PREFIX/bin:$PATH
export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH
export ACLOCAL_FLAGS="-I $PREFIX/share/aclocal"

使用这些设置创建一个脚本并使用源 shell 命令运行它。每次构建 GIMP 都需要设置这些变量。然后切换到 GIMP 源基目录并输入清单 6 中的代码。

清单 6. 使用 make 安装 GIMP 源代码
./autogen.sh --prefix=<xxx>
make
sudo make install

如果出现任何关于缺失库的消息,按照 “您的发行版知识库上过期或缺失的库” 小节介绍的方法处理。

浏览源代码

现在您已经成功构建了 GIMP,现在我们看看 GIMP 源代码中有什么新内容。

您应该检查的程序特性包括标记资源(比如画笔、调色板、渐变色、图层组、画布上文本编辑)的能力和新的绘图动力系统。上述每项内容轻而易举地占用 GIMP 书的一章。由于它们正在积极的开发,记住,如果您选择修改这些部分,最好与其他开发人员密切合作,否则您的更改将来可能毫无用处。

源代码树

GIMP 源代码从 29 个目录开始,其中 7 个目录(po.* 目录)只用于其它组件的翻译文件 。9 个目录用于模块化的库,它们包含一些特定函数和常量,可从 GIMP 本身或从以 C 语言编写的插件中使用。其他值得注意的目录包括插件目录,其中保存 GIMP 带有的插件(包括用于编写插件和脚本的模式 script-fu 和 Python 扩展)的所有插件。

最后,所有应用程序代码本身包含在 app 目录中。这里有几个 C 文件,以及 20 个目录,应用程序本身就分布在这些目录中。有些目录名描述其中的代码的功能,比如 dialogs 和 xcf;另一些目录名则更普通,比如 core、base、actions。

在源代码中找到方向

作为一个大型项目,GIMP 中的每个特性都很好地分隔开,因此处理相同特性组的代码被编组在一起。大多数 C 文件存储在 /app 目录中,且在项目被构建时在单个 GIMP 可执行二进制文件中链接在一起。这里有超过 900 个 C 文件,头文件(.h)的数量也差不多,因此,当您查找一个特性的编码位置时,必须准备好进行一些搜索。

要找到您在程序中看到的某个特性的代码的驻留位置,最好的方法之一就是使用 grep 命令。grep 是一个标准的 UNIX® 命令行实用程序,用于在一个或几个文件中搜索一个文本模式。要寻找一个特性的代码,一个明显的方法就是搜索该特性在 UI 本身上显示的任何文本。当然,为此您必须运行英语版 GIMP,否则屏幕上显示的所有文本则位于 po/ 目录(位于 app 树外面)中的翻译文件中。

假设您想找到负责将一个 GIMP 窗口切换为全屏模式的代码。首先您验证该代码的工具提示是 Toggle fullscreen view(位于 GIMP 的 View 菜单上)。切换到 app 目录,键入 grep -i "toggle fullscreen view" ‛find -name "*c" ‛

-i 开关告知 grep 区分大小写,子命令 ‛find -name "*c" ‘(注意它包含在两个向后单引号中,这很重要,因为该命令不使用其他类型的引号)返回您正在搜索的目录树中的所有 C 文件的列表。因此,grep 检查所有 C 文件,查找指定的文本模式。该命令的答复如下:

./actions/view-actions.c:    NC_("view-action", "Toggle fullscreen view"),

要查看文件内部,建议使用 less 命令。如果您想查看这个文件的详细内容,请使用您喜欢的文本编辑器(参见清单 7)。

清单 7. 查看 view-actions.c 文件的内容
  { "view-fullscreen", GTK_STOCK_FULLSCREEN,
    NC_("view-action", "Fullscr_een"), "F11",
    NC_("view-action", "Toggle fullscreen view"),
    G_CALLBACK (view_fullscreen_cmd_callback),
    FALSE,
    GIMP_HELP_VIEW_FULLSCREEN },

这样,即使不能理解这个文件的全部功能,您也可以预期下一步。action 条目的惟一功能就是将其关联到一个名为 view_fullscreen_cmd_callback 的函数,仅此而已。因此,您的下一步就是再次运行 grep。就您目前所知,这个函数可以位于树中任何位置:

grep -i  "view_fullscreen_cmd_callback" ‛find -name "*c" ‛

这条命令返回两个匹配结果:其中一个是您刚才在 actions.c 文件中看到的那个条目;另一个位于文件 ./actions/view-commands.c 中。检查该文件的内容后,您终于看到一些代码。其中有一个 C 函数,检查是否存在一个窗口,检索活动图像,然后调用一个名为 gimp_image_window_set_fullscreen 的函数。您猜对了,它就是您的下一个 grep 目标:

grep -i  gimp_image_window_set_fullscreen   ‛find -name "*c" ‛

这将我们直接带到文件 app/display/gimpimagewindow.c,如清单 8 所示。当您检查这个文件的内容时,您将看到这个函数的功能。结果相当简单:只是一个对 GTK+ 函数的调用,以便最大化窗口。

清单 8. app/display/gimpimagewindow.c
gimp_image_window_set_fullscreen (GimpImageWindow *window,
                                  gboolean         fullscreen)
{
  g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window));

  if (fullscreen != gimp_image_window_get_fullscreen (window))
    {
      if (fullscreen)
        gtk_window_fullscreen (GTK_WINDOW (window));
      else
        gtk_window_unfullscreen (GTK_WINDOW (window));
    }
}

即使这个最大化程序函数本身相当简单(通过查看这个文件的内容可以看出),但您还是可以由此很好地感受到 GIMP 的内部工作机制。负责显示这个天窗并保持其属性的代码出现在这个文件中。如果您使用的是 GIMP V2.7 树,您将看到许多新编写的代码,用于在 GIMP 处于新的单一窗口模式时管理可停靠的对话框。

现在,您应该能够很好地理解 actions 目录中的操作和命令文件的功能了。您不必完全理解它们就能意识到,它们针对来自用户的操作创建可调用的命令。这些操作本身进一步简化该过程,汇集工具提示、显示文本、翻译提示等元信息。一旦一个操作被命名,它就可以直接从一个 XML 文件使用,(比如)添加到一个应用程序菜单。从 app/actions 目录中的动作创建菜单的那些 XML 文件全部位于项目根目录中的 menu 目录中。重新排列 GIMP 的菜单很简单,只需编辑一个 XML 文件。

修改 GIMP 源代码

现在您已对如何在源代码树中找到特性有所了解,可以进行一些更改了。在这个阶段,您应该尝试一些微小更改,其目的只是为了看看更改是否有效。我们首先通过注释掉上述函数调用并重新构建 GIMP 来禁用这个全屏功能。

要查看这种效果,只需打开上述 app/display/gimpimagewindow.c 文件进行编辑,在 gimp_image_window_set_fullscreen 函数的第一行中添加一个 return; 语句。键入 make,重新构建您的 GIMP 应用程序。没有必要重新运行 autogen.sh。您还可以运行 app 目录中生成的 gimp-2.7 二进制文件,而不是重新安装它。这个版本的 GIMP 不具有全屏功能。

面向对象的 C 语言编程

通常,C 不被视作一个面向对象的(OO)语言,但 OO 通常是一种应用程序构建方式,而不是一种语言的固有特征。正如可以用强制使用一个 “类/实例” 语法的语言来编写非 OO 应用程序代码一样,不使用高级语言提供的精妙语法也可以编写 OO 代码。

GIMP 就属于这种情况:它基于 GObject 框架,该框架允许 C 代码以一种 OO 方式编写并在运行时表现出 OO 风格。 不足之处在于这需要一些样板文件代码来创建一些新类并需要四处操作对象。另一方面,这种方法的确有效。

用 C 语言编写 OO 库的好处之一是您可以使用其他语言(比如 Python、Ruby 和 Java 编程语言等)的语言绑定。严格绑定到 GIMP 的库(比如 GTK+、GEGL、等等)也使用 GObject 系统。

创建一个新工具

现在我们将对这个程序进行一个真实的更改。在本文的示例中,您将创建一个可以从工具箱中选择的绘图工具。请记住,在创建一个新工具之前,必须检查这个程序中已经存在的功能。

在绘图动力系统、动画画笔、绘图模式和现有工具的所有可能组合中,许多组合都可以被实现。目前不可能实现的就是使一个单一绘画笔触的颜色呈放射状变化,以便画笔的中心呈现一种颜色,而画笔的外围呈现另一种颜色。笔触的颜色选择将遵循活动渐变色,因为它适用 “color from gradient” 绘图选项。

发现需要更改的特性

要使这个应用程序获取具有上述功能的一个额外绘图工具,第一步是确认需要复制、修改和添加到构建系统的文件。需要检查的第一个位置是 app/tools 目录中的 gimppaintbrushtool.h 和 gimppaintbrushtool.c 文件。

令人沮丧的是,这些文件只是一些小文件,主要包含一些样板文件代码,用于将这个工具创建为一个 GObject 类。返回命令行,在 app 目录中,您可以针对 paintbrush 执行一个 grep,检查是否有其他位置可能包含相关代码。

这次,返回的是一个更大一些的列表,其中包含几个文件和每个文件中的一些点。好消息是您得到了一些结果。不太好的消息是,由于您正试图克隆画笔工具,您必须访问这些文件中的大部分及其对应的头文件(.h),要么复制它们,要么将一些引用添加到您的新工具,就像当前画笔工具也有一些引用那样。

清单 9 显示了执行 grep 后列出的文件。

清单 9. 搜索画笔的 grep 的结果
./core/gimpbrush.c
./core/gimpcontext.c
./paint/gimp-paint.c
./paint/gimpairbrush.c
./paint/gimppaintbrush.c
./pdb/paint-tools-cmds.c
./tools/gimp-tools.c
./tools/gimppaintbrushtool.c
./widgets/gimpcursor.c

还有一个名为 gimpaintbrush.c 的文件,它没有 tool 后缀。打开该文件进行编辑。这个文件看起来似乎正是我们要寻找的。它的最后一个函数 _gimp_paintbrush_motion 实际执行一系列函数调用,检索将要绘图的缓冲区、要使用的当前画笔、绘图选项和绘图动力系统将应用的颜色,然后填充将要绘图的缓冲区的像素。它通过调用 gimp_brush_core_paste_canvas 来提交这个笔触。

进行一些试验

好的一方面是,您只需对 C 语言有基本了解就能理解正在进行的操作。大函数名和对象系统现在显示出它们的价值。代码比较容易理解。您也可以检查部分函数调用的功能,方法是使用 grep 来找到那些函数并查看它们的代码。

现在您可以随意进行一些试验,即使您在绘图时强制实施了一种特殊的绘图模式或不透明度,看看有什么变化。例如,您可以在任何函数调用 opacity 变量之前,将其内容替换为一个固定数字。

进行相关更改

要继续前进,一旦您确定在这个画笔文件中的适当更改能够获得理想的效果,就应该复制该文件并重新命名。一个好主意是使用为这个新工具选择的名称。对于这个示例,您将使用名称 gradientbrush。

在同一个目录中复制 app/paint/paintbrush.c 和 app/paint/paintbrush.h files,分别重命名为 gradientbrush.c 和 gradientbrush.h。编辑这些文件,执行一个替换操作,以便名为 paintbrush、Paintbrush、或 PAINTBRUSH 的任何事物都分别更改为新名称 gradientbrush。重要的是在所有实例中都应区分大小写。这将为您的工具创建一个新的 GObject 类。

对于运行 make 时将被编译的新文件,您必须将它们添加到它们所在的目录中的 Makefile.am 文件中。当您执行一个构建时,这足以使它们被编译并链接到 GIMP 可执行文件中。

这只是创建了一个新的 GIMP 核心,这个核心现在什么也做不了;它必须集成到一个新工具并被注册。

当然,当一切都就绪时,您必须对这个绘图功能本身进行想要的更改,以便它执行想要的任务:使用一个放射状颜色渐变沿画笔绘图。(下面的 清单 10 中有实现此功能的代码。)

从现在开始,几乎所有需要的更改都涉及编辑大部分包含词汇 paintbrush 的文件。如果某个文件列示其他所有绘图工具,只需复制引用这个 paintbrush 的行并重写它们,以便它们在 gradientbrush 上操作。

注意,由于本示例中没有创建任何新图标或工具游标,以下 paintbrush 内容原样保留:GIMP_STOCK_TOOL_PAINTBRUSH, GIMP_TOOL_CURSOR_PAINTBRUSH

paint 目录中的代码负责实际使用各种工具更新图像。tools 目录中的代码负责将一个工具功能(比如 paintbrush 或 bucket-fill)绑定到用户界面。这两个目录都有创建新工具需要更新的文件。

要将这个 paintbrush 工具的一个副本添加到 paint 目录:

  1. 复制文件 gimppaintbrush.c 和 gimppaintbrush.h 为 gimpgradientbrush.*,将它们中出现的所有 paintbrush 更名为 gradientbrush(保留大小写)。
  2. 在 Makefile.am 中,添加对 gimpgradientbrush.h 和 gimpgradientbrush.c 的引用。
  3. 在 gimp-paint.c 中,将 include 子句添加到头文件 gimpgradientbrush.h;将函数 gimp_gradientbrush_register 添加到 gimp_paint_init 函数中的清单。
  4. 在 paint-types.h 中,dd 一个 typedef 行模拟(line analog)到 GimpPaintbrush 使用的行模拟。

要将 paintbrush 的一个副本添加到 tool 目录:

  1. 复制文件 gimppaintbrushtool(c 和 h)为 gimpgradientbrushtool 版本,并将内部引用更名为新工具名称。
  2. 在 Makefile.am 中,添加对新的 gimpgradientbrushtool 文件的引用。
  3. 在 gimp-tools.c 中,将 include 子句添加到头文件 gimpgradientbrushtool.h。将函数 gimp_gradientbrush_register 添加到 gimp_tools_init 函数中的清单。在函数 gimp_tools_register 中,将一个 else if 块模拟(block analog)添加到 paintbrush 使用的块模拟。在这里将 paint_code_name wet 为 gimp-gradientbrush

上述更改应该会获得成功。现在,当您构建 GIMP 时,工具箱中应该有一个额外的画笔图标可用,如图 1 所示。

图 1. GIMP 的工具箱,特色是新创建的工具
GIMP 的工具箱的屏幕快照,在列表末尾显示第二个画笔图标

对于代码本身,更改更广泛一些。清单 10 显示了 app/paint/gimpgradientbrush.c 文件中的 _gimp_gradientbrush_motion 的代码。这是创建这个新工具必须编写新代码的惟一位置。

清单 10. _gimp_gradientbrush_motion 的代码
void
_gimp_gradientbrush_motion (GimpPaintCore    *paint_core,
                            GimpDrawable     *drawable,
                            GimpPaintOptions *paint_options,
                            const GimpCoords *coords,
                            gdouble           opacity)
{
  GimpBrushCore            *brush_core = GIMP_BRUSH_CORE (paint_core);
  GimpContext              *context    = GIMP_CONTEXT (paint_options);
  GimpDynamics             *dynamics   = brush_core->dynamics;
  GimpImage                *image;
  GimpRGB                   gradient_color; 
  TempBuf                  *area;
  guchar                   *col;
  GimpPaintApplicationMode  paint_appl_mode;
  gdouble                   fade_point;
  gdouble                   force;
  guchar                   *colors;
  gint                      max_colors;
  gint                      x, y;
  gint                      u, v;
  guchar                   *area_data;
  GimpGradient             *gradient;
  gdouble                   cx, cy;

  image = gimp_item_get_image (GIMP_ITEM (drawable));
  fade_point = gimp_paint_options_get_fade (paint_options, image,
                                            paint_core->pixel_dist);

  opacity *= gimp_dynamics_output_get_linear_value (dynamics->opacity_output,
                                                    coords,
                                                    paint_options,
                                                    fade_point);
  if (opacity == 0.0)
    return;

  area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options,
                                         coords);
  if (! area)
    return;

  paint_appl_mode = GIMP_PAINT_INCREMENTAL;;

  gradient = gimp_context_get_gradient (GIMP_CONTEXT (paint_options));
  max_colors = (gint)(sqrt(area->width * area->width +
                      area->height * area->height) / 2) + 1;
  colors = g_malloc(max_colors * MAX_CHANNELS);

  for (x = 0; x < max_colors; x++)
    {
      gimp_gradient_get_color_at (gradient, GIMP_CONTEXT (paint_options),
                          NULL, (gdouble)x / max_colors,
                          paint_options->gradient_options->gradient_reverse,
                         &gradient_color);
       col = &colors[x * MAX_CHANNELS];
       gimp_rgba_get_uchar (&gradient_color,
                            &col[RED],  
                            &col[GREEN],
                            &col[BLUE],
                            &col[ALPHA]);
    }

  area_data = temp_buf_get_data(area);
  cx = area->width / 2;
  cy = area->height / 2;
  for (x = 0; x < area->width; x++)
     for (y = 0; y < area->height; y++)
        {
           u = (gint)sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) * MAX_CHANNELS;
           v = (x + y * area->width) * area->bytes;
           if (area->bytes >= 3)
             {
               area_data[v + RED] = colors[u + RED];
               area_data[v + GREEN] = colors[u + GREEN];
               area_data[v + BLUE] = colors[u + BLUE];
               if (area->bytes == 4)
                 area_data[v + ALPHA] = colors[u + ALPHA];
             }
          else
            {
              /* FIXME: get color value instead; */
              area_data[v] = colors[u + RED];
              if (area->bytes == 2)
                area_data[v] = colors[u + ALPHA];
            }
        }

  force = gimp_dynamics_output_get_linear_value (dynamics->force_output,
                                                    coords,
                                                    paint_options,
                                                    fade_point);

  /* finally, let the brush core paste the colored area on the canvas */
  gimp_brush_core_paste_canvas (brush_core, drawable,
                                coords,
                                MIN (opacity, GIMP_OPACITY_OPAQUE),
                                gimp_context_get_opacity (context),
                                gimp_context_get_paint_mode (context),
                                gimp_paint_options_get_brush_mode (paint_options),
                                force,
                                paint_appl_mode);
  g_free(colors);
}

这个绘图工具的每一个绘图笔触都将调用 _gimp_gradientbrush_motion 函数。该函数创建一个内部缓冲区,其大小为包含 GIMP 中正用于绘图的笔刷的矩形的对角线的一半;并使用从活动渐变色提取的颜色填充该缓冲区。然后,它使用一个映射填充该区域,这个映射是用于对每个笔触的绘图区域着色的图像缓冲区。与 gimppaintbrush.c 中的原始函数进行一个函数调用来使用一个纯色填充不同的是,这个新函数遍历每个像素,根据该像素与笔刷中心的距离分配一个颜色。

使用清单 10 中的代码替换 _gimp_gradientbrush_motion 的代码。使用这些更改构建 GIMP 时,键入 make;构建完成后,键入 make install as root,您应该在 GIMP 中拥有了一个功能完整的新工具。

图 2 展示了使用这个新工具和绘图动力系统创建的一个快速草稿,设置为跟踪绘图方向,使用一个高纵横比笔刷。

图 2. gradientbrush 工具使用示例
实际运行的新工具 gradiantbrush 的屏幕快照
实际运行的新工具 gradiantbrush 的屏幕快照

这样,您创建了自己的 GIMP 工具。

在决定创建一个新工具之前,也应该考虑编写一个 GIMP 插件。这个流程比更改核心要容易一些。但是,插件的功能有限,比如,不允许用户在图像窗口自身上交互。任何预览或用户交互都必须来自主程序。在另一方面,如果您编写一个插件,该插件可以单独分发,不必提供一个 GIMP 自定义版本就可供他人使用。

结束语

现在,您应该对如何检索 GIMP 源代码和依赖项,如何在 Linux 系统中构建 GIMP 有比较深入的理解。通过这个画笔示例,您了解到如何导航源代码树以及使用 grep 来查找实现您想修改的特性的代码。您还看到为 GIMP 创建一个新工具需要完成哪些步骤。也许某一天,您的新特性将进入 GIMP 源代码,供全世界数以百万计的用户使用。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source, Linux
ArticleID=512805
ArticleTitle=添加您自己的 GIMP 特性
publish-date=08232010