2011年4月12日火曜日

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

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

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





//
// ConicalGradient  円錐形グラデーションの描画クラス
//
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

using System.Threading.Tasks;

namespace DrawTest
{
    class ConicalGradient
    {
        
        public static Bitmap GetBitmap(Color[] colorList, int width, int height)
        {
            return GetBitmap(colorList, 0, width, height, width / 2, height / 2);
        }

        public static Bitmap GetBitmap(Color[] colorList, double d, int width, int height)
        {
            return GetBitmap(colorList, d, width, height, width / 2, height / 2);
        }

        public static Bitmap GetBitmap(Color[] colorList, double d, int width, int height, int centerX, int centerY)
        {
            try
            {
                //Color配列が空の時はHueの値でも入れとく
                if (colorList == null || colorList.Length == 0)
                {
                    colorList = new Color[6];
                    colorList[0] = Color.FromArgb(255, 0, 0);
                    colorList[1] = Color.FromArgb(255, 255, 0);
                    colorList[2] = Color.FromArgb(0, 255, 0);
                    colorList[3] = Color.FromArgb(0, 255, 255);
                    colorList[4] = Color.FromArgb(0, 0, 255);
                    colorList[5] = Color.FromArgb(255, 0, 255);
                }

                Color[] colArry;
                int colLength;

                //色と色の間隔は最大でも256色なので×配列の要素数分の色を作っとく
                colArry = new Color[colorList.Length * 256];
                colLength = colArry.Length;
                Parallel.For(0, colLength, delegate(int i)
                {
                //for (int i = 0; i < colLength; i++)
                //{
                    colArry[i] = GetColor(colorList, ((double)i / colLength) * 360.0);
                //}
                });


                Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
                int stride = bmpData.Stride;
                byte[] bmpByte = new byte[height * stride];

                const double rad = 180 / Math.PI;

                Parallel.For(0, width, delegate(int i)
                {
                //for (int i = 0; i < width; i++) 
                //{
                    for (int j = 0; j < height; j++)
                    {
                        double angle = (Math.Atan2(centerY - j, centerX - i) * rad) + 180 + d;
                        angle = (angle + 360) % 360;//0~359.99…の中に収める

                        Color col = colArry[(int)(angle * (colLength / 360.0))];

                        bmpByte[(i * 4) + (j * stride) + 3] = col.A;
                        bmpByte[(i * 4) + (j * stride) + 2] = col.R;
                        bmpByte[(i * 4) + (j * stride) + 1] = col.G;
                        bmpByte[(i * 4) + (j * stride) + 0] = col.B;
                    }
                //}
                });

                Marshal.Copy(bmpByte, 0, bmpData.Scan0, bmpByte.Length);
                bmp.UnlockBits(bmpData);

                return bmp;
            }
            catch
            {
                return null;
            }
        }

        /// 
        /// 色の取得
        /// 
        /// /// 0.0~259.99...        ///          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);         }     } }  
テストに作ってみたコントロール クリックで中心が移動してドラッグで回転する感じ。
using System.Drawing;
using System.Windows.Forms;
using System;
using System.Drawing.Drawing2D;
using System.Diagnostics;

namespace DrawTest
{
    class ConicalGradientView : Control
    {
        private Bitmap bmp;

        private double angle = 0;

        private Point center;
        public Point Center
        {
            get { return center; }
            set
            {
                center = value;
                Refresh();
            }
        }

        private Color[] colors = null;
        public Color[] Colors
        {
            get { return colors; }
            set
            {
                colors = value;
                Refresh();
            }
        }

        public ConicalGradientView()
        {
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
            Stopwatch sw = new Stopwatch();
            sw.Start();

            bmp = ConicalGradient.GetBitmap(colors, angle, this.ClientSize.Width, this.ClientSize.Height, Center.X, Center.Y);

            Console.WriteLine(sw.Elapsed);

            if (bmp != null)
            {
                pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                pe.Graphics.FillEllipse(new TextureBrush(bmp), 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);
                pe.Graphics.DrawEllipse(Pens.Black, 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);

                pe.Graphics.DrawEllipse(Pens.Black, Center.X - 5, Center.Y - 5, 10, 10);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            Center = e.Location;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.Button == MouseButtons.Left)
            {
                angle = (Math.Atan2(Center.X - e.X, Center.Y - e.Y) * (180 / Math.PI));

                Refresh();
            }
        }
    }
}

Conical Gradient

0 件のコメント:

コメントを投稿