FreeBASIC マニュアルのトップに戻る

FreeBASIC ProPgAntiFlickering

目次→教本→プログラマーのための案内Graphics Mode Refresh and Anti-Flickering←オリジナル・サイト

描画モードの更新とアンチフリッカリング 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

描画モードの更新 のための アンチ・フリッカー コーディング方法(画面やビューポートをクリアした後)。

序文:
描画モードでウィンドウを更新(予防なしで再描画)すると、迷惑なちらつきが発生する可能性があります (これは、ユーザーの更新フェーズで取得した、不要な中間画像が表示されるためです)。
この現象は、完全に更新される前に、ウィンドウ全体が最初に消去された場合に明らかに激しくなり、これが繰り返し行われます。

このページでは、この現象に対応するための、基本的な 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
注:
ページめくりとダブルバッファリング(上記)は、作業ページが、ここにあるように、各反復で完全に更新される場合、機能的に同等です(ただし、内部ではありません)。

完全なプログラム・リスト
ProPgAntiFlickering
コード:
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のミリ秒単位の希望値)です。

参照
プログラマーのための案内に戻る
←リンク元に戻る プログラム開発関連に戻る

ページ歴史:2019-09-02 15:25:08
日本語翻訳:WATANABE Makoto、原文著作者:fxm

ホームページのトップに戻る

表示-非営利-継承