酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Windows Mobile(.NET Compact Framework)でグラデーション描画をおこなう

Windows Mobileの、さらに.NET Compact Frameworkを使って、グラデーションをするのには工夫が必要です。工夫って言ってもよくあるP/Invokeを使います。

グラデーションする為には、.NET Compact Frameworkからは使えませんので、ネイティブのGradientFill関数を使います。まずはGradientFillを使うためにP/Invokeする為に定義をします。

以下にサンプルコードを示します。

グラデーションを描画する

ネイティブ関数とやり取りする為の構造体を定義
public sealed class Win32
{
      public struct TRIVERTEX
      {
          public int x;
          public int y;
          public ushort Red;
          public ushort Green;
          public ushort Blue;
          public ushort Alpha;
          public TRIVERTEX(int x, int y, Color color)
              : this(x, y, color.R, color.G, color.B, color.A)
          {
          }
          public TRIVERTEX(
              int x, int y,
              ushort red, ushort green, ushort blue,
              ushort alpha)
          {
              this.x = x;
              this.y = y;
              this.Red = (ushort)(red << 8);
              this.Green = (ushort)(green << 8);
              this.Blue = (ushort)(blue << 8);
              this.Alpha = (ushort)(alpha << 8);
          }
      }
      public struct GRADIENT_RECT
      {
          public uint UpperLeft;
          public uint LowerRight;
          public GRADIENT_RECT(uint ul, uint lr)
          {
              this.UpperLeft = ul;
              this.LowerRight = lr;
          }
      }
      public struct GRADIENT_TRIANGLE
      {
          public uint Vertex1;
          public uint Vertex2;
          public uint Vertex3;
          public GRADIENT_TRIANGLE(uint v1, uint v2, uint v3)
          {
              this.Vertex1 = v1;
              this.Vertex2 = v2;
              this.Vertex3 = v3;
          }
      }

        [DllImport("coredll.dll", SetLastError = true, EntryPoint = "GradientFill")]
        public extern static bool GradientFill(
            IntPtr hdc,
            TRIVERTEX[] pVertex,
            uint dwNumVertex,
            GRADIENT_RECT[] pMesh,
            uint dwNumMesh,
            uint dwMode);

        public const int GRADIENT_FILL_RECT_H = 0x00000000;
        public const int GRADIENT_FILL_RECT_V = 0x00000001;

        // Not supported on Windows CE:
        public const int GRADIENT_FILL_TRIANGLE = 0x00000002;
}

次にP/InvokeしたGradientFill関数を使いやすくするヘルパーメソッドを作ります。ちなみにGRADIENT_FILL_TRIANGLEは、Windows CEでは使えません。縦方向と横方向限定。いい感じに組み合わせて頑張りましょう。

public sealed class GradientFill
{
    public static bool Fill(
        Graphics gr,
        Rectangle rc,
        Color startColor, Color endColor,
        FillDirection fillDir)
    {
        Win32.TRIVERTEX[] tva = new Win32.TRIVERTEX[2];
        tva[0] = new Win32.TRIVERTEX(rc.X, rc.Y, startColor);
        tva[1] = new Win32.TRIVERTEX(rc.Right, rc.Bottom, endColor);
        Win32.GRADIENT_RECT[] gra 
            = new Win32.GRADIENT_RECT[] { new Win32.GRADIENT_RECT(0, 1)};

        IntPtr hdc = gr.GetHdc();
        bool b;
        b = Win32.GradientFill(
                hdc,
                tva,
                (uint)tva.Length,
                gra,
                (uint)gra.Length,
                (uint)fillDir);
        System.Diagnostics.Debug.Assert(b, string.Format(
            "GradientFill failed: {0}",
            System.Runtime.InteropServices.Marshal.GetLastWin32Error()));
        gr.ReleaseHdc(hdc);
        return b;
    }
}

public enum FillDirection
{
    // 水平にグラデーションで塗りつぶします
    LeftToRight = Win32.GRADIENT_FILL_RECT_H,

    // 垂直にグラデーションで塗りつぶします
    TopToBottom = Win32.GRADIENT_FILL_RECT_V
}

20msとか短いピッチで再描画をかけるには、グラデーションは割と時間がかかるのでキャッシュした方が良さそう。

Expression Blendを使えば、グラデーションなんて一瞬なんだけどね。はやくMSはWindows Phone 7を日本で発売すべき。