2009/1/16 1ライン毎のバイト数取得方法を修正しました。

修正した箇所は、紫字で示しています。


2009/1/9 UpdateTextureのコードの一部を修正しました。

修正した箇所は、青字で示しています。


昨日、動的テクスチャに画像データを設定したら、水平同期がずれたような画像になってしまったというお話しをしました。


そこで、画像幅が2のn乗でない場合、その幅以上である直近の2のn乗値の幅に合わせてライン毎にオフセット変更して画像データを設定してみることにしました。


そこで、テクスチャの1ライン毎のデータサイズを取得して、そのサイズに合わせてライン毎にオフセットを変更して画像データを設定してみることにしました。


という訳で、先日紹介したサンプルプログラムの仕様を


(1)画像データのARGBバイト配列を1次元の配列から、1ライン毎のジャグ配列に変更。


(2)テクスチャに画像データを設定する際に、1ライン毎の設定オフセットをLockRectangle メソッドのpitch 引数で取得した値にそろえて設定するように変更。


と、以上のように変更します。

この仕様で動作させた画像が以下の画像です。


なお、後の説明を判り易くするために、本来は透明である、スライドショースクリーンの余白を白く塗りつぶしています。



アニメとか好きだから-SSX動的テクスチャ1


うまく行きました。


念のために、スクリーンアスペクト比が1:1以外のケースも確認してみます。


まずは、4:3のケースです。


すると…



アニメとか好きだから-SSX動的テクスチャ2

画像のアスペクト比は4:3ですから、スクリーンに余白無しで表示されなければならないのに、上下に圧縮されて表示されてしまいます。


これはどういうことでしょうか?


スクリーンアスペクト比が1:1の画像には上下空白が存在しますが、この空白はスクリーンの中央に画像が配置されるようにプログラムで大本の画像データを作成する際に付加したものです。


対して、スクリーンアスペクト比が4:3の画像の場合は、スクリーンアスペクト比と画像アスペクト比が同じであるため、プログラムでは余白を設定していません。


つまり、2番目の画像の下部の余白は想定外のものなのです。


ここで2番目の画像を良く見てみましょう。


画像部分と余白部分の縦方向のサイズ比ですが、3:1になっています。


そう、もう、気づいた方もいらっしゃると思います。


つまり、テクスチャの内部形式のデータは、


(1)常に縦横のピクセル数が同じとなる。


(2)横方向のデータは、左端にそろえて設定される。


(3)縦方向のデータは、先頭行から末尾行までに伸展して設定される。


ということですね。


今回のプログラムでは、(3)の箇所についての対応が不足しているわけです。


とりあえず、今日は、原因の推理までです。


(3)の処理の追加は、明日の課題としましょう。



以下に、今回改造した、画像ジャグ配列データ作成メソッドと、動的テクスチャ更新メソッドのサンプルコードを掲載します。


======================== ここから ========================

// 画像ジャグ配列データ作成
public static byte[][] CreateBitmapBytes(Bitmap bmp)
{
  byte[][] bmpBytes = new byte[0][];
  BitmapData bmpData = null;
  try
  {
    bmpData = bmp.LockBits(
      new Rectangle(0, 0, bmp.Width, bmp.Height),
      ImageLockMode.ReadOnly,
      bmp.PixelFormat);

    IntPtr ptr = bmpData.Scan0;
    bmpBytes = new byte[bmpData.Height][];
    for (int i = 0; i < bmpData.Height; i++)
    {
      bmpBytes[i] = new byte[bmpData.Stride];
      System.Runtime.InteropServices.Marshal.Copy(
        (IntPtr)(ptr.ToInt64() + bmpData.Stride * i),
        bmpBytes[i], 0, bmpData.Stride);
    }
  }
  catch (Exception)
  {
    bmpBytes = new byte[0][];
  }
  finally
  {
    if (bmpData != null)
      bmp.UnlockBits(bmpData);
  }
  return bmpBytes;
}

// 動的テクスチャ更新
public static void UpdateTexture(
  Texture tex, byte[][] bmpBytes, Size size)
{
  if (tex != null && bmpBytes.Length > 0)
  {
    int textureStride = 0;
    using (GraphicsStream gs = tex.LockRectangle(
      0, LockFlags.Discard, out textureStride))
    {
      if (gs.CanWrite)
        try
        {
          int stride = bmpBytes[0].Length;
          int pixelSize = stride / size.Width;
          int textureStride =
            BinaryBoundary(size.Width) *
            pixelSize;
          for (int i = 0; i < bmpBytes.Length; i++)
          {
            int offset = i * textureStride;
            gs.Seek(
              offset,
              System.IO.SeekOrigin.Begin);
            gs.Write(bmpBytes[i], 0, stride);
          }
        }
        catch (Exception) { }
      tex.UnlockRectangle(0);
      gs.Close();
    }
  }
}

// 引数以上で直近の2の階乗値を求める
private static int BinaryBoundary(int value)
{
  int boundary = -1;
  try
  {
    for (int i = 0; boundary < value; i++)
    {
      boundary = 1 << i;
    }
  }
  catch (Exception)
  {
    boundary = -1;
  }
  return boundary;
}
======================== ここまで ========================