DrawOperation (列挙)
public DrawOperation
インポート元: CURL.GUI.STANDARD. package 内で定義されています CURL.GRAPHICS._2D.RENDERER2D.
要素リスト:
source
accumulate
add
blend
blend-premultiply
invert-destination
invert-source
mask

ソース ピクセルおよび出力先ピクセルを結合するアルゴリズムを定義します。

説明

DrawOperation は一般的にソース色がレンダリングされる方法を定義します。ほとんどの DrawOperation では、ソースおよび出力先の色の、アルファおよびカラーの各値の数学的関数としてこれを定義します。
たとえば、DrawOperation.mask は単にソース色のアルファ値を使用して、ピクセルがレンダリングされているかどうかを判別します。これは非常に一般的な DrawOperation で、透過セクションを含む Pixmap をレンダリングするのによく使用されます。
Renderer2dのデフォルト値は、DrawOperation.blendで、下にあるRenderer2d の実装がサポートしている場合、部分透過を許可します。
DrawOperationは、Renderer2d で使用されます。with-render-properties マクロで DrawOperation を選択します。次の例を参照してください。
有効な値は以下のとおりです。
  • accumulate — 各ソース チャネルをそのアルファ値で乗算し、それを出力先の対応チャネルに加算します。この演算の関数は次のとおりです。
    result-Channel = (src-Channel * src-A) + dst-Channel
  • add — ソース チャネルと対応する出力先チャネルを加算します。この演算の関数は次のとおりです。
    result-Channel = src-Channel + dst-Channel
  • blend — ソース アルファ値に基づいてソース チャネルと出力先チャネルを混合します。この演算の関数は次のとおりです。
    result-Channel = (src-Channel * src-A) + (dst-Channel * (1 - src-A))
  • blend-premultiply — アルファ値がすでにソースに乗算されている場合に、ソース チャネルと出力先チャネルを混合します。この演算の関数は次のとおりです。
    result-Channel = src-Channel + dst-Channel * (1 - src-A)
  • invert-destination — 出力先を反転させます。これはイメージの選択操作で使用できます。この演算の関数は次のとおりです。
    result-Channel = 1 - dst-Channel
  • invert-source — ソースを反転させます。この演算の関数は次のとおりです。
    result-Channel = 1 - src-Channel
  • mask — ソース アルファ値に基づいてソース チャネルと出力先チャネルを混合します。ただし、アルファ値は 0 または 1 のどちらかである必要があります。そうでない場合はこの関数の動作は未定義になります。この演算の関数は次のとおりです。
    result-Channel = (src-Channel * src-A) + (dst-Channel * (1 - src-A))
  • source — ソースを直接、出力先にコピーします。この演算の関数は次のとおりです。
    result-Channel = src-Channel

注意事項

ほとんどの DrawOperation は、標準の GUI ツールキットである Renderer2d や、他の "ホスト" Renderer2d でサポートされていません。
次の DrawOperation Renderer2d でサポートされています。:
  • DrawOperation.source
  • DrawOperation.mask
他の DrawOperation を使用するために、"非ホスト" Renderer2d を使用してレンダリングしなければなりません。Renderer2dGraphicAntialiasedFrameCanvas を使用したり、Renderer2d.create-offscreen を使用して独自のオフスクリーン Drawable を作成することで、そのようなレンダラーを取得することができます。
バージョン 6.0 からは、描画に使用される Renderer2d に影響を与えるためにトップレベルで set-rendering-modeView.set-rendering-mode を使用することができます。詳細に関しては、RenderingMode を参照してください。
DrawOperation.blend は、全ての"非ホスト" レンダラーでサポートされています。残りの DrawOperation は、advanced-draw-operations? フラグが true でレンダラーが作成される場合にのみサポートされます。このフラグは、上記のテクニックで設定されます。
DrawOperation が設定された Renderer2d でサポートされているかどうかを確認するために、Renderer2dCapabilities.draw-operation-supported? を使用することができます。(Renderer2d.caps を参照してください。)レンダラーによっては他のレンダラーよりサポートしていることがあるので、これは便利かもしれません。(たとえば、Macintosh における標準のホスト レンダラーは DrawOperation.blend をサポートしますが、ほとんどのホスト レンダラーではサポートしていません。)開発者はこの呼び出しの結果が非常にプラットフォームに依存したものであることに注意する必要があります。この理由により、このフラグは予備のロジックを実装するためにのみ便利になります。たとえば、混合を利用できる場合に特別な効果を追加するとします。しかし、混合が利用できない場合に従来の外観に戻すよう追加することができます。アプリケーションが、設定された DrawOperation に依存している場合、必要な操作をサポートしている Renderer2d の作成に関しては、上記のアドバイスに従うことを推奨します。
サポートされていない DrawOperation を使用すると、定義されていない結果を生むことになります。これらの結果が矛盾なく見えたとしても、それらを信頼してはいけません。このルールに関する唯一の例外は、DrawOperation.blend です。混合をサポートしていない全ての Renderer2dmask と同等の blend を扱うことが保証されています。
デスティネーション アルファ値はサポートされていないことに注意してください。デスティネーション アルファ値はレンダリング操作に決して影響を与えません。又、ファイナル アルファ値(例えば、Drawable.to-Pixmap によって取得される)は、定義されず信頼されるべきではありません。

次のテーブルは、各描画オペレーションがサポートされていると仮定した場合の結果を示しています。(内部的には、advanced-draw-operations?true に設定されて作成されたオフスクリーン Renderer2d を使用しています。)
グリッドの各カラムは 2 つの三角形を示しています。右の三角形が最初に描画され、常に完全に不透明な赤色のソリッドで描画されます。それは常に DrawOperation.source を使用します。左の三角形は各行で異なる DrawOperation と各列で異なる透明度の値を使用します。ボックスの一つ又は左の "choose color" ボタンをクリックすることで左側の三角形に描画される FillPattern を変更することが可能です。




次の例では、スムーズに透過を混合した Pixmap の作成とレンダリングを示しています。レンダリングには Renderer2dGraphic が使用されています。また、効果的なアニメーション技術も示しています。アニメーションは、しばらくしたら停止するようになっています。Shape.draw-operation から描画操作を理解するのに役立つ例を参照することができます。

例: Renderer2dGraphic を用いた高度な DrawOperation の使用
||filter-pixmap と scaleにアクセスする必要があります。
{import * from CURL.GRAPHICS.IMAGEFILTER}

{let background:FillPattern =
    {FillPattern {url "curl://install/docs/default/images/grass.jpg"}}
}

|| .JPGイメージからPixmapを設定します。 保存されているjpgは、大きすぎるので 
|| もっと小さくしましょう。
{let our-pixmap:Pixmap =
    {filter-pixmap
        scale,
        scale = 50%,
        {Pixmap.from-url
            {url "curl://install/docs/default/images/adria.jpg"}
        }
    }
}
{set our-pixmap.ignore-alpha? = false}

|| 中心では完全に(100%)不透明で、端にむかって徐々に
|| 透過するようにpixmapを設定します。
|| 処理が早いので、doubleではなくて
|| floatを使用し続けることに注意してください
{let half-shortest-dimension:int =
    {min our-pixmap.width, our-pixmap.height} div 2
}
{for-pixel pixel at x, y in our-pixmap do
    let delta-x-from-center:int =
        (x - our-pixmap.width div 2)
    let delta-y-from-center:int =
        (y - our-pixmap.height div 2)
    let distance-from-center:float =
        {sqrt
            ((delta-x-from-center * delta-x-from-center) +
             (delta-y-from-center * delta-y-from-center))
            asa float
        } asa float
    let alpha:float =
        1.0f - (distance-from-center / half-shortest-dimension)
    || alpha may be negative, since it's possible
    || for distance-from-center to be greater than
    || half-shortest-dimension.  We'll clamp it to 0, below.
    set pixel =
        {Pixel.from-float
            pixel.red,
            pixel.green,
            pixel.blue,
            alpha = {min 1, {max 0, alpha}}
        }
}

|| PixmapからFillPatternを作成します。これを作成しない場合、以下の
|| render-rectangleの呼び出しは、それが呼び出される度に暗黙的にFillPattern
|| を作成します。
{let our-fillpattern:FillPattern = {FillPattern.from-pixmap our-pixmap}}

|| pixmapの位置は、左から右、上から下に変動します。
|| また、それは時間とともに回転します。
|| ここで、位置の計算に使われる変数を定義します。

{let constant pixmap-horizontal-period:Time = 6s}
{let constant pixmap-vertical-period:Time = 2.5s}
{let constant pixmap-rotation-period:Time = 1.29s}
{let constant pixmap-min-rotation-angle:Angle = -20deg}
{let constant pixmap-max-rotation-angle:Angle = 20deg}


|| 厳密にいえば、これは本当の時間を追跡しません。
|| これはアニメーションが利用可能な時にのみ、進みます。 
{let animation-time:Time = 0s}

{define-proc package {redraw-proc
                         rg:Renderer2dGraphic,
                         r2d:Renderer2d,
                         dirty-area:#RectangleSet
                     }:void
    let drawable:Drawable = {non-null rg.drawable}
    
    || First, clear drawable using a FillPattern
    {r2d.render-rectangle
        0m, 0m,
        drawable.width, drawable.height,
        fill-pattern = background
    }

    let pixmap-size:Distance2d =
        {Distance2d
            r2d.pixel-size * our-pixmap.width,
            r2d.pixel-size * our-pixmap.height
        }

    || 補間量(アルファと呼ばれます)の値を計算する場合、
    || 何か変動させるものを作成するためにサイン関数を使います。
    || この結果は、-1から1の間になります。ですから(補間にあうように)
    || これを0から1に調整します。 
    let pixmap-pos:Distance2d =
        {Distance2d
            {interpolate
                0m,
                (1 + {sin (animation-time / pixmap-horizontal-period) * 360deg}) / 2,
                drawable.width
            },
            {interpolate
                0m,
                (1 + {sin (animation-time / pixmap-vertical-period) * 360deg}) / 2,
                drawable.height
            }
        }
    let pixmap-angle:Angle =
        {interpolate
            pixmap-min-rotation-angle,
            (1 + {sin (animation-time / pixmap-rotation-period) * 360deg}) / 2,
            pixmap-max-rotation-angle
        }
    
    || 透過の為に、後ろから前への順序でオブジェクトを描画する必要が
    || あります。これは、各操作があて先(既に描画されている)ピクセル
    || を元の(これから描画される)ピクセルと混合させるので
    || この発生順序は重要です。
    || 幸いなことに、すでにbackgroundを描画しています。
    {with-render-properties
        || Try different DrawOperations!  add and
        || accumulate are interesting in particular.

        draw-operation = DrawOperation.blend,

        translation = pixmap-pos,
        rotation = pixmap-angle,
        translation = -0.5 * pixmap-size

        on r2d do

        {r2d.render-rectangle
            0m,
            0m,
            pixmap-size.x,
            pixmap-size.y,
            fill-pattern = our-fillpattern
        }
    }
}

{let graphic:Renderer2dGraphic =
    {Renderer2dGraphic
        width = 3in,
        height = 2in,
        repaint-handler = redraw-proc,
        advanced-draw-operations? = true
    }
}
{value graphic}

|| これは、アニメーションのスタートをマークします。
|| アニメーションを停止させるためだけに使います。
|| (ずっとアニメーションを動かしたままにしたくないので)

{let animation-start-time:DateTime = {DateTime}}
{let constant timer-duration:Time = 20s}
{let constant timer-frequency:Frequency = 20fps}

|| これは、アニメーションの”最後”の時間をマークします。
|| どの位、時間が経過したかを知るために使います。
|| これはアニメーションのフレームを描画した最後の時間だからです。
|| これは、アニメーション時間の値をどのくらい進めるのか知る方法です。
{let last-animation-time:DateTime = {DateTime}}

{let timer:Timer =
    {Timer
        enabled? = false,
        frequency = timer-frequency,
        {on TimerEvent do
            let time-now:DateTime = {DateTime}
            let time-elapsed:Time = {last-animation-time.elapsed ending=time-now}
            set last-animation-time = time-now

            {inc animation-time, time-elapsed}

            {graphic.update-drawable}

            {if {animation-start-time.elapsed} > timer-duration then
                set timer.enabled? = false
            }
        }
    }
}

{CommandButton
    label = "Start animation!",
    {on Action do
        || Reset the animation
        set last-animation-time = {DateTime}

        || Reset our "animation start" timer
        set animation-start-time = last-animation-time

        || Enable the timer
        set timer.enabled? = true
    }
}