2

我正在使用 C# 和 P/Invoke 来访问 GDK 库。我的目标是将一组 SVG 文件转换为光栅图像(特别是 png),并且使用 GDK 库似乎是最可靠/准确的。

在阅读了一些 Gnome/Cairo 文档之后,我发现了两种基本方法来实现这一点。其中一种方法使用不推荐使用的函数,而另一种则不使用。

第一种方法已被弃用,但可以说更简单/直接,基本上看起来像这样:

bool result = false;
ptrPixbuf = rsvg_pixbuf_from_file_at_size(svgFilePath, width, height, out ptrError);
if (ptrError == UIntPtr.Zero && ptrPixbuf != UIntPtr.Zero)
{
    bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputFilePath, out ptrError, UIntPtr.Zero);
    if (ptrError == UIntPtr.Zero && isSaved == true)
    {
        result = true;
    }
}
return result;

第二种方法并未被弃用,它涉及设置 Cairo Surface,渲染它,然后将其保存到文件中。它看起来基本上是这样的:

bool result = false;
ptrRsvgHandle = rsvg_handle_new_from_file(svgFilePath, out ptrError);
if (ptrError == UIntPtr.Zero)
{
    ptrCairoSurface = cairo_image_surface_create(CairoFormat.CAIRO_FORMAT_ARGB32, width, height);
    if ((cairo_surface_status(ptrCairoSurface) == CairoStatus.CAIRO_STATUS_SUCCESS)
    {
        ptrcairoContext = cairo_create(ptrCairoSurface);
        if ((cairo_status(ptrCairoContext) == CairoStatus.CAIRO_STATUS_SUCCESS))
        {
            bool isRendered = rsvg_handle_render_cairo(ptrRsvgHandle, ptrCairoContext);
            if (isRendered)
            {
                ptrPixbuf = rsvg_handle_get_pixbuf(ptrRsvgHandle);
                if (ptrPixbuf != UIntPtr.Zero)
                {
                    bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputFilePath, out ptrError, UIntPtr.Zero);
                    if (ptrError == UIntPtr.Zero && isSaved == true)
                    {
                        result = true;
                    }
                }
            }
        }
    }
}
return result;

这两种方法似乎都有效 - 它们生成正确的光栅图像输出(尽管当我尝试并行执行一堆操作时,“Cairo 方式”存在一些错误 - 我最终会耗尽内存)。

我的问题是:为什么旧的/不推荐使用的方式(rsvg_pixbuf_from_file_at_size明显快于新的/开罗方式?我的测试显示第一种方法更快(一个文件/多个文件,标准 C# ForEach/Parallel.ForEach)。

例如,对于 16 个输入文件(输出尺寸为 6000x4200)并且没有并行处理,第一种方法需要大约 2:15.89 秒。第二种方法大约需要 2:37.95。使用并行处理(Parallel.Foreach 调用我的 P/Invoke 代码),结果是相似的——MaxDegreesOfParallelism设置为默认值时,不推荐使用的方法需要 30.7 秒,而 Cairo 方法需要 36.95 秒。

开罗似乎也使用了更多的内存。此外,除了简单地为每次转换使用更多资源之外,Cairo 似乎也不知道如何避免使用我所有的 RAM。例如,如果我将输入文件的数量从 16 个增加到 720 个,并使用 Parallel.ForEach 循环,我最终会得到 0 MB 的可用 RAM,并且系统会停止运行(最终,调试进程退出,我的系统又回来了……但它锁定了一分钟左右)。

我的问题的简单答案是只使用不推荐使用的方法,但为什么不推荐使用它?开罗方法在任何方面都更好吗?

如果有人想查看更多我的代码,请告诉我,我会添加它。我试图将我发布的代码缩减为相关位(实际的 P/Invoke 代码,而不是调用它的 C# 代码)。

4

1 回答 1

1

首先,您的内存不足错误可能会发生,因为您的代码存在内存泄漏。您的示例不调用任何清理函数(如 cairo_destroy()、cairo_surface_destroy()、gdk_pixbuf_unref()、g_object_unref(),可能是发生任何 GError 的东西)。

其次,您的第二个代码实际上绘制的是什么?您创建一个 cairo 表面和上下文,让 rsvg 绘制它,然后调用 rsvg_handle_get_pixbuf() 它在内部创建一个新的 cairo 表面并再次绘制所有内容。换句话说,调用 rsvg_handle_new_from_file()、rsvg_handle_get_pixbuf() 和 gdk_pixbuf_save() 还不够吗?

于 2013-11-29T16:41:50.683 回答