我正在使用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曲面,渲染它,然后将它保存到文件中。它看起来基本上是这样的:
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;
这两种方法似乎都有效——它们生成了正确的光栅图像输出(尽管,当我尝试并行执行一系列操作时,"开罗方式"有一些错误——我最终会耗尽内存)。
我的问题是:为什么旧的/不推荐的方式(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。例如,如果我将输入文件的数量增加到720个(从16个),并使用Parallel.ForEach循环,我最终会得到0 MB的可用RAM,系统就会陷入停顿(最终,调试过程退出,我的系统会回来……但它会锁定一分钟左右)。
我的问题的简单答案是只使用不推荐使用的方法,但为什么不推荐使用呢?开罗的做法在任何方面都更好吗?
如果有人想看更多我的代码,请告诉我,我会添加它。我试图将我发布的代码精简到相关的部分(实际的p/Invoke代码,而不是调用它的C#代码)。
首先,发生内存不足错误的原因可能是代码内存泄漏。您的示例不调用任何清理函数(如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_Handlee_get_pixbuf()和gdk_pixbuf_save()难道还不够吗?