描画モードの更新 のための
アンチ・フリッカー コーディング方法(画面やビューポートをクリアした後)。
序文:
描画モードでウィンドウを更新(予防なしで再描画)すると、迷惑なちらつきが発生する可能性があります
(これは、ユーザーの更新フェーズで取得した、不要な中間画像が表示されるためです)。
この現象は、完全に更新される前に、ウィンドウ全体が最初に消去された場合に明らかに激しくなり、これが繰り返し行われます。
このページでは、この現象に対応するための、基本的な
FreeBASIC コーディング技術を紹介します。
ちらつき防止のための主な原則
(OS に必要な CPU リソースに関係なく)描画ユーザー・タスクだけを考えると、ちらつきを避けるための 2つの主な方法があります:
- 最優先は、ブロック
'[Screenlock...Screenunlock]' を使って、更新のための描画指示をカプセル化します。
しかし、ドキュメントは、この使用に対して警告を啓発しています:
ページのロックを、可能な限り短時間に保持することを、強く推奨します。
画面がロックされている間は、画面の描画だけを行う必要があります。
入出力や待機は避けてください。
Win32 や Linux では、OS のイベントも処理する、スレッドを停止することにより、画面がロックされます。
画面が、長時間ロックされたままになると、イベント・キューがオーバーフローし、システムが不安定になる可能性があります。
- 優先順位の 2番目は、ロック時間が長すぎる場合、二重ビデオ・ページングの原則を使います。
'Screensync' 命令は、ちらつきを改善するために、この形式の命令 (
'wait &h3DA, 8') しかなかった、古い
QuickBASIC を思い出させます。
これは、2つのフレーム間の固定デッドタイムに関連する描画だけを同期できるので、経験的です。
描画をほとんど行わずに、時折使います。
通常、これらの2つ(または3つ)の方法を、一緒に使う必要はありません。
次に、ディスプレイ・ループ(存在する場合)で、ユーザーは十分な CPU リソースを OS に提供する必要があります(ループの最後に
'Sleep' 命令でスムーズ化)。
そうしないと、ユーザー・コントロールからディスプレイ・ループ自身を奪い、ぎくしゃくした表示になります。
注: 'Screensync' を使うと、OS に CPU リソース(グラフィック描画の終了からフレームトレースの終了までの不感時間)が提供されますが、制御不可能な方法(フレーム期間にリンクされているため)です。
小さな例で、ちらつき防止方法を学習します
画面を消去するループで、描画画面への描画/表示をアニメーション化するために、以下の 2 から 5 までの 4 つの異なる方法(方法 1 の後で)を明らかにする(そして異なる効率を比較する)小さなプログラム:
方法 1:
生のコーディングで画面に描画/表示するループ(方法なし)
アルゴリズム:
' SCREEN 19, , 2 'to enable double-paging
' SCREENSET 0, 0 'to cancel double-paging
'
' ┌─► CLS 'to clear the page
' │ Drawing 'to draw on the page
' │ Printing 'to print on the page
' └── Temporizing 'to avoid hogging the CPU
方法 2:
同期を用いて画面に描画/表示するループ
アルゴリズム:
' SCREEN 19, , 2 'to enable double-paging
' SCREENSET 0, 0 'to cancel double-paging
'
' ┌─► SCREENSYNC 'to synchronize between two frames
' │ CLS 'to clear the page
' │ Drawing 'to draw on the page
' │ Printing 'to print on the page
' └── Temporizing 'to avoid hogging the CPU
方法 3:
ロック付きで画面に描画/表示するループ
アルゴリズム:
' SCREEN 19, , 2 'to enable double-paging
' SCREENSET 0, 0 'to cancel double-paging
'
' ┌─► SCREENLOCK 'to lock the page's frame buffer
' │ CLS 'to clear the page
' │ Drawing 'to draw on the page
' │ Printing 'to print on the page
' │ SCREENUNLOCK 'to unlock the page's frame buffer
' └── Temporizing 'to avoid hogging the CPU
方法 4: ダブル・バッファリングで画面に描画/表示するループ
アルゴリズム:
' SCREEN 19, , 2 'to enable double-paging
' SCREENSET 1, 0 'to activate double-paging
'
' ┌─► CLS 'to clear the work page
' │ Drawing 'to draw on the work page
' │ Printing 'to print on the work page
' │ SCREENCOPY 'to copy the work page into the visible page
' └── Temporizing 'to avoid hogging the CPU
注:
ダブルバッファリングとページフリッピング(下)は、作業ページが、ここにあるように、各反復で完全に更新される場合、機能的に同等です(ただし、内部ではありません)。
方法 5: ページをめくりながら画面に描画/表示するループ
アルゴリズム:
' SCREEN 19, , 2 'to enable double-paging
' SCREENSET 1, 0 'to activate double-paging
' p0=0 : p1=1 'to initialize flipping
'
' ┌─► CLS 'to clear the work page
' │ Drawing 'to draw on the work page
' │ Printing 'to print on the work page
' │ SCREENSET p0, p1 'to set the work page to the p0 value, and the visible page to the p1 value
' │ SWAP p0, p1 'to exchange the values of p0 and p1
' └── Temporizing 'to avoid hogging the CPU
注:
ページめくりとダブルバッファリング(上記)は、作業ページが、ここにあるように、各反復で完全に更新される場合、機能的に同等です(ただし、内部ではありません)。
完全なプログラム・リスト
コード:
Declare Sub Draw_circle_recursion
(ByVal x
As Integer,
ByVal y
As Integer,
ByVal r
As Integer,
ByVal rmin
As Integer)
Dim I
As Integer =
0
Dim Inc
As Integer
Dim Key
As String
Dim Code
As Integer =
1
Dim Tempo
As Integer =
3
Dim T
As Single = Tempo
Dim p0
As Integer =
0
Dim p1
As Integer =
1
Screen 19, ,
2
Do
If Code =
4 Or Code =
5 Then
ScreenSet 1,
0
Else
ScreenSet 0,
0
End If
Do
Select Case Code
Case 2
ScreenSync
Case 3
ScreenLock
End Select
Cls
Draw_circle_recursion
(10 + I,
300,
9 + I * I /
29 /
29,
10)
Locate 1,
1
Select Case Code
Case 1
Print "1. Draw/Print loop to screen with raw coding:"
Print
Print " SCREEN 19, , 2 'to enable double-paging"
Print " SCREENSET 0, 0 'to cancel double-paging"
Print
Print " " &
Chr(218) &
Chr(196) &
Chr(16) &
" CLS"
Print " " &
Chr(179) &
" " &
" " &
" Drawing"
Print " " &
Chr(179) &
" " &
" " &
" Printing"
Print " " &
Chr(192) &
Chr(196) &
Chr(196) &
" Temporizing"; T;
" ms";
Case 2
Print "2. Draw/Print loop to screen with synchronizing:"
Print
Print " SCREEN 19, , 2 'to enable double-paging"
Print " SCREENSET 0, 0 'to cancel double-paging"
Print
Print " " &
Chr(218) &
Chr(196) &
Chr(16) &
" SCREENSYNC"
Print " " &
Chr(179) &
" " &
" " &
" CLS"
Print " " &
Chr(179) &
" " &
" " &
" Drawing"
Print " " &
Chr(179) &
" " &
" " &
" Printing"
Print " " &
Chr(192) &
Chr(196) &
Chr(196) &
" Temporizing"; T;
" ms";
Case 3
Print "3. Draw/Print loop to screen with locking:"
Print
Print " SCREEN 19, , 2 'to enable double-paging"
Print " SCREENSET 0, 0 'to cancel double-paging"
Print
Print " " &
Chr(218) &
Chr(196) &
Chr(16) &
" SCREENLOCK"
Print " " &
Chr(179) &
" " &
" " &
" CLS"
Print " " &
Chr(179) &
" " &
" " &
" Drawing"
Print " " &
Chr(179) &
" " &
" " &
" Printing"
Print " " &
Chr(179) &
" " &
" " &
" SCREENUNLOCK"
Print " " &
Chr(192) &
Chr(196) &
Chr(196) &
" Temporizing"; T;
" ms";
Case 4
Print "4. Draw/Print loop to screen with double buffering:"
Print
Print " SCREEN 19, , 2 'to enable double-paging"
Print " SCREENSET 1, 0 'to activate double-paging"
Print
Print " " &
Chr(218) &
Chr(196) &
Chr(16) &
" CLS"
Print " " &
Chr(179) &
" " &
" " &
" Drawing"
Print " " &
Chr(179) &
" " &
" " &
" Printing"
Print " " &
Chr(179) &
" " &
" " &
" SCREENCOPY"
Print " " &
Chr(192) &
Chr(196) &
Chr(196) &
" Temporizing"; T;
" ms";
Case 5
Print "5. Draw/Print loop to screen with page flipping:"
Print
Print " SCREEN 19, , 2 'to enable double-paging"
Print " SCREENSET 1, 0 'to activate double-paging"
Print " p0=0 : p1=1 'to initialize flipping"
Print
Print " " &
Chr(218) &
Chr(196) &
Chr(16) &
" CLS"
Print " " &
Chr(179) &
" " &
" " &
" Drawing"
Print " " &
Chr(179) &
" " &
" " &
" Printing"
Print " " &
Chr(179) &
" " &
" " &
" SCREENSET p0, p1"
Print " " &
Chr(179) &
" " &
" " &
" SWAP p0, p1"
Print " " &
Chr(192) &
Chr(196) &
Chr(196) &
" Temporizing"; T;
" ms";
End Select
Locate 30,
1
Print "<1>: Draw/Print with raw coding"
Print "<2>: Draw/Print with synchronizing"
Print "<3>: Draw/Print with locking"
Print "<4>: Draw/Print with double buffering"
Print "<5>: Draw/Print with page flipping"
Print "<+/->: Tempo setting (+/-)"
Print
Print "<Escape> or click [X]: Quit";
Select Case Code
Case 3
ScreenUnlock
Case 4
ScreenCopy
Case 5
ScreenSet p0, p1
Swap p0, p1
End Select
If I =
0 Then
Inc = +
1
ElseIf I =
480 Then
Inc = -
1
End If
I = I + Inc
Key =
Inkey
If Key =
"+" And Tempo <
10 Then
Tempo = Tempo +
1
ElseIf Key =
"-" And Tempo >
0 Then
Tempo = Tempo -
1
End If
If Tempo >
0 Then
T = Tempo
Else
T =
0.5
End If
Static As Integer K
K +=
1
If K >=
25 / T
Then
Sleep 25
K =
0
End If
Loop While Key <>
"1" And Key <>
"2" And Key <>
"3" And Key <>
"4" And Key <>
"5" And Key <>
Chr(27) And Key <>
Chr(255) &
"k"
Code =
Val(Key
)
Loop Until Key =
Chr(27) Or Key =
Chr(255) &
"k"
Sub Draw_circle_recursion
( ByVal x
As Integer,
ByVal y
As Integer,
ByVal r
As Integer,
ByVal rmin
As Integer )
Circle (x, y
), r, r
Shr 1
If r > rmin
Then
Draw_circle_recursion
(x + r
Shr 1, y, r
Shr 1, rmin
)
Draw_circle_recursion
(x - r
Shr 1, y, r
Shr 1, rmin
)
Draw_circle_recursion
(x, y + r
Shr 1, r
Shr 1, rmin
)
Draw_circle_recursion
(x, y - r
Shr 1, r
Shr 1, rmin
)
Draw_circle_recursion
(x + r
Shr 1, y + r
Shr 1, r
Shr 2, rmin
)
Draw_circle_recursion
(x - r
Shr 1, y + r
Shr 1, r
Shr 2, rmin
)
Draw_circle_recursion
(x + r
Shr 1, y - r
Shr 1, r
Shr 2, rmin
)
Draw_circle_recursion
(x - r
Shr 1, y - r
Shr 1, r
Shr 2, rmin
)
End If
End Sub
ループ内での一時化に関する注意(色々な PC での互換性のため):
適用される実際のテンポ化値は、常に 25 ミリ秒('Sleep 25')ですが、N で 1ループだけ(
'N = 25 / tempo', 'tempo' は、0.5から10のミリ秒単位の希望値)です。
参照