2011年4月12日火曜日

C# 円錐形グラデーション

円錐形のグラデーションの描画方法が分からなかったので、
書いてみた。

本当はもっと簡単な方法があるのかもしれない。





  1. //  
  2. // ConicalGradient  円錐形グラデーションの描画クラス  
  3. //  
  4. using System;  
  5. using System.Drawing;  
  6. using System.Drawing.Imaging;  
  7. using System.Runtime.InteropServices;  
  8.   
  9. using System.Threading.Tasks;  
  10.   
  11. namespace DrawTest  
  12. {  
  13.     class ConicalGradient  
  14.     {  
  15.           
  16.         public static Bitmap GetBitmap(Color[] colorList, int width, int height)  
  17.         {  
  18.             return GetBitmap(colorList, 0, width, height, width / 2, height / 2);  
  19.         }  
  20.   
  21.         public static Bitmap GetBitmap(Color[] colorList, double d, int width, int height)  
  22.         {  
  23.             return GetBitmap(colorList, d, width, height, width / 2, height / 2);  
  24.         }  
  25.   
  26.         public static Bitmap GetBitmap(Color[] colorList, double d, int width, int height, int centerX, int centerY)  
  27.         {  
  28.             try  
  29.             {  
  30.                 //Color配列が空の時はHueの値でも入れとく  
  31.                 if (colorList == null || colorList.Length == 0)  
  32.                 {  
  33.                     colorList = new Color[6];  
  34.                     colorList[0] = Color.FromArgb(255, 0, 0);  
  35.                     colorList[1] = Color.FromArgb(255, 255, 0);  
  36.                     colorList[2] = Color.FromArgb(0, 255, 0);  
  37.                     colorList[3] = Color.FromArgb(0, 255, 255);  
  38.                     colorList[4] = Color.FromArgb(0, 0, 255);  
  39.                     colorList[5] = Color.FromArgb(255, 0, 255);  
  40.                 }  
  41.   
  42.                 Color[] colArry;  
  43.                 int colLength;  
  44.   
  45.                 //色と色の間隔は最大でも256色なので×配列の要素数分の色を作っとく  
  46.                 colArry = new Color[colorList.Length * 256];  
  47.                 colLength = colArry.Length;  
  48.                 Parallel.For(0, colLength, delegate(int i)  
  49.                 {  
  50.                 //for (int i = 0; i < colLength; i++)  
  51.                 //{  
  52.                     colArry[i] = GetColor(colorList, ((double)i / colLength) * 360.0);  
  53.                 //}  
  54.                 });  
  55.   
  56.   
  57.                 Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);  
  58.                 BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);  
  59.                 int stride = bmpData.Stride;  
  60.                 byte[] bmpByte = new byte[height * stride];  
  61.   
  62.                 const double rad = 180 / Math.PI;  
  63.   
  64.                 Parallel.For(0, width, delegate(int i)  
  65.                 {  
  66.                 //for (int i = 0; i < width; i++)   
  67.                 //{  
  68.                     for (int j = 0; j < height; j++)  
  69.                     {  
  70.                         double angle = (Math.Atan2(centerY - j, centerX - i) * rad) + 180 + d;  
  71.                         angle = (angle + 360) % 360;//0~359.99…の中に収める  
  72.   
  73.                         Color col = colArry[(int)(angle * (colLength / 360.0))];  
  74.   
  75.                         bmpByte[(i * 4) + (j * stride) + 3] = col.A;  
  76.                         bmpByte[(i * 4) + (j * stride) + 2] = col.R;  
  77.                         bmpByte[(i * 4) + (j * stride) + 1] = col.G;  
  78.                         bmpByte[(i * 4) + (j * stride) + 0] = col.B;  
  79.                     }  
  80.                 //}  
  81.                 });  
  82.   
  83.                 Marshal.Copy(bmpByte, 0, bmpData.Scan0, bmpByte.Length);  
  84.                 bmp.UnlockBits(bmpData);  
  85.   
  86.                 return bmp;  
  87.             }  
  88.             catch  
  89.             {  
  90.                 return null;  
  91.             }  
  92.         }  
  93.   
  94.         /// <summary>  
  95.         /// 色の取得  
  96.         /// </summary>  
  97.         /// <param name="colors">/// <param name="angle">0.0~259.99...        /// <returns></returns>         private static Color GetColor(Color[] colors, double angle)         {             //色の初期化             int a, r, g, b,                 //色の数              length = colors.Length,               num = 0;              int d = (int)(angle / (360.0 / length));              double rest = (angle % (360.0 / length)) / (360.0 / length);               if (d + 1 < length) num = d + 1;              int startA = colors[d].A, endA = colors[num].A,                 startR = colors[d].R, endR = colors[num].R,                 startG = colors[d].G, endG = colors[num].G,                 startB = colors[d].B, endB = colors[num].B;              if (startA < endA)             {                 a = startA + (int)((endA - startA) * rest);             }             else             {                 a = startA - (int)((startA - endA) * rest);             }              if (startR < endR)             {                 r = startR + (int)((endR - startR) * rest);             }             else             {                 r = startR - (int)((startR - endR) * rest);             }              if (startG < endG)             {                 g = startG + (int)((endG - startG) * rest);             }             else             {                 g = startG - (int)((startG - endG) * rest);             }              if (startB < endB)             {                 b = startB + (int)((endB - startB) * rest);             }             else             {                 b = startB - (int)((startB - endB) * rest);             }              return Color.FromArgb(a, r, g, b);         }     } }    
テストに作ってみたコントロール クリックで中心が移動してドラッグで回転する感じ。
  1. using System.Drawing;  
  2. using System.Windows.Forms;  
  3. using System;  
  4. using System.Drawing.Drawing2D;  
  5. using System.Diagnostics;  
  6.   
  7. namespace DrawTest  
  8. {  
  9.     class ConicalGradientView : Control  
  10.     {  
  11.         private Bitmap bmp;  
  12.   
  13.         private double angle = 0;  
  14.   
  15.         private Point center;  
  16.         public Point Center  
  17.         {  
  18.             get { return center; }  
  19.             set  
  20.             {  
  21.                 center = value;  
  22.                 Refresh();  
  23.             }  
  24.         }  
  25.   
  26.         private Color[] colors = null;  
  27.         public Color[] Colors  
  28.         {  
  29.             get { return colors; }  
  30.             set  
  31.             {  
  32.                 colors = value;  
  33.                 Refresh();  
  34.             }  
  35.         }  
  36.   
  37.         public ConicalGradientView()  
  38.         {  
  39.             SetStyle(ControlStyles.UserPaint, true);  
  40.             SetStyle(ControlStyles.AllPaintingInWmPaint, true);  
  41.             SetStyle(ControlStyles.OptimizedDoubleBuffer, true);  
  42.   
  43.             SetStyle(ControlStyles.SupportsTransparentBackColor, true);  
  44.             SetStyle(ControlStyles.ResizeRedraw, true);  
  45.         }  
  46.   
  47.         protected override void OnPaint(PaintEventArgs pe)  
  48.         {  
  49.             base.OnPaint(pe);  
  50.             Stopwatch sw = new Stopwatch();  
  51.             sw.Start();  
  52.   
  53.             bmp = ConicalGradient.GetBitmap(colors, angle, this.ClientSize.Width, this.ClientSize.Height, Center.X, Center.Y);  
  54.   
  55.             Console.WriteLine(sw.Elapsed);  
  56.   
  57.             if (bmp != null)  
  58.             {  
  59.                 pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;  
  60.                 pe.Graphics.FillEllipse(new TextureBrush(bmp), 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);  
  61.                 pe.Graphics.DrawEllipse(Pens.Black, 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);  
  62.   
  63.                 pe.Graphics.DrawEllipse(Pens.Black, Center.X - 5, Center.Y - 5, 10, 10);  
  64.             }  
  65.         }  
  66.   
  67.         protected override void OnMouseDown(MouseEventArgs e)  
  68.         {  
  69.             base.OnMouseDown(e);  
  70.             Center = e.Location;  
  71.         }  
  72.   
  73.         protected override void OnMouseMove(MouseEventArgs e)  
  74.         {  
  75.             base.OnMouseMove(e);  
  76.             if (e.Button == MouseButtons.Left)  
  77.             {  
  78.                 angle = (Math.Atan2(Center.X - e.X, Center.Y - e.Y) * (180 / Math.PI));  
  79.   
  80.                 Refresh();  
  81.             }  
  82.         }  
  83.     }  
  84. }  

Conical Gradient

0 件のコメント:

コメントを投稿