JavaFXでダブルバッファリングのやりかたが分かりません。
BufferedImageクラスとImageIOクラスを使ってイメージに書き込んで
それをSwingFXUtilsで変換してキャンバスに表示すればいいのでしょうか?
ほかに方法があるなら教えてください
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
結論をいうと「ダブルバッファリングをJavaFXで意識しないほうがよいのではないか」というのが自分の意見です。
グラフィックシステムにおいて「再描画」には2つぐらいの意味があると思います。
- 画面の重なり具合などの状態の変化に応じて不可視エリアが可視に変化した際に再描画する
- 表示すべきモデルデータが変化したときに表示内容を変えるために再描画する
ダブルバッファリングは上記の1,2ともに「複雑な描画プロセスが目に見えてしまうことによる画面のちらつきなどの見づらさを防止する」目的で用いる再描画のための工夫ですね。
swingでは(計算機の搭載メモリーが大きくなってきたためか)ダブルバッファリングがデフォルトになっていると思います(少なくともWindows JREではそのような動作です)。よって意識するとしたら「あえてダブルバッファリングをさせたくない特殊なケースのみ」ともいえるでしょう。
JavaFXではどうかといえば、プログラマーの仕事は「表示すべきビューモデルの設定」であり「どのように再描画(=レンダリング)」するかを意識する機会はほぼありません。そもそも1.のケースでswingの時代にあったJComponent.update/paintというメソッドが存在しません。ゆえに「どう再描画すべきか」をコードに書く余地がないともいえましょう。
さてJavaFXのCanvasについていえば、GraphicsContextへの描画がJComponent#paintComponentでGraphicsを用いて再描画することと似てはいるものの意味合いは違っており「再描画する処理」ではなく「どのような描画内容にするかをCanvasに対して教える(=モデルの変更)処理」ということに注意する必要があります。実際の再描画はJavaFXが必要なタイミングで自動的に行います。JavaFXのCanvasのリファレンスには以下のように書かれています。
呼出しごとに、必要なパラメータがバッファにプッシュされ、その後、パルスの最後に、レンダリング・スレッドによってCanvasノードのイメージ上にそれらのパラメータがレンダリングされます。
これを文字通り読むと「GraphicsContextに書いた通りに再描画が必要な契機でレンダリングします」といっているだけでダブルバッファリングを行うかどうかははっきりしません。よってCanvasへ複数の図形を描画している処理を、わざわざ自前で用意したオフスクリーンバッファを介して行うことに意味があるかどうかはCanvasがどのように再描画をしているかの実装に依存すると考えます。試しに以下のようなコードを実行してみました。(4万円代のdesktop PC, Windows10 64bit)
java
1package sample; 2 3import javafx.application.Application; 4import javafx.application.Platform; 5import javafx.scene.Scene; 6import javafx.scene.canvas.Canvas; 7import javafx.scene.canvas.GraphicsContext; 8import javafx.scene.control.ScrollPane; 9import javafx.scene.layout.StackPane; 10import javafx.scene.paint.Color; 11import javafx.stage.Stage; 12 13import java.util.Random; 14 15import static java.lang.Thread.sleep; 16 17/** 18 * Created by KSOH on 2016/12/21. 19 */ 20public class DoubleBufferingFX extends Application { 21 Canvas canvas; 22 23 @Override 24 public void start(Stage primaryStage) throws Exception { 25 canvas = new Canvas(1000, 1000); 26 ScrollPane scroll = new ScrollPane(canvas); 27 scroll.setPrefSize(300, 300); 28 StackPane border = new StackPane(scroll); 29 Scene scene = new Scene(border); 30 primaryStage.setScene(scene); 31 primaryStage.setTitle("Double Buffering"); 32 primaryStage.show(); 33 34 Thread t = new Thread(() -> { 35 for (;;) { 36 Platform.runLater(() -> { 37 drawCanvas(); 38 }); 39 try { 40 sleep(100); 41 } catch (InterruptedException e) { 42 break; 43 } 44 } 45 }); 46 t.setDaemon(true); 47 t.start(); 48 } 49 50 Random rnd = new Random(); 51 52 void drawCanvas() { 53 int N = 10000; 54 GraphicsContext gc = canvas.getGraphicsContext2D(); 55 for (int i = 0; i < N; i++) { 56 double x = rnd.nextDouble() * 1000; 57 double y = rnd.nextDouble() * 1000; 58 float hue = rnd.nextFloat() * 360; 59 Color c = Color.hsb(hue, 1F, 1F); 60 gc.setFill(c); 61 gc.fillRect(x, y, 2, 2); 62 } 63 } 64 65 public static void main(String[] args) { 66 Application.launch(args); 67 } 68}
上記はダブルバッファリングを自分では行わず、1000x1000のCanvas上で0.1秒ごとにランダムな位置へ1万個の2x2の正方形を描画するというものです。観察した限りではスクロールバーを動かしても描画がもたつくことはなく画面の更新も一瞬で更新されるように見えます。時間経過によってスクロールが遅くなることがないところをみるとGraphicsContextへ指示した描画命令そのものを覚えているわけではなさそうな感じですね。つまりうまいことダブルバッファリング的なことをしてくれていそうに見えます。
リファレンスへのあっさりとした記述や上記のコードの結果から私が感じた印象は「画面描画メカニズムに関するハードウェアの利用や最適化については複雑すぎてプログラマーに意識させるのは最早最適なAPI設計にはできなさそうなのでプログラマーが気にしなくてもJavaFXのブラックボックスの中でほぼうまくやってくれるように設計方針が進化している」でした。
投稿2016/12/21 09:53
総合スコア18394
0
ベストアンサー
JavaFXではダブルバッファリングの必要はありません。
Canvas の GraphicsContext に描けば、バッファに溜まっていき、pulseごとに描画されます。
Java
1GraphicsContext gc = canvas.getGraphicsContext2D(); 2gc.fillRect(0, 0, 100, 100);
https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/canvas/GraphicsContext.html
投稿2016/11/20 22:18
総合スコア1150
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。