piledCopyと遮蔽

Posted 2013.05.06 in 吉里吉里

遮蔽処理にはさらに複雑な問題がある。

piledCopy によって起こる現象

遮蔽処理は Layer クラスの piledCopy 関数を実行した時にも行われる。
次の tjs スクリプトを実行してみて欲しい。


class TestWindow extends Window
{
  var layerGray;
  var layerBlack;
  var layerPiled;
  var timer;

  function TestWindow()
  {
    super.Window();
    setInnerSize(800, 600);

    // プライマリレイヤー
    add(new Layer(this, null));
    primaryLayer.setImageSize(innerWidth, innerHeight);
    primaryLayer.setSizeToImageSize();
    primaryLayer.fillRect(0, 0, innerWidth, innerHeight, 0xFFFFFFFF); // 白

    // 黒レイヤーを作る
    layerBlack = new Layer(this, primaryLayer);
    initLayer(layerBlack, 100, 100, 400, 400, 0xFF000000, 0, ltAlpha); // 黒

    // 黒レイヤーに紫の子レイヤーを作る
    var sub = new Layer(this, layerBlack);
    add(sub);
    initLayer(sub, 100, 100, 200, 200, 0xFFFF00FF, 0, ltAlpha); // 紫

    // タイマーを起動
    timer = new Timer(onTimer, "");
    timer.interval = 1000;
    timer.enabled = true;
  }

  function finalize()
  {
    invalidate layerGray;
    invalidate layerBlack;
    invalidate layerPiled;
    invalidate timer;

    super.finalize();
  }

  function initLayer(layer, x, y, w, h, color, absolute, type)
  {
    layer.left = x;
    layer.top = y;
    layer.setImageSize(w, h);
    layer.setSizeToImageSize();
    layer.fillRect(0, 0, w, h, color);
    layer.absolute = absolute;
    layer.type = type;
    layer.visible = true;
  }

  var step = 0;
  function onTimer()
  {
    switch(step)
    {
    case 0:
      // 灰色レイヤーを黒レイヤーの上に作る(遮蔽する)
      layerGray = new Layer(this, primaryLayer);
      initLayer(layerGray, 100, 100, 400, 400, 0xFF777777, 5, ltOpaque); // 灰
      break;

    case 1:
      // 黒レイヤーをpiledCopyしたレイヤーを一番上に作る
      layerPiled = new Layer(this, primaryLayer);
      initLayer(layerPiled, 100, 100, 400, 400, 0xFFFF0000, 100, ltAlpha);
      layerPiled.piledCopy(0, 0, layerBlack, 0, 0, 400, 400);
      break;
    }
    step++;
  }
}

var win = new TestWindow();
win.visible = true;

これを実行すると次のようになる。

KiriKiri_Exclude3_1

黒レイヤーを描画できていない!

本来次のようになるはずである。

KiriKiri_Exclude3_2

回避方法

この現象は遮蔽処理にまつわるものなのだが、
absolute の変更によって起きた問題とはまた原因が異なる。
UpdateExcludeRect の更新ができていないわけではない。
試しに次のように記述しても動作は何も変わらない。


    case 1:
      // 黒レイヤーをpiledCopyしたレイヤーを一番上に作る
      layerPiled = new Layer(this, primaryLayer);
      initLayer(layerPiled, 100, 100, 400, 400, 0xFFFF0000, 100, ltAlpha);

      layerBlack.opacity = 0;
      layerBlack.opacity = 255;
      layerPiled.piledCopy(0, 0, layerBlack, 0, 0, 400, 400);
      break;

この問題を回避するには次のようにする。


    case 1:
      // 黒レイヤーをpiledCopyしたレイヤーを一番上に作る
      layerPiled = new Layer(this, primaryLayer);
      initLayer(layerPiled, 100, 100, 400, 400, 0xFFFF0000, 100, ltAlpha);

      layerBlack.visible = false;
      layerPiled.piledCopy(0, 0, layerBlack, 0, 0, 400, 400);
      layerBlack.visible = true;
      break;

piledCopy 対象のレイヤーを不可視にしてから piledCopy するようにしている。
なぜこれで問題を回避できるのか…

原因

この問題の原因は、

遮蔽処理には常に全てのレイヤーが考慮される

という点にある。

黒レイヤーを piledCopy しているので、
遮蔽処理は黒レイヤー以下のレイヤーについてのみ考慮されるものと期待するだろうが
実際には黒レイヤーの兄弟レイヤーである灰色レイヤーも考慮される。

つまり灰色レイヤーによる遮蔽によって、
黒レイヤーの描画が省かれてしまったというわけだ。

回避方法の意味

では対象レイヤーを不可視にすると、なぜこの現象を回避できるのか。
この回避方法は

「レイヤーが遮蔽されるのは、
 レイヤーが可視の時だけである」

という条件を逆手に取ったものだ。

レイヤーを不可視にすることで、
レイヤーが他のレイヤーによって遮蔽されないようにできるのである。
対象レイヤーが不可視でも、piledCopy は実行できるので問題ない。

単純に、遮蔽している灰色レイヤーの方を不可視にしてもこの問題は回避できるが、
どのレイヤーが遮蔽しているのかわかりやすいケースばかりとは限らないので
対象を不可視にするというトリックは便利だろう。

まとめ

今回は遮蔽処理の条件を逆手にとって問題を回避したが
遮蔽処理が実施される条件は実際のところかなり複雑だ。

黒レイヤーの描画が省かれる一方で、その子レイヤーである紫レイヤーが
ちゃんと描画されている点について不思議に思ったかもしれない。
これもまた遮蔽処理の複雑な条件に理由がある。

詳しくは次回にでも~


Leave a Reply

*