2017年8月29日火曜日

Graphicsで変形後の位置を計算する

 C#では1つのBitmapに対して複数のGraphicsを作成できます。これを使うと、オフセットを個別に設定できるので、画像解析とかの結果を1枚の画像にまとめるのが楽ですが、画像1の点aから画像2の点Aに対して線を書く、といったことができません。ということで、Graphics.TransformからPointFを計算するようなメソッドを作ってみました。といっても単なる行列の計算ですが。



 0はGraphicsの変形で書き込んでおり、1はあらかじめ変形した位置を書き込んでいます。
 GraphicsのTransformはピクセル単位で計算されますが、自前の計算では位置しか計算できないので、線幅や図形の形状は計算されません。上図の比較はLineとEllipseしか使っていませんが、Rectangleとか使うともっと顕著にわかると思います。

 とりあえず今回の目的はGraphics1のaからGraphics2のAに対して直線を引きたいだけだったので、位置の計算だけで充分なので、目標は達成です。

 やろうと思えばピクセル単位での変形も可能でしょうが、そこまでするとグラフィックエンジンを自前で作ることになるので、相当に面倒なことになると思います。


 C#のImage, Bitmap, Graphicsはちょっとクセがありますが、そこに触れない程度の使い方ならかなり便利だと思います。
 でもGraphics.DrawImageUnscaledにImageを渡すとスケーリングされる?バグはどうにかならんのか。。。



using System;
using System.Drawing;

namespace GraphicsMatrixTransform
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int j = 0; j < 2; j++)
            {
                using (Bitmap bmp = new Bitmap(1000, 1000))
                using (Graphics g1 = Graphics.FromImage(bmp))
                using (Graphics g2 = Graphics.FromImage(bmp))
                {
                    g1.Clear(Color.Black);

                    g1.TranslateTransform(bmp.Width * 0.5f, bmp.Height * 0.5f);

                    Random rand = new Random(1234);

                    for (int i = 0; i < 100; i++)
                    {
                        switch (rand.Next(0, 3))
                        {
                        case 0:
                            {
                                float f = rand.Next(-45, 91);
                                g1.RotateTransform(f);
                                break;
                            }
                        case 1:
                            {
                                float x = rand.Next(-19, 20);
                                float y = rand.Next(-19, 20);
                                g1.TranslateTransform(x,y);
                                break;
                            }
                        case 2:
                            {
                                float x = rand.Next(6, 16) * 0.1f;
                                float y = rand.Next(6, 16) * 0.1f;
                                g1.ScaleTransform(x,y);
                                break;
                            }
                        }

                        using (Pen pen = new Pen(Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))))
                        {
                            switch (rand.Next(2))
                            {
                            case 0:
                                {
                                    PointF p1 = new PointF(rand.Next(-2000, 2000) * 0.1f, rand.Next(-2000, 2000) * 0.1f);
                                    PointF p2 = new PointF(rand.Next(-2000, 2000) * 0.1f, rand.Next(-2000, 2000) * 0.1f);

                                    if (j == 0)
                                    {
                                        g1.DrawLine(pen, p1, p2);
                                    }
                                    else
                                    {
                                        p1 = g1.Transform.Translate(p1);
                                        p2 = g1.Transform.Translate(p2);
                                        g2.DrawLine(pen, p1, p2);
                                    }

                                    break;
                                }
                            case 1:
                                {
                                    PointF p1 = new PointF(rand.Next(-1000, 1000) * 0.1f, rand.Next(-1000, 1000) * 0.1f);
                                    float r = rand.Next(100, 500) * 0.1f;

                                    if (j == 0)
                                    {
                                        g1.DrawEllipse(pen, p1.X - r, p1.Y - r, r * 2, r * 2);
                                    }
                                    else
                                    {
                                        p1 = g1.Transform.Translate(p1);
                                        g2.DrawEllipse(pen, p1.X - r, p1.Y - r, r * 2, r * 2);
                                    }

                                    break;
                                }
                            }
                        }
                    }

                    using (Font font = new Font("メイリオ", 24))
                    {
                        g2.DrawString(j.ToString(), font, Brushes.White, 0, 0);
                    }

                    bmp.Save("./log" + j + ".png");
                }
            }
        }
    }

    static class MyExt
    {
        public static PointF Translate(this System.Drawing.Drawing2D.Matrix mtx, PointF point)
        {
            return (new PointF(
                mtx.Elements[0] * point.X + mtx.Elements[2] * point.Y + mtx.Elements[4],
                mtx.Elements[1] * point.X + mtx.Elements[3] * point.Y + mtx.Elements[5]
                ));
        }
    }
}

0 件のコメント:

コメントを投稿