Skip to content

Instantly share code, notes, and snippets.

@miau
Last active December 15, 2015 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miau/5277670 to your computer and use it in GitHub Desktop.
Save miau/5277670 to your computer and use it in GitHub Desktop.

SumatraPDF 先読み対応バージョン

概要

SumatraPDF は

  • 前後 2 ページずつの先読みを行う
  • 直近 60 ページのキャッシュを保持する

という動作になっていますが、これを

  • ファイルを開いた時点で全ページの先読みを行う
  • 直近 1500 ページのキャッシュを保持する

動作に変更したものです。

注意点

1500 ページを超えるドキュメントを開いた場合は、破棄されたキャッシュに対して 即座に先読みを行おうとするため、パフォーマンスが低下する可能性があります。

単純に全ページのキャッシュを保持するので、メモリを大量に消費します。 たとえば 1920x1080 の環境で 500 ページの縦書きドキュメントを 「ページに合わせる」設定で開いた場合、

  • 764x1080 pixels × 32 bit × 500 = 1.65 GB

程度のメモリを消費することになります。

メモリが足りない場合、キャッシュから追い出された部分の表示が崩れます。 32 bit OS で試したところ、メモリが足りる計算でもうまく表示されない ケースがあったため、メモリが潤沢な 64 bit OS での利用を推奨します。

PC のスペック、画面の解像度、ドキュメントの構成等によりますが、 先読みには数十秒~数分程度の時間がかかります。 進捗の表示機能もないため使いづらいとは思いますが、CPU の利用率を確認すると 先読みが終わったか判断できると思います。

ソースコードの説明(開発者向け)

SumatraPDF 2013-03-30 時点のソース(バージョンとしては 2.2.1 と 2.3 の間に相当) を元に変更を加えており、ソースは以下の URL に置いています。

変更を行ったコミットは

で、変更点は下記のとおりです。 (こういうつもりで変更した、という情報です。正しいかどうかはわかりません。)

+    // render any page
+    return true;
+

レンダリングを行うスレッドはメインスレッドとは別になっており、 スタックからレンダリングのリクエストを受け取って処理するのですが、 指示されたページが表示中かのチェックを行っています。

先読みでレンダリングを行うため、そのチェック処理が 常に true を返すよう変更しています。

+        // prerender all pages
+        for (int pageNo = PageCount(); pageNo >= 1; --pageNo) {
+            dmCb->RequestRendering(pageNo);
+        }
+

全ページのレンダリング指示を行っています。 「前後 100 ページをキャッシュする形に変更したい」 といった場合は、この部分を変更すれば対応可能です。

-#define CONSERVE_MEMORY
+#undef CONSERVE_MEMORY

表示されなくなったページのキャッシュを解放して、メモリを節約するオプションです。 先読み対応のため無効化しています。

-                PageRenderRequest tmp;
-                tmp = requests[requestCount-1];
-                requests[requestCount-1] = *req;
-                *req = tmp;
+                PageRenderRequest tmp = requests[i];
+                memmove(&requests[i], &requests[i + 1], sizeof(requests[0]) * (requestCount - i - 1));
+                requests[requestCount-1] = tmp;

レンダリングのリクエストを行う際に、もしそのページのレンダリングが すでにリクエストされていたら、スタックの先頭に移動させる処理です。

現在表示中のページを優先して表示させるための処理ですが、 「スタック中から該当ページのリクエストを発見した場合は スタックの先頭のものと入れ替える」という処理になっており、 1 画面に複数ページを表示している場合には期待通りに動作していませんでした。 (複数のページでスタックの先頭を取り合う形になり、1 ページぶんしか 優先して表示されません。元々はスタックのサイズが 8 だったので 問題になっていなかったようです。)

リクエストをスタックの先頭に移動させる際、その間にあったデータを 1 つずつずらす処理に変更しています。

-#define MAX_PAGE_REQUESTS 8
+#define MAX_PAGE_REQUESTS 1500

レンダリングを行うスレッドへのリクエストスタックの最大長です。 ファイル読み込み時にまとめてリクエストを行うため、大きな値に設定しています。

-#define MAX_BITMAPS_CACHED 64
+#define MAX_BITMAPS_CACHED 1500

キャッシュされる画像の最大数です。 手元のファイルでは 1300 ページ程度のものが最大だったため、 1500 に設定しています。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment