2

C# と P/Invoke を使用して GDK ライブラリにアクセスしています。私の目標は、一連の SVG ファイルをラスター イメージ (具体的には png) に変換することであり、GDK ライブラリを使用することが最も信頼性が高く正確なようです。

Gnome/Cairo のドキュメントを読んだ後、これを達成するための 2 つの基本的なアプローチを見つけました。これらのアプローチの 1 つは非推奨の関数を使用し、もう 1 つは使用しません。

最初のアプローチは推奨されていませんが、間違いなくより単純でわかりやすいもので、基本的には次のようになります。

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;

非推奨ではない2 番目の方法では、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)が新しい/Cairo の方法よりも著しく速いのはなぜですか? 私のテストでは、最初のアプローチが全面的に高速であることが示されています (1 つのファイル/複数のファイル、標準の C# ForEach/Parallel.ForEach)。

たとえば、16 個の入力ファイル (出力サイズが 6000x4200) で並列処理がない場合、最初のアプローチは ~2:15.89 秒かかります。2 番目のアプローチでは、約 2:37.95 かかります。並列処理 (Parallel.Foreach が私の P/Invoke コードを呼び出します) でも、結果は似MaxDegreesOfParallelismています。デフォルトに設定すると、非推奨のアプローチで 30.7 秒、Cairo アプローチで 36.95 秒かかります。

Cairo もかなり多くのメモリを使用しているようです。さらに、Cairo は変換ごとにより多くのリソースを使用するだけでなく、RAM のすべてを使用しないようにする方法も知らないようです。たとえば、入力ファイルの数を (16 から) 720 に増やし、Parallel.ForEach ループを使用すると、空き RAM が 0 MB になり、システムが停止します (最終的に、デバッグ プロセスは終了します)。 、そして私のシステムは戻ってきます...しかし、それは1分ほどロックアップします)。

私の質問に対する簡単な答えは、非推奨のアプローチを使用することですが、なぜ非推奨なのですか? カイロのアプローチは、何らかの点で優れていますか?

誰かが私のコードをもっと見たい場合は、お知らせください。追加します。投稿したコードを関連するビット (実際の P/Invoke コードであり、それを呼び出す C# コードではありません) だけに切り詰めようとしました。

4

1 に答える 1

1

まず、コードにメモリ リークがあるため、メモリ不足エラーが発生する可能性があります。あなたの例では、クリーンアップ関数 (cairo_destroy()、cairo_surface_destroy()、gdk_pixbuf_unref()、g_object_unref() など、発生した GError の何か) を呼び出しません。

次に、2 番目のコードは実際に何を描画しますか? カイロ サーフェスとコンテキストを作成し、それに rsvg を描画しますが、rsvg_handle_get_pixbuf() を呼び出すと、新しいカイロ サーフェスが内部的に作成され、すべてが再度描画されます。つまり、rsvg_handle_new_from_file()、rsvg_handle_get_pixbuf()、gdk_pixbuf_save() を呼び出すだけで十分ではないでしょうか?

于 2013-11-29T16:41:50.683 に答える