【C#】Bitmapの描画について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
からあげ

【C#】Bitmapの描画について

#1

投稿記事 by からあげ » 7年前

C#のBitmap描画について教えて欲しいです。

Bitmap上に4角形のオブジェクトを配置するマップエディタを作成しているのですが、
配置したオブジェクトの大きさや座標をNumericUpDownで連続で変更すると例外が発生します。
再描画の度にBitmapを新規作成していることが原因のようなので、
Bitmapを新規作成しないor描画間隔を少なくする といった方法は思いつくのですが、
どうすれば実装できるかがわかりません。どなたかアドバイスをお願いいたします。
例外が起こるのはCanvasクラスのdraw関数です。

コード:

    class Canvas
    {
        /** 四角形オブジェクトリスト */
        private List<RectangleObject> recList = null;

        private Bitmap orgCanvas = null;
        private Brush brushColor 
            = new SolidBrush(
                Color.FromArgb( Byte.MaxValue / 2 ,Color.White)
                );
        private bool selectFlag = false;
        private int selectElem = 0;

        private Canvas()
        {
        }

        public Canvas(PictureBox parentBox)
        {
            orgCanvas = new Bitmap(parentBox.Width, parentBox.Height );
            recList = new List<RectangleObject>();
        }

        /** クリック判定 */
        public void clickDecision(Point p)
        {
            int elemNum = recList.FindIndex(
                delegate (RectangleObject rec)
                {
                    return rec.clickDecision(p);
                });
            selectFlag = (elemNum != -1);
            selectElem = elemNum;
        }

        public void addRectangle( int id, Point point, Size size ,float angle = 0.0f)
        {
            selectFlag = true;
            selectElem = recList.Count;
            recList.Add( new RectangleObject( id,point,size,angle ) );
        }

        public void reposition( Point p )
        {
            if(selectFlag)
            {
                recList[selectElem].setPoint(p);
            }
        }

        public void resize(Size s)
        {
            if (selectFlag)
            {
                recList[selectElem].setSize(s);
            }
        }

        public void reangle(float angle)
        {
            if (selectFlag)
            {
                recList[selectElem].setAngle(angle);
            }
        }

        /** 問題箇所、コントロールで値が変更される度にこの処理が呼ばれ再描画される */
        public void draw( PictureBox pictureBox )
        {
            /** bitmap新規作成時にArgumentException */
            Bitmap canvas = new Bitmap(this.orgCanvas.Width, this.orgCanvas.Height);
            for (int i = 0; i < recList.Count; i++)
            {
                Pen FrameColor;
                bool isSelect = selectFlag;
                
                /** 選択中のオブジェクトはオレンジで描画 */
                isSelect &= ( selectElem == i );
                if ( isSelect ) {
                    FrameColor = Pens.Orange;
                } else
                {
                    FrameColor = Pens.Black;
                }
                recList[i].draw(FrameColor,brushColor,canvas);
            }
            
            pictureBox.Image = canvas;
        }

    }

コード:

    class RectangleObject
    {
        /** 中心座標 */
        private Point centerP;
        /** 識別id */
        private int id;
        /** 四角形 */
        private Rectangle rect;
        private Graphics graphics = null;

        /** 判定 */
        private const int radius = 10;
        private Size dicisionSize = new Size(radius, radius); 
        private Rectangle dicisionRect;

        private float angle;

        private RectangleObject()
        {
        }

        public RectangleObject( int objectId, Point point,Size size,float objectAngle = 0.0f)
        {
            id = objectId;
            centerP = point;
            angle = objectAngle;
            rect = createRect(point, size);
            dicisionRect = createRect(point, dicisionSize);
        }

        /** クリック判定 */
        public bool clickDecision(Point p)
        {
            double diffX = (double)( centerP.X - p.X );
            double diffY = (double)( centerP.Y - p.Y );
            double distance = Math.Sqrt( Math.Pow(diffX, 2.0) + Math.Pow(diffY, 2.0));
            return distance < ( radius / 2.0 );
        }

        public void setSize( Size size )
        {
            rect = createRect(centerP, size);
            dicisionRect = createRect(centerP, dicisionSize);
        }

        public void setAngle(float objectAngle)
        {
            angle = objectAngle;
        }

        public void setPoint( Point point )
        {
            centerP = point;
            Point rectP = culcUpperLeftPoint(centerP, rect.Size);
            rect.X = rectP.X;
            rect.Y = rectP.Y;
            Point dicP = culcUpperLeftPoint(centerP, dicisionRect.Size);
            dicisionRect.X = dicP.X;
            dicisionRect.Y = dicP.Y;

        }

        /** 描画 */
        public void draw(Pen frameColor,Brush fillColor,Bitmap canvas)
        {
            graphics = RotateGraphics(canvas, angle);
            graphics.FillRectangle(fillColor, rect);
            graphics.DrawRectangle(frameColor, rect);
            graphics.DrawEllipse(frameColor, dicisionRect);
        }

        private Graphics RotateGraphics(Bitmap canvas, float angle)
        {
            Graphics g = Graphics.FromImage(canvas);
            int x = centerP.X; int y = centerP.Y;

            g.TranslateTransform(-x, -y);
            g.RotateTransform(angle, System.Drawing.Drawing2D.MatrixOrder.Append);
            g.TranslateTransform(x, y, System.Drawing.Drawing2D.MatrixOrder.Append);

            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
            return g;
        }

        private Rectangle createRect(Point point, Size size)
        {
            Size s = new Size(size.Width * 2, size.Height * 2);
            Rectangle r = new Rectangle( culcUpperLeftPoint(centerP,s), s );
            return r;
        }

        private Point culcUpperLeftPoint( Point center , Size size )
        {
            Point point = new Point(center.X, center.Y);
            point.X -= size.Width / 2;
            point.Y -= size.Height / 2;
            return point;
        }

    }

Math

Re: 【C#】Bitmapの描画について

#2

投稿記事 by Math » 7年前

[PowerShell_ise Unixコマンド、Windows10の機能のみでテスト。]
・どうやってテストしたか全体像を示すこと。(画像をはりつける)
‣using文を省略しないこと。
・クラス設計図を示しDLL単体テスト用スクリプトをしめすこと。
・他人にみてもらうのだから分かりやすくする事。自分だけわかっても他人にはわからないような書き方はしない・
・付属の(ildasm.exe )で確認すること。
PoweShell_ise...??

コード:

csc /target:library .\RectangleObject.cs
ls Rec*
Add-Type -Path .\RectangleObject.dll
[RectangleObject]
$R=New-Object RectangleObject
csc /target:library /reference:RectangleObject.dll .\Canvas.cs
ls Can*
#Add-Type -Path ./Class.dll

コード:

using System;
using System.Drawing;
class RectangleObject
{
    /** 中心座標 */
    private Point centerP;
    /** 識別id */
    private int id;
    /** 四角形 */
    private Rectangle rect;
    private Graphics graphics = null;

    /** 判定 */
    private const int radius = 10;
    private Size dicisionSize = new Size(radius, radius);
    private Rectangle dicisionRect;

    private float angle;

    private RectangleObject()
    {
    }

    public RectangleObject(int objectId, Point point, Size size, float objectAngle = 0.0f)
    {
        id = objectId;
        centerP = point;
        angle = objectAngle;
        rect = createRect(point, size);
        dicisionRect = createRect(point, dicisionSize);
    }

    /** クリック判定 */
    public bool clickDecision(Point p)
    {
        double diffX = (double)(centerP.X - p.X);
        double diffY = (double)(centerP.Y - p.Y);
        double distance = Math.Sqrt(Math.Pow(diffX, 2.0) + Math.Pow(diffY, 2.0));
        return distance < (radius / 2.0);
    }

    public void setSize(Size size)
    {
        rect = createRect(centerP, size);
        dicisionRect = createRect(centerP, dicisionSize);
    }

    public void setAngle(float objectAngle)
    {
        angle = objectAngle;
    }

    public void setPoint(Point point)
    {
        centerP = point;
        Point rectP = culcUpperLeftPoint(centerP, rect.Size);
        rect.X = rectP.X;
        rect.Y = rectP.Y;
        Point dicP = culcUpperLeftPoint(centerP, dicisionRect.Size);
        dicisionRect.X = dicP.X;
        dicisionRect.Y = dicP.Y;

    }

    /** 描画 */
    public void draw(Pen frameColor, Brush fillColor, Bitmap canvas)
    {
        graphics = RotateGraphics(canvas, angle);
        graphics.FillRectangle(fillColor, rect);
        graphics.DrawRectangle(frameColor, rect);
        graphics.DrawEllipse(frameColor, dicisionRect);
    }

    private Graphics RotateGraphics(Bitmap canvas, float angle)
    {
        Graphics g = Graphics.FromImage(canvas);
        int x = centerP.X; int y = centerP.Y;

        g.TranslateTransform(-x, -y);
        g.RotateTransform(angle, System.Drawing.Drawing2D.MatrixOrder.Append);
        g.TranslateTransform(x, y, System.Drawing.Drawing2D.MatrixOrder.Append);

        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
        return g;
    }

    private Rectangle createRect(Point point, Size size)
    {
        Size s = new Size(size.Width * 2, size.Height * 2);
        Rectangle r = new Rectangle(culcUpperLeftPoint(centerP, s), s);
        return r;
    }

    priv
ate Point culcUpperLeftPoint(Point center, Size size)
    {
        Point point = new Point(center.X, center.Y);
        point.X -= size.Width / 2;
        point.Y -= size.Height / 2;
        return point;
    }

}

コード:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
class Canvas
{
    /** 四角形オブジェクトリスト */
    private List<RectangleObject> recList = null;

    private Bitmap orgCanvas = null;
    private Brush brushColor
        = new SolidBrush(
            Color.FromArgb(Byte.MaxValue / 2, Color.White)
            );
    private bool selectFlag = false;
    private int selectElem = 0;

    private Canvas()
    {
    }

    public Canvas(PictureBox parentBox)
    {
        orgCanvas = new Bitmap(parentBox.Width, parentBox.Height);
        recList = new List<RectangleObject>();
    }

    /** クリック判定 */
    public void clickDecision(Point p)
    {
        int elemNum = recList.FindIndex(
            delegate (RectangleObject rec)
            {
                return rec.clickDecision(p);
            });
        selectFlag = (elemNum != -1);
        selectElem = elemNum;
    }

    public void addRectangle(int id, Point point, Size size, float angle = 0.0f)
    {
        selectFlag = true;
        selectElem = recList.Count;
        recList.Add(new RectangleObject(id, point, size, angle));
    }

    public void reposition(Point p)
    {
        if (selectFlag)
        {
            recList[selectElem].setPoint(p);
        }
    }

    public void resize(Size s)
    {
        if (selectFlag)
        {
            recList[selectElem].setSize(s);
        }
    }

    public void reangle(float angle)
    {
        if (selectFlag)
        {
            recList[selectElem].setAngle(angle);
        }
    }

    /** 問題箇所、コントロールで値が変更される度にこの処理が呼ばれ再描画される */
    public void draw(PictureBox pictureBox)
    {
        /** bitmap新規作成時にArgumentException */
        Bitmap canvas = new Bitmap(this.orgCanvas.Width, this.orgCanvas.Height);
        for (int i = 0; i < recList.Count; i++)
        {
            Pen FrameColor;
            bool isSelect = selectFlag;

            /** 選択中のオブジェクトはオレンジで描画 */
            isSelect &= (selectElem == i);
            if (isSelect)
            {
                FrameColor = Pens.Orange;
            }
            else
            {
                FrameColor = Pens.Black;
            }
            recList[i].draw(FrameColor, brushColor, canvas);
        }

        pictureBox.Image = canvas;
    }

}

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: 【C#】Bitmapの描画について

#3

投稿記事 by YuO » 7年前

からあげ さんが書きました:Bitmap上に4角形のオブジェクトを配置するマップエディタを作成しているのですが、
配置したオブジェクトの大きさや座標をNumericUpDownで連続で変更すると例外が発生します。
再描画の度にBitmapを新規作成していることが原因のようなので、
Bitmapを新規作成しないor描画間隔を少なくする といった方法は思いつくのですが、
どうすれば実装できるかがわかりません。どなたかアドバイスをお願いいたします。
例外のメッセージには何と書いてあったのでしょうか。

ArgumentException自体は,例えばnew Bitmap(0, 100)のような値を指定した場合に発生します。
ただ,今回は固定値のはずなので,上記の理由で発生する事は考えにくいです。
例外が発生した時点での,Bitmapのコンストラクタに渡そうとしていた値はどうなっていたでしょうか。
あまりにも大きいか,0以下のような場合には例外が発生します。
オフトピック
例外が発生したらIDE側が止めてくれるはずですが……。
そして,その状態で値を見たりして,デバッグしていくものなのですが……。

とりあえず,最初に私が疑ったのはリソース不足でした。
しかし,リソース不足でArgumentExceptionが出るかどうかは確認できませんでした(new BitmapをDisposeせずに繰り返したらOutOfRangeExceptionが出た)。
ただ,pictureBox.Imageの設定前のオブジェクトは,今回の場合はdrawメソッド内でDisposeした方がよいでしょう。
オフトピック
通常は,自身がオブジェクトを所有していない可能性があるのでDisposeしない方がよいのですが,
おそらくdrawメソッドだけがImageにオブジェクトを設定する,という前提の元にこのように書いています。

Math

Re: 【C#】Bitmapの描画について

#4

投稿記事 by Math » 7年前

直前にVS2015CommunityでBuildは確認(okだった)してあるのでテスト用Mainコードがあるならそれでいいですよ。

からあげ

Re: 【C#】Bitmapの描画について

#5

投稿記事 by からあげ » 7年前

すみません情報不足でした。
ildasm.exeなんて物があるんですね、初めて知りました。
ソースも読みづらいですね、申し訳ないです。

例外の内容はこんな感じです。
追加情報:使用されたパラメーターが有効ではありません。
画像

とりあえず簡単なテストコードを作って検証してみましたが、
同じ現象は起こりませんでした。

コード:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace test1
{
    public partial class Form1 : Form
    {
        private Rectangle rect;
        private Bitmap orgCanvas = null;
        private Graphics graphic = null;
        private float angle;

        public Form1()
        {
            InitializeComponent();
            orgCanvas = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            angle = 0.0f;
            Point p = new Point(0, 0);
            Size s = new Size(10, 10);
            rect = new Rectangle( p, s );
            draw(pictureBox1);
        }

        /** 描画 */
        public void draw(PictureBox pictureBox)
        {
            Bitmap canvas = new Bitmap(orgCanvas.Width, orgCanvas.Height);
            graphic = RotateGraphics(canvas, angle);
            graphic.DrawRectangle(Pens.Black, rect);
            pictureBox.Image = canvas;
        }

        /** コントロールの値に合わせて回転 */
        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            angle = (float)numericUpDown1.Value;
            draw(pictureBox1);
        }

        /** 回転処理 */
        private Graphics RotateGraphics(Bitmap canvas, float angle)
        {
            Graphics g = Graphics.FromImage(canvas);
            int x = 5; int y = 5;

            g.TranslateTransform(-x, -y);
            g.RotateTransform(angle, System.Drawing.Drawing2D.MatrixOrder.Append);
            g.TranslateTransform(x, y, System.Drawing.Drawing2D.MatrixOrder.Append);

            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
            return g;
        }

    }
}

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace test1
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
元のソースのBitmapのインスタンス作成時の引数はおかしくないみたいなので別のことが原因なんですかね?
例外メッセージで調べてみたら、まだ調査の余地がありそうでした。
http://d.hatena.ne.jp/atsukanrock/20110421/1303381271

普段使わない言語だとコードもエラー調査も雑になるのは悪い癖ですね...
とりあえずもう少し自力で頑張ってみますので一旦締めさせてもらいます。
ご協力ありがとうございました。またわからなければお願いします。

からあげ

Re: 【C#】Bitmapの描画について

#6

投稿記事 by からあげ » 7年前

画像貼るの失敗してました...
画像

閉鎖

“C言語何でも質問掲示板” へ戻る