2D レンダリング

要約:
  • Fill または Frame のような GUI オブジェクトをサブクラス化して、ほとんどの 2D レンダリングタスクに適した Renderer2d にアクセスします。
  • Renderer2d には、レンダリング プロパティの状態を保持し、表面へのレンダリングを実行するためのさまざまなメソッドがあります。
  • 特定の目的でより正確なレンダリングが必要な場合は、Renderer2dGraphic から Renderer2d を取得します。

2D レンダリングの概要

2Dレンダリングは、パッケージ CURL.GRAPHICS._2D.RENDERER2D に実装されています。これは、スーパーパッケージCURL.GRAPHICS.STANDARDに含まれ、CURL.GUI.STANDARDの一部として全てのアプレットにロードされます。2Dレンダリング パッケージは、比較的低水準のグラフィックAPIを提供します。2D レンダリングを直接使用する前に、シェイプ(形状) または、 グラフが提供する機能も検討してください。
2Dレンダリングに関連する主なクラスはRenderer2dです。このクラスはレンダリング プロパティの状態を保持し、描画面を持ち、描画面にレンダリングを実行する為の様々なレンダリング メソッドを持っています。
Curl®言語は、Renderer2dの2種類の実装を提供します。グラフィック オブジェクトのサブクラスを作成し、そのdrawメソッドをオーバーライドすることで、それらの1つにアクセスすることが出来ます。そのdrawメソッドは、引数としてRenderer2dを取り、そのRenderer2dを描画します。このレンダラの正確な機能はプラットフォームに依存します。Windows と Macシステム上では、アルファ混合やテクスチャの回転を含む、幾つかの高度な機能をサポートします。Linux上では、これらの機能を得る為には、フル機能のレンダラを使用する必要があります。
追加のレンダリング機能が必要な場合、Renderer2dGraphicを作成するか、そのrepaint-handlerに渡されるRenderer2dを使用して、フル機能を備えた Renderer2dを取得することが出来ます。オフスクリーン レンダラを使用する場合は、Frameのようなグラフィック オブジェクトを使用して、レンダリングされるイメージを画面に配置する必要があります。
これらのアプローチについては、 高度な2Dレンダリングで説明します。
全てのGUIツールキット オブジェクトは、Graphicからdrawメソッドを継承します。グラフィックオブジェクトは、drawメソッドに渡されるRenderer2dがレンダリングするオンスクリーン領域を表します。
描画可能な領域の変換は、Renderer2dの座標系の原点が、グラフィック オブジェクトの原点と同じになるように設定します。グラフィック オブジェクトの原点の位置は、Graphic.horiginGraphic.voriginオプションで設定することが出来ます。
以下のサンプルでは、Renderer2d.render-rectangleを使用して、ピンクの長方形をレンダリングするrect-procと呼ばれるプロシージャを定義しています。次に、そのdrawメソッドでrect-procを使用するRepaintFrameと呼ばれるFrameのサブクラスを作成します。サンプルでは、RepaintFrameのインスタンスを作成し、Renderer2drect-procに渡してピンクの長方形をレンダリングします。render-rectangleに渡されるx、y座標は、グラフィックオブジェクトの境界から相対的に長方形の位置を決定します。

例: Renderer2dの使用
{define-proc
    {rect-proc ren:Renderer2d}:void
    {ren.render-rectangle
        0.5cm, 0.5cm,
        2cm, 2cm,
        fill-pattern = {FillPattern.get-pink}
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {rect-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 4cm,
    height = 4cm
}

レンダリング メソッド

以前のサンプルでは、長方形の形状をレンダリングする為にRenderer2d.render-rectangleを 使用していました。Renderer2dは、様々な種類のレンダリングを実行するメソッドを提供します。 以下のメソッドは、閉じた図形をレンダリングします。
以下のメソッドは、 をレンダリングします。
Renderer2d は以下のメソッドも提供します。
以下のサンプルでは、render-triangleメソッドを示しています。

例: Renderer2d.render-triangleの使用
{define-proc
    {tri-proc ren:Renderer2d}:void
    {ren.render-triangle
        {Distance2d 1cm, 4cm},
        {Distance2d 4cm, 1cm},
        {Distance2d 0cm, 0cm},
        fill-pattern = {url "curl://install/docs/default/images/adria.jpg"}
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {tri-proc ren}
  }
}
{RepaintFrame
    width = 4cm,
    height = 4cm
}

レンダリング プロパティ

レンダリング プロパティは、どのようにレンダリングを実行するのかについての情報を提供します。Renderer2d.render-lineのようなメソッドを使ってレンダリングする際に使われます。各レンダリング メソッドのAPIリファレンスにはメソッドが使用するプロパティが説明されています。例えば、Renderer2d.render-lineは以下のレンダリング プロパティを使用しています。
レンダリング プロパティは、メソッドに引数として渡すことで設定できます。このようなメソッドは、レンダリング プロパティを指定されたとおりに一時的に設定して、レンダリング実行します。
with-render-propertiesマクロ ブロックで、レンダリング メソッドの1つまたは複数の呼び出しを囲むことが出来ます。このマクロは、全てのメソッド呼び出しのレンダリング プロパティに指定された値を設定し、レンダリングの実行後、コードブロックの最後で、プロパティの値を元に戻します。レンダリング メソッドを呼び出す時に、プロパティを設定することでwith-render-propertiesの中でプロパティの設定をオーバーライドすることが出来ます。
レンダリング メソッドでは、メソッドの呼び出しを通じて、関連する全てのプロパティの 設定ができるわけではありません。例えば、回転などの、変換レンダリング プロパティは 全てのレンダリング メソッドに影響しますが、with-render-properties によってのみ 設定できます。
以下のサンプルでは、4本の線を描画します。赤い線の色は、Renderer2d.render-lineの引数で設定されています。残りの3本の線は、with-render-properties内でのレンダリングの呼び出しで描画されます。colorとstroke-thicknessはwith-render-properties内で設定されています。緑色の線の色は、with-render-propertiesで設定されている色をオーバーライドするrender-lineへ引数を渡すことで設定されます。

例: 一連のレンダリング呼び出しをwith-render-propertiesでラップする
{define-proc
    {lines-proc ren:Renderer2d}:void
    {ren.render-line
        2cm, 0cm,
        2cm, 4cm,
        fill-pattern = {FillPattern.get-red}
    }
    {with-render-properties
        fill-pattern = {FillPattern.get-blue},
        stroke-thickness = 2pt
        on ren do
        {ren.render-line
            1cm, 2cm,
            3cm, 2cm}
        {ren.render-line
            fill-pattern = {FillPattern.get-green},
            0cm, 0cm,
            4cm, 4cm}
        {ren.render-line
            0cm, 4cm,
            4cm, 0cm}
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {lines-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 4cm,
    height = 4cm
}

クリッピング領域

レンダリングを実行する時に、Renderer2dは定義されたクリッピング領域内に 収まるものだけをレンダリングします。クリッピング領域外になるものはレンダリングされず、 画面に表示されません。最初のクリッピング領域は、Renderer2dに関連付けられている 描画面によって定義されているので、描画面の境界でクリッピングが発生します。 drawメソッドに渡されるRenderer2dは自動的に、そのクリッピング領域を Graphicによって占められているエリアに設定します。
clipping-regionレンダリング プロパティはクリッピング領域を設定する相対的なプロパティです。clipping-regionが、with-render-properties呼び出しで指定されている場合、レンダラーは、新しく設定されたクリッピング領域と以前のクリッピング領域の交点を計算し、その領域にレンダリングを制限します。with-render-propertiesコード ブロックの最後で、レンダラーは、描画面の境界に囲まれている当初のクリッピング領域に戻します。
Regionを使ってクリッピング領域を指定することが出来ます。領域を参照してください。以下のサンプルは、with-render-propertiesブロック内での、クリッピング領域を設定を示しています。Renderer2d.render-lineの2つの呼び出しは、Frameの角で接続される斜線を描画します。クリッピング領域内の線の部分だけが、RepaintFrameがレンダリングされる時に現れます。クリッピング領域を設定しているコード行をコメントにして、実行すると両方の線の全長を見ることが出来ます。

例: クリッピング領域の使用
{define-proc {clip-lines-proc
                 ren:Renderer2d
             }:void
    let clip:Region =
    {Region.from-rectangle
        0.5cm, 0.5cm,
        3cm,
        2cm
    }
    {with-render-properties
        fill-pattern = {FillPattern.get-blue},
|| Comment out the following line to see
|| the example without the clipping region
        clipping-region = clip,
        stroke-thickness = 2pt
        on ren do
        {ren.render-line
            0cm, 0cm,
            4cm, 4cm}
        {ren.render-line
            0cm, 4cm,
            4cm, 0cm}
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {clip-lines-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 4cm,
    height = 4cm
}

テクスチャ座標

テクスチャ マッピングを使用してレンダリングを実行すると、レンダリングされるシェイプの各頂点は、空間座標とテクスチャ座標を持ちます。空間座標は明示的に提供されます。しかしテクスチャ座標は、テクスチャが正確にレンダリングされたシェイプを覆うように通常自動的に生成されます。Renderer2d.render-pixmapRenderer2d.render-rectangleでは明示的にテクスチャ座標を記述することが出来るメソッドです。以下のサンプルではTextureFrameは、オプション パラメータuv1uv2を指定して、長方形にマッピングするテクスチャの部分を指定するテクスチャ座標を提供します。

例: テクスチャ座標の指定
{define-class TextureFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {ren.render-rectangle
        0cm, 0cm, 2cm, 2cm,
        fill-pattern = {url "curl://install/docs/default/images/adria.jpg"},
        uv1 = {Fraction2d .3, .3},
        uv2 = {Fraction2d .7, .7}
    }
  }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {ren.render-rectangle
        0cm, 0cm, 2cm, 2cm,
        fill-pattern = {url "curl://install/docs/default/images/adria.jpg"}
    }
  }
}
{RepaintFrame
    width = 2cm,
    height = 2cm
}
{TextureFrame
    width = 2cm,
    height = 2cm
}

2D 変換

Transformation2dクラスは、with-render-propertiesブロック内でtransformationプロパティを設定し、レンダリングされたオブジェクト上で変換を可能にします。
座標変換は、レンダリングの前に、全ての座標に適用される線形関数を示します。以下のサンプルで示す変換は、x及びyのスケーリング係数がそれぞれ2、3としてTransformation2d.local-scaleを使って作成されています。この変換は、各x座標を2倍し、各y座標を3倍する効果があります。

例: 長方形のスケーリング
{define-class package ExampleFrame {inherits BaseFrame}
  field private xform:Transformation2d =
      {Transformation2d}

  {constructor package {default
                           xform:Transformation2d,
                           ...
                       }
    set self.xform = xform
    {construct-super ...}
  }
  {method public {draw r2d:Renderer2d}:void
    let bounds:GRect = {self.layout.get-bounds}
    {with-render-properties
        transformation = self.xform
        on r2d do
        {r2d.render-rectangle
            0cm, 0cm, 2cm, 2cm,
            fill-pattern = 
                {url "curl://install/docs/default/images/adria.jpg"}
        }
    }
  }
}
{let identity-xform:Transformation2d = {Transformation2d}}
{let xform:Transformation2d = {Transformation2d}}
{xform.local-scale 3, 2}
{ExampleFrame
    background = {FillPattern.get-gray},
    identity-xform,
    width = 6cm,
    height = 4cm
}
{ExampleFrame
    background = {FillPattern.get-gray},
    xform,
    width = 6cm,
    height = 4cm 
}
次のサンプルでは、変換の平行移動を示しています。

例: 長方形の平行移動
{define-class package QuickExampleWidget {inherits BaseFrame}
  field private xform:Transformation2d =
      {Transformation2d}

  {constructor package {default
                           xform:Transformation2d,
                           ...
                       }
    set self.xform = xform
    {construct-super ...}
  }
  {method public {draw r2d:Renderer2d}:void
    let bounds:GRect = {self.layout.get-bounds}
    {with-render-properties
        transformation = self.xform
        on r2d do
        {r2d.render-rectangle
            0cm, 0cm, 2cm, 2cm,
            fill-pattern = {url "curl://install/docs/default/images/adria.jpg"}
        }
    }
  }
}
{let identity-xform:Transformation2d = {Transformation2d}}
{let xform:Transformation2d = {Transformation2d}}
{xform.local-translate 2cm, 2cm}
{spaced-hbox
    {VBox
        {QuickExampleWidget
            identity-xform,
            width = 4cm,
            height = 4cm,
            background = {FillPattern.get-gray}
        }
    },
    {VBox
        {QuickExampleWidget
            xform,
            width = 4cm,
            height = 4cm,
            background = {FillPattern.get-gray}
        }
    }
}
以下のサンプルは、テクスチャの回転を含んでいるので、幾つかのプラットフォーム上ではRenderer2dGraphicから取得されるRenderer2dを通じて利用できる高度な機能を必要とします。このタイプのRenderer2dの取得の詳細については高度な2Dレンダリングを参照してください。このサンプルでは、transform-procプロシージャは、Renderer2d.caps.can-render-rotated-textures?をチェックして2Dレンダラーがテクスチャの回転を扱うことができるかどうかを判断しています。
このサンプルでは、Transformation2dを使用して座標変換を行い、長方形を回転させています。テクスチャ変換は明示的には実行されません。テクスチャは長方形の回転の結果のとおり回転されます。

例: テクスチャを含む長方形の回転
{define-proc {identity-proc
                 ren:Renderer2d
             }:void
    {ren.clear fill-pattern = {FillPattern.get-gray}}
    {ren.render-rectangle
        0cm, 1cm,
        2cm, 2cm,
        fill-pattern =
            {url "curl://install/docs/default/images/adria.jpg"}
    }
}
{define-proc {transform-proc
                 ren:Renderer2d
             }:void
    {ren.clear fill-pattern = {FillPattern.get-gray}}
    {if ren.caps.can-render-rotated-textures? then
        {with-render-properties
            transformation = xform
            on ren do
            {ren.render-rectangle
                0cm, 1cm,
                2cm, 2cm,
                fill-pattern =
                    {url "curl://install/docs/default/images/adria.jpg"}
            }
        }
     else
        {ren.render-rectangle
            0cm, 1cm,
            2cm, 2cm,
            fill-pattern =
                {url "curl://install/docs/default/images/adria.jpg"}
        }
    }
}
{let xform:Transformation2d = {Transformation2d}}
{xform.local-rotate -30deg}

{define-class IdentityFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {identity-proc ren}
  }
}
{define-class TransformFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {transform-proc ren}
  }
}

{spaced-hbox
    {IdentityFrame
        width = 5cm,
        height = 3cm,
        background = "silver"
    },
    {TransformFrame
        width = 5cm,
        height = 3cm,
        background = "silver"
    }
}

FillPatternの一部のレンダリング

以下のサンプルでは、よりRenderer2dのより複雑な使用法を示します。このコードは、Renderer2d.render-rectangleを呼び出して、FillPatternの一部をレンダリングします。コマンドボタンをクリックするとピクチャの象限の位置が変わります。

例: Rendering parts of a FillPattern
{define-class public KidGraphic {inherits Frame}
  field fill-pattern:FillPattern
  field quads:{Array-of Fraction2d} =
      {new {Array-of Fraction2d},
        {Fraction2d 0.0, 0.0},
        {Fraction2d 0.5, 0.0},
        {Fraction2d 0.5, 0.5},
        {Fraction2d 0.0, 0.5}
      }
  {constructor public
    {default
        fill-pattern:FillPattern =
            {url "curl://install/docs/default/images/adria.jpg"},
        ...
    }
    {construct-super ...}
    set self.fill-pattern = fill-pattern
  }
  {method private {render-quadrant
                      renderer2d:Renderer2d,
                      quadrant:int,
                      uv:Fraction2d
                  }:void
    let bounds:GRect = {self.layout.get-bounds}

    let width:Distance = (bounds.lextent + bounds.rextent) / 2
    let height:Distance = (bounds.ascent + bounds.descent) / 2
    let x:Distance = -bounds.lextent
    let y:Distance = -bounds.ascent

    {if quadrant == 1 or quadrant == 2 then {inc x, width}}
    {if quadrant == 2 or quadrant == 3 then {inc y, height}}
    {renderer2d.render-rectangle
        x, y, width, height,
        fill-pattern = self.fill-pattern,
        uv1 = uv, uv2 = uv + {Fraction2d 0.5, 0.5}
    }
  }
  {method public {draw renderer2d:Renderer2d}:void
    {for i = 0 below 4 do
        {self.render-quadrant renderer2d, i, self.quads[i]}
    }
  }
  {method public {shift}:void
    let tmp:Fraction2d = self.quads[0]
    set self.quads[0] = self.quads[1]
    set self.quads[1] = self.quads[2]
    set self.quads[2] = self.quads[3]
    set self.quads[3] = tmp
    {self.request-draw}
  }
}
{value
    let obj:KidGraphic =
        {KidGraphic
            width = 2in,
            height = 2in
        }
    let button:CommandButton =
        {CommandButton
            width = 1in,
            label = "Rotate",
            {on Action do
                {obj.shift}
            }
        }
    {center
        {spaced-vbox
            obj,
            {HBox {Fill}, button, {Fill}}
        }
    }
}

パス

Pathは、無限に細く方向を持った 2 次元の曲線として正式に定義されています。線を描画する為の方向のセットとして解釈することが出来ます。方向は、点で構成され、Distance2dとして記述されます。点を接続するパス セグメントを作成する操作はPathOperationとして記述されます。結果のパスには、切れ目や直線の部分、曲線の部分を含めることが出来ます。
実際の線のレンダリングは、Renderer2dによって行われ、多くの場合、レンダリングを制御するプロパティをwith-render-propertiesを使って記述します。
以下の例で示すように、パスを作成する時にコンストラクタに対して点とパス操作を指定することが出来ます。このサンプルでは3つのパス操作move-toline-tocurve-toを使用して、直線セグメントと曲線からなるパスを生成しています。
curve-to操作に続く2つの点は、曲線を制御する点です。インタラクティブにベジェ曲線を描画させるグラフィック アプリケーションでは、これらの点をスクリーン上で視覚化し、それらをドラッグして曲線を形成します。パスの曲線部分を生成する為には、それらの点をx、yの座標として指定する必要があります。

例: シンプル Path
{define-proc public {draw-a-path
                        renderer2d:Renderer2d
                    }:void
    || Here is the Path definition:
    let path:Path = {Path
                        {Distance2d 0.5cm, 0.5cm},
                        PathOperation.line-to,
                        {Distance2d 3cm, 2cm},
                        PathOperation.move-to,
                        {Distance2d 2cm, 3cm},
                        PathOperation.curve-to,
                        {Distance2d 4cm, 3cm},
                        {Distance2d 4cm, 2cm},
                        {Distance2d 2cm, 0.5cm} 
                    }
    {renderer2d.render-path path}
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {draw-a-path ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 4cm,
    height = 4cm
}

セグメント作成メソッド

Pathは、パスを作成した後にパスに線セグメントを追加するメソッドも提供します。点をDistance2dとして記述し、メソッドの引数として渡します。各メソッドには、2つのバージョンがあります。ひとつは、引数を座標空間として解釈し、もう1つは、パスに記述された最後の点からの相対的に引数を解釈します。
以下は、座標空間内の点で実行されるメソッドの一覧です。
このサンプルでは、以前のサンプルと同じパスを作成するのにPath.move-toPath.line-toPath.curve-toメソッドを使用しています。

例: セグメント生成メソッドの使用
{define-proc public {draw-a-path
                        renderer2d:Renderer2d
                    }:void
    let path:Path = {Path}
    {path.move-to {Distance2d 0.5cm, 0.5cm}}
    {path.line-to {Distance2d 3cm, 2cm}}

    {path.move-to{Distance2d 2cm, 3cm}}
    {path.curve-to
        {Distance2d 4cm, 3cm},
        {Distance2d 4cm, 2cm},
        {Distance2d 2cm, 0.5cm}
    }
    {renderer2d.render-path path}
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {draw-a-path ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 4cm,
    height = 4cm
}
セグメント生成メソッドでは、curve-toによって作成される3次曲線に加えて、2次曲線と弧を作成することが出来ます。次の例では、3次曲線と2次曲線と弧を作成しており、全て同じ端点と制御点を使用しています。

例: 曲線の種類
{let start-point:Distance2d = {Distance2d 1cm, 4cm}}
{let control-point:Distance2d = {Distance2d 1cm, -1cm}}
{let end-point:Distance2d = {Distance2d 5cm, 4cm}}

{let path:Path = {Path}}
{path.move-to start-point}
{path.curve-to
    control-point,
    control-point,
    end-point
}
{path.move-to start-point}
{path.quadratic-curve-to
    control-point,
    end-point
}
{path.move-to start-point}
{path.arc-to
    2cm,
    2cm,
    0deg,
    false,
    false,
    end-point
}
{define-proc public {draw-a-path
                        renderer2d:Renderer2d,
                        path:Path
                    }:void
    on renderer2d do
    {renderer2d.render-path path}
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {draw-a-path ren, path}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 6cm,
    height = 4.5cm
}

相対セグメント生成メソッド

既に触れたとおり、Pathは、点引数をパス内の最後の点からの相対距離と解釈するセグメント生成メソッドも提供します。以下はこれらのメソッドの一覧です。
相対セグメント生成メソッドの使用法の1つは、異なる場所で同じ曲線を繰り返すことです。次のサンプルで示しているように、1つの曲線を複数回繰り返してパスを作り上げています。それぞれの新しい曲線は、最後の端点から始まっています。

例: Using relative segment-creation methods
{define-proc public {draw-a-path
                        renderer2d:Renderer2d
                    }:void
    let path:Path = {Path}
    {path.move-to {Distance2d 0.5cm, 0.5cm}}

    {for i:int = 0 to 5 do
        {path.curve-to-relative
            {Distance2d 1cm, 0cm},
            {Distance2d 2cm, 0cm},
            {Distance2d 2cm, 0.5cm}
        }
    }
    {with-render-properties
        fill-pattern = "darkgreen",
        stroke-thickness = 2pt
        on renderer2d do
        {renderer2d.render-path path}
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {draw-a-path ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 13cm,
    height = 5cm
}

なめらかな曲線

smooth-*メソッドは、先立つ曲線の最後の制御点の反射である暗黙的な最初の制御点を使用します。それは2つの曲線間の滑らかな接続を保証します。次のサンプルでは、Path.smooth-curve-toを前の曲線との滑らかな結合を行っています。またPath.closeを使用して、閉じたパス内の曲線の2端点を接続しています。

例: 滑らかに曲線を接続
{define-proc public {draw-a-path
                        renderer2d:Renderer2d
                    }:void
    let path:Path = 
        {Path}
    {path.curve-to
        {Distance2d 8cm, 2cm},
        {Distance2d 8cm, 3cm},
        {Distance2d 4cm, 4cm}
    }
    {path.smooth-curve-to
        {Distance2d 0cm, 8cm},
        {Distance2d 4cm, 8cm}
    }
    {path.close}
    {renderer2d.render-path path}
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {draw-a-path ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 7cm,
    height = 8.5cm
}

パス操作メソッド

Pathは、パスで動作するメソッドも提供します。 それらのうちいくつかを以下に挙げます。
以下のサンプルでは、これらのメソッドの幾つかを使用して、パスを操作しています。2次曲線で構成されたパスを作成し、Transformation2dtransform-cloneを使用して、別の場所にパスを複製しています。次にappendを使ってオリジナルのパスにクローンを追加しています。draw-a-pathプロシージャは出来上がったパスを三回描画します。それはwith-render-propertiesで色、太さ、平行移動を設定し、描画します。with-render-propertiesで設定される平行移動は、相対的であるので各ループの繰り返しでは、最後の位置に対してパスを配置します。

例: Pathの操作
{let path:Path = {Path}}
{path.move-to {Distance2d 0.5cm, 3cm}}
{path.quadratic-curve-to
    {Distance2d 1cm, -2cm},
    {Distance2d 3cm, 3cm}
}
{let xform:Transformation2d = {Transformation2d}}
{xform.local-translate 2.5cm, 0cm}
{let path1:Path = {path.transform-clone xform, out = path1}}
{path.append path1}

{define-proc public {draw-a-path
                        renderer2d:Renderer2d,
                        path:Path
                    }:void
    {for i:int = 0 to 2 do
        let r:double = 1/(i + .5)
        {with-render-properties
            fill-pattern = {FillPattern.from-rgb r, .4, .6, opacity = .5},
            stroke-thickness = 6pt,
            draw-operation = DrawOperation.blend,
            translation = {Distance2d (i * .7cm), (i * .3cm)}
            on renderer2d do
            {renderer2d.render-path path}
        }
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {draw-a-path ren, path}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 7.5cm,
    height = 4cm
}

Regions

Regionクラスは、ポリゴンのように、塗りつぶされた2次元の閉じた領域を示します。領域は、with-render-propertiesでクリッピング領域として使用することができ、成形されたウィンドウの形状を定義します。ウィンドウの形状の設定を参照してください。
Regionを作成するもっとも簡単な方法は、以下で示されるように from-rectangleコンストラクタを使用して、長方形(rectangle)から することです。

例: 長方形から Region の作成
{define-proc {region-proc ren:Renderer2d}:void
    {ren.render-region
        {Region.from-rectangle
            .5cm, .5cm, 5cm, 3cm
        },
        fill-pattern = "orange"
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {region-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 6cm,
    height = 4cm
}
他のコンストラクタでは、Regionの形状を柔軟に作成することが 出来ます。Region.from-vertices#factoryは、以下の例で示されるように、 引数として頂点のリストを取ります。

例: 頂点を指定してRegionの作成
{define-proc {region-proc ren:Renderer2d}:void
    {ren.render-region
        {Region.from-vertices
            {Distance2d 1cm, .5cm},
            {Distance2d .5cm, 3.5cm},
            {Distance2d 5.5cm, 3.5cm},
            {Distance2d 6cm, .5cm}
        },
        fill-pattern = "orange"
    }
}

{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {region-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 6.5cm,
    height = 4cm
}
頂点の順序は重要です。y軸で下向き方向を正とする座標系で反時計回りに指定しなければなりません。もしも頂点を時計回りに指定すると、領域の動作は定義されず、数学的に不安定になります。以下で示すように、自身と交差した領域を作成する頂点を指定することも出来ます。

例: 自己交差した Region
{define-proc {region-proc ren:Renderer2d}:void
    {ren.render-region
        {Region.from-vertices
            {Distance2d 6cm, .5cm},
            {Distance2d 5.5cm, 3.5cm},
            {Distance2d 1cm, .5cm},
            {Distance2d .5cm, 3.5cm}
        },
        fill-pattern = "orange"
    }
}

{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {region-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 6.5cm,
    height = 4cm
}
Region.from-vertex-array#factoryコンストラクタを使用して、領域を定義する頂点を配列で構成することも出来ます。このアプローチは、Regionのコンストラクタに直接渡すには不便なプロセスやデータ ソースからの頂点情報の時に、特に便利です。

例: 頂点の配列から Region の作成
{let arr:{Array-of Distance2d} = {new {Array-of Distance2d}}}
{let x:Distance = 0cm}
{let y:Distance = 6cm}

{arr.append {Distance2d x, y}}

{for i:int = 0 to 5 do
    set x = x + 1cm
    {if i mod 2 == 0 then
        set y = y - 2cm
     else
        set y = y + 1cm
    }
    {arr.append {Distance2d x, y}}
}
{arr.append {Distance2d x, 6cm}}

{define-proc {region-proc ren:Renderer2d}:void
    {ren.render-region
        {Region.from-vertex-array arr},
        fill-pattern = "orange"
    }
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {region-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 6cm,
    height = 6cm
}
Pathは、Regionの基礎になることが出来ます。閉じていないパスを指定すると、領域を作成する前に、それは閉じられます。以下のサンプルは、PathからRegionを生成しています。

例: Path から Region を生成
{define-proc public {draw-a-region
                        renderer2d:Renderer2d
                    }:void
    || Here is the Path definition:

    let path:Path = 
        {Path }
    {path.move-to {Distance2d 2.5cm, 0.5cm}}
    {path.arc-to-relative
        2cm,
        2cm,
        0deg,
        false,
        false,
        {Distance2d 0cm, 4cm}
    }
    {path.arc-to-relative
        2cm,
        2cm,
        0deg,
        false,
        false,
        {Distance2d 0cm, -4cm}
    }
    let region:Region =
        {Region.from-path path}
    {renderer2d.render-region
        region,
        fill-pattern = "orange"
    }
    
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {draw-a-region ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 5cm,
    height = 5cm
}
以下のサンプルでは、Region.from-vertex-array#factoryを使用して、五つの三角形として現れる、1つの自己交差Regionを作成しています。それは、Frameの原点から1インチに配置されています。

例: 領域の作成
{define-proc {region-proc ren:Renderer2d}:void
    || Here is the Region definition:
    let region:Region =
        {Region.from-vertex-array
            {new {Array-of Distance2d},
                {Double2d {cos 0deg}, {sin 0deg}} * 1in +
                {Distance2d 1in, 1in},
                {Double2d {cos 2*72deg}, {sin 2*72deg}} * 1in +
                {Distance2d 1in, 1in},
                {Double2d {cos 4*72deg}, {sin 4*72deg}} * 1in +
                {Distance2d 1in, 1in},
                {Double2d {cos 6*72deg}, {sin 6*72deg}} * 1in +
                {Distance2d 1in, 1in},
                {Double2d {cos 8*72deg}, {sin 8*72deg}} * 1in +
                {Distance2d 1in, 1in}
            }
        }
    {ren.render-region region, fill-pattern = "orange"}
}
{define-class RepaintFrame {inherits Frame}
  {constructor {default ...}
    {construct-super ...}
  }
  {method public {draw ren:Renderer2d}:void
    {region-proc ren}
  }
}
{RepaintFrame
    border-width = 1px,
    width = 2in,
    height = 2in
}

高度な 2D レンダリング

グラフィック オブジェクトのdrawメソッドに渡されるRenderer2dは、DrawOperationで定義されている全てのレンダリング操作をサポートするわけではありません。DrawOperation.sourceDrawOperation.maskをサポートすることを保証します。他の描画操作がサポートされるかどうかはプラットフォームに依存します。Windows プラットフォームと Mac プラットフォームにおいては、このレンダラーは、DrawOperation.blendと回転したテクスチャもサポートします。Renderer2dの全ての機能を引き出す必要がある時には、Renderer2dGraphicを通じてRenderer2dを取得するか、または、Renderer2d.create-offscreenメソッドを呼び出す必要があります。2Dレンダリングに追加の機能を提供する必要がある時だけ、Renderer2dGraphicRenderer2d.create-offscreenを呼び出してください。

Renderer2dGraphicインスタンスからRenderer2dの取得

Renderer2dGraphicは、Renderer2dとオフスクリーンDrawableへのアクセスを提供するGraphicです。
Renderer2dGraphicを生成する時、Renderer2dは、repaint-handlerに渡されます。関連したDrawable上でレンダリングを実行する為には、このRenderer2dを使用して、再描画プロシージャを記述します。Drawableには、Renderer2dGraphic.drawableでアクセスすることが出来ます。
Renderer2dGraphicは、インスタンス化し、その再描画ハンドラとしてprocを提供することで直接利用することが出来ます。ここでは、テクスチャで埋められた長方形を回転する簡単なサンプルを示します。以前のサンプルテクスチャを含む長方形の回転を、グラフィックのdrawメソッド内で回転したテクスチャのレンダリングをサポートしないプットフォームで実行させる為にどのように書き換えるかを示しています。

例: Renderer2dGraphicを使用してテクスチャを含む長方形の回転
{define-proc {identity-proc
                 rg:Renderer2dGraphic,
                 ren:Renderer2d,
                 area:#RectangleSet}:void
    {ren.clear fill-pattern = {FillPattern.get-gray}}
    {ren.render-rectangle
        0cm, 1cm,
        2cm, 2cm,
        fill-pattern =
            {url "curl://install/docs/default/images/adria.jpg"}
    }
}
{define-proc {transform-proc
                 rg:Renderer2dGraphic,
                 ren:Renderer2d,
                 area:#RectangleSet}:void
    {ren.clear fill-pattern = {FillPattern.get-gray}}
    {with-render-properties
        transformation = xform
        on ren do
        {ren.render-rectangle
            0cm, 1cm,
            2cm, 2cm,
            fill-pattern = 
                {url "curl://install/docs/default/images/adria.jpg"}
        }
    }
}
{let xform:Transformation2d = {Transformation2d}}
{xform.local-rotate -30deg}
{spaced-hbox
    {VBox
        {Renderer2dGraphic
            width = 4cm,
            height = 3cm,
            repaint-handler = identity-proc
        }
    },
    {VBox
        {Renderer2dGraphic
            width = 4cm,
            height = 3cm,
            repaint-handler = transform-proc
        }
    }
}

レンダラーの機能

Renderer2dGraphicの既定のコンストラクタは、ブール値の引数advanced-draw-operations?を取ります。それは、関連付けられたRenderer2dによってサポートされる描画操作を決定します。既定値は、falseです。全ての描画機能をサポートする必要がある場合には 、trueに設定します。
Renderer2dGraphic.capsプロパティで、グラフィックに関連付けられたレンダラーのRenderer2dCapabilitiesにアクセスすることが出来ます。このオブジェクトのプロパティと、Renderer2dCapabilities.draw-operation-supported?を使用して、どの描画機能をレンダラーが提供するのか決定することが出来ます。以下のサンプルでは、コマンドボタンをクリックした時にレンダラーの機能をポップアップ メッセージで表示します。以下の行をサンプルのコードから削除して実行してみてください。
advanced-draw-operations? = true,
レンダラーは、より制限され機能を提供することに注意してください。
Renderer2dGraphicは、DrawableGraphicからDrawableGraphic.occlusion-performance-modeプロパティを継承します。このプロパティは、パフォーマンスとレンダリングの精度に影響を与える可能性があります。SceneGraphicocclusion-performance-modeを参照してください。

例: レンダラーの機能の決定
{let message:VBox = {VBox}}
{define-proc {add-proc
                 dg:DrawableGraphic,
                 ren:Renderer2d,
                 area:#RectangleSet}:void
    {ren.clear fill-pattern = {FillPattern.get-maroon}}
    {message.clear}
    {message.add
        "accurate-fill-rules?: " &
        ren.caps.accurate-fill-rules?
    }
    {message.add
        "can-antialias?: " &
        ren.caps.can-antialias?
    }
    {message.add
        "can-render-rotated-textures?: " &
        ren.caps.can-render-rotated-textures?
    }
    {message.add
        "DrawOperation.accumulate: " &
        {ren.caps.draw-operation-supported? DrawOperation.accumulate}
    }
    {message.add
        "DrawOperation.add: " &
        {ren.caps.draw-operation-supported? DrawOperation.add}
    }
    {message.add
        "DrawOperation.blend: " &
        {ren.caps.draw-operation-supported? DrawOperation.blend}
    }
    {message.add
        "DrawOperation.invert-destination: " &
        {ren.caps.draw-operation-supported? DrawOperation.invert-destination}
    }
    {message.add
        "DrawOperation.invert-source: " &
        {ren.caps.draw-operation-supported? DrawOperation.invert-source}
    }
    {message.add
        "DrawOperation.source: " &
        {ren.caps.draw-operation-supported? DrawOperation.source}
    }
    {message.add
        "DrawOperation.mask: " &
        {ren.caps.draw-operation-supported? DrawOperation.mask}
    }
    {with-render-properties
        draw-operation = DrawOperation.add
        on ren do
        {ren.render-rectangle
            0.5cm, 0.5cm,
            3cm, 3cm,
            fill-pattern = 
                {url "curl://install/docs/default/images/adria.jpg"}
        }
    }
}
{spaced-vbox
    {CommandButton
        label = "Show renderer capabilities",
        {on Action do
            {popup-message
                message
            }
        }
    },
    {Renderer2dGraphic
        width = 4cm,
        height = 4cm,
        advanced-draw-operations? = true,
        repaint-handler = add-proc
    }
}

Renderer2d.create-offscreenからRenderer2dの取得

Renderer2d.create-offscreenメソッドは、2D レンダラーとレンダラーに関連したオフスクリーン描画面を返します。そのDrawableにレンダリングを行うことができ、結果をグラフィック オブジェクトとして表示することが出来ます。このメソッドは、advanced-draw-operations?引数をとり、描画サポートのレベルを決定します。
もしもDrawableが、Appletが存在する間ずっと使用されるのでなければ、必要がなくなったらすぐにDrawableを破棄することは重要です。Renderer2dGraphicRenderer3dGraphicSceneGraphicは、これを処理しますが、Drawableを直接生成した場合には、これが破棄されることを確実に行わなければなりません。Drawableは、Drawable.destroyを使用して破棄されます。オフスクリーンDrawableの生成と破棄を示す例は、3D レンダリングを参照してください。
以下のサンプルは、直前のサンプルと同じレンダリング操作を実行します。今回は、Renderer2dGraphicではなく、Renderer2d.create-offscreenを使用しています。

例: Renderer2d.create-offscreenの使用
{let g-width:Distance = 4cm}
{let g-height:Distance = 4cm}
{let (ren:Renderer2d, d:Drawable) =
    {Renderer2d.create-offscreen 
        g-width, 
        g-height, 
        advanced-draw-operations? = true,
        resolution = 100dpi
    }
}
{ren.clear fill-pattern = {FillPattern.get-maroon}}
{with-render-properties
    draw-operation = DrawOperation.add
    on ren do
    {ren.render-rectangle
        0.5cm, 0.5cm,
        3cm, 3cm,
        fill-pattern = 
            {url "curl://install/docs/default/images/adria.jpg"}
    }
} 
{Frame
    width = g-width,
    height = g-height,
    background = {d.to-Pixmap}
}
{d.destroy}