遮蔽処理の仕組み

Posted 2013.05.06 in 吉里吉里

前回の記事では遮蔽処理にまつわる不可思議な現象を再現してみたが、
この現象を回避する方法は、現象の複雑さに対して実に簡単だ。

不具合の回避

onTimer関数に次の記述を追加してみよう。


  var step = 0;
  function onTimer()
  {
    switch(step)
    {
    case 0:
      // 黒レイヤーを上に移動(不具合発生)
      layerBlack.absolute = 10;
      break;
    case 1:
      // 無意味にopacityを変更する(不具合回避)
      layerBlack.opacity = 0;
      layerBlack.opacity = 255;
      break;
    }
    step++;
  }

一見無意味に思える処理だが、実際この処理を行うと正しい動作結果になる。
この回避方法の意味を知るには、吉里吉里の遮蔽処理の仕組みを理解する必要がある。

遮蔽処理の仕組み

全てのレイヤーはそれぞれ個別に UpdateExcludeRect(更新遮断矩形) という情報を持つ。
UpdateExcludeRect は、そのレイヤーが他のレイヤーによって遮蔽されている領域を表す。

UpdateExcludeRect の情報は、レイヤーの再描画時に利用される。
レイヤーの再描画範囲が UpdateExcludeRect に被る時、
再描画範囲は UpdateExcludeRect にって分割され
UpdateExcludeRect に含まれる領域の再描画が省かれる。
まさしく「更新を遮断する矩形」なのである。
これによって遮蔽されていてどうせ見えない領域の描画の無駄が削減される。

重要なのは UpdateExcludeRect の情報が更新されるタイミングである。
UpdateExcludeRect は再描画のたびに毎回更新されるわけではない。
UpdateExcludeRect の更新は、
再描画時に VisualStateChanged フラグが立っている時だけ行われる。
VisualStateChanged フラグは、遮蔽状態に変化がある操作が行われた時に立てられる。
よって遮蔽状態に変化がなければ UpdateExcludeRect は更新されない。

回避方法の意味

先の不具合は、この UpdateExcludeRect の情報が正しく更新されないことによって起こる。
absolute の操作によって遮蔽状態が変化するにもかかわらず、
VisualStateChanged フラグが立てられていないからである。

この不具合を回避するには、立てられていない VisualStateChanged フラグを立ててやればよい。
opacity を操作すれば VisualStateChanged フラグが立てられるので、
opacity を無意味に変更すれば VisualStateChanged フラグを間接的に立てることができる。
これにより不具合を回避することができるのである。

よりスマートな回避方法

問題のあるプロパティをオーバーライドしておくと
プロパティを操作する度にいちいち気を遣う必要がなく便利だろう。
把握している限りでは order, type プロパティにも同様の問題があるので、そちらも対処しておく。


class OcclusionLayer extends Layer
{
  function OcclusionLayer(window, parent)
  {
    super.Layer(...);

    // 遮蔽するように設定
    type = ltOpaque;
  }

  // VisualStateChanged フラグを間接的に立てる
  function notifyVisualStateChanged()
  {
    var temp = opacity;
    opacity = !opacity;
    opacity = temp;
  }

  property absolute
  {
    setter(v){ super.absolute = v; notifyVisualStateChanged(); }
    getter(){ return super.absolute; }
  }

  property order
  {
    setter(v){ super.order = v; notifyVisualStateChanged(); }
    getter(){ return super.order; }
  }

  property type
  {
    setter(v){ super.type = v; notifyVisualStateChanged(); }
    getter(){ return super.type; }
  }
}

まとめ

実はこの absolute の問題は、わたなべごうさんに修正していただき
最新の開発版の吉里吉里では修正されている。
今回紹介した回避方法は、古いバージョンの吉里吉里を使っている場合に
この問題に遭遇した時の応急策として使えるだろう。


Leave a Reply

*