none
お絵かきソフトのズームアップ機能を実現するには。 RRS feed

  • 質問

  • 質問させていただきます。

    VisualC#ExpressEdition2008でお絵かきソフトの様なものを作ろうと思っています。

    Photoshop等のソフトを使ってみると分かりやすいと思うのですが、画像を2倍にズームアップしたときは、その上から新たに線などを描画する場合に、4つ分のピクセルが1ピクセルとして扱われると思います。

    逆にいうと、幅が1ピクセルという指定で線を描いた場合は、2ピクセルの線が描画されるということです。

    ようするに描画する図形や線すべてが2倍の大きさで扱われれば嬉しいのですが。

    pictureBoxコントロールの上で、簡単にそのような機能を実現させることは可能でしょうか?

     

    わりと初心者なので何か思い違いをしているかもしれませんが、よろしくお願いします。

    2008年6月18日 11:06

回答

  • 線の感じが思われてるのと同じかどうかわかりませんが、

    私がやるならこんな感じです。

    かなり、てきとーですので、補完してください。イメージだけ掴んでください。

     

    Code Snippet

    public Form1()
            {
                InitializeComponent();
            }

            private Bitmap orgbmp;
            private Bitmap zoombmp;
            private int zoomratio;

            private void Form1_Load(object sender, EventArgs e)
            {
                orgbmp=new Bitmap(@"c:\test.jpg");
                zoomratio = 4;

                zoombmp = new Bitmap(orgbmp.Width * zoomratio, orgbmp.Height * zoomratio, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                Graphics g = Graphics.FromImage(zoombmp);
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

                g.DrawImage(orgbmp, new Rectangle(0, 0, zoombmp.Width, zoombmp.Height), new Rectangle(0, 0, orgbmp.Width, orgbmp.Height), GraphicsUnit.Pixel);
                g.Dispose();

                this.pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;

                this.pictureBox1.Image = zoombmp;

            }

            private void pictureBox1_Click(object sender, EventArgs e)
            {

            }

            private int startx;
            private int starty;
            private int endx;
            private int endy;

            private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
            {
                int tempx;
                int tempy;
               
                startx=System.Math.DivRem(e.X , zoomratio,out tempx);
                starty=System.Math.DivRem(e.Y, zoomratio, out tempy);

            }

            private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
            {
                int tempx;
                int tempy;
               
               
                endx=System.Math.DivRem(e.X, zoomratio,out tempx);
                endy=System.Math.DivRem(e.Y, zoomratio, out tempy);

                Graphics g = Graphics.FromImage(orgbmp);
                g.DrawLine(Pens.Red, startx, starty, endx, endy);
                g.Dispose();
                Graphics g2 = Graphics.FromImage(zoombmp);
                g2.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

                g2.DrawImage(orgbmp, new Rectangle(0, 0, zoombmp.Width, zoombmp.Height), new Rectangle(0, 0, orgbmp.Width, orgbmp.Height), GraphicsUnit.Pixel);
                g2.Dispose();
                this.pictureBox1.Image = zoombmp;
            }

     

     

    2008年6月18日 12:13
  • 外池です。作ろうとしている機能のイメージは良く理解できました。

    で、さらに細かく分けて考えると、次のような感じになるのではないかと思います。

    1. 原画のビットマップ画像そのものと、現ズーム画像が原画のどの部分なのかを示す座標の保持。
    2. マウスの位置を、原画の位置に変換する機能。
    3. ズーム画像を描画する機能。(Paintイベントで再描画する機能)
    4. マウスでズーム範囲を指示する機能。(マウスの位置情報をとりつつ、かつ、ズームした範囲を示す四角い枠を表示する)

    で、最後のご質問は、たぶん、4に関することだと思いますが? 私も似たようなプログラムを作ったことがありますが、概要は次のような感じです。

     

    MouseDownで、

    マウス・ドラッグのモードを示すFragを立てる。

    マウス・ドラッグの基点の座標を記録する。

     

    MouseMoveで、

    マウス・ドラッグのモードでなければなにもしない。

    現時点の座標を記録する。

    再描画を指示する。(描画面のFormなり、PictureBoxなりのInvalidateメソッドを呼び出す。この際、Invalidateする範囲を全面にせずに、基点と現時点の座標で囲われる四角形にしておけば、再描画のスピードアップが期待できる。)

     

    MouseUpで、

    マウス・ドラッグの終点の座標を記録する。

    マウス・ドラッグのモードを示すFragを下げる。

    基点と終点の座標を用いて、次のズーム画像を生成する処理を呼び出す。

     

    Paintイベントで、

    現ズーム画像を描画する。(マウス・ドラッグのモードでなければ、ここで終わり。)

    マウス・ドラッグのモードであれば、マウスドラッグの基点と現時点の座標で囲われる四角を描画する。

     

    ミソはですね・・・、Mouse関連のイベントの中で描画するのではなく、座標を記録するだけ。描画が必要な場合は、Paintイベントが起こるように(Paintイベントを呼び出すわけじゃないのも大切なミソ)、Invalidateを呼び出します。Refreshでも良いかもしれません。描画はすべてPaintイベントに書きます。

     

     

    2008年6月20日 1:44

すべての返信

  • 線の感じが思われてるのと同じかどうかわかりませんが、

    私がやるならこんな感じです。

    かなり、てきとーですので、補完してください。イメージだけ掴んでください。

     

    Code Snippet

    public Form1()
            {
                InitializeComponent();
            }

            private Bitmap orgbmp;
            private Bitmap zoombmp;
            private int zoomratio;

            private void Form1_Load(object sender, EventArgs e)
            {
                orgbmp=new Bitmap(@"c:\test.jpg");
                zoomratio = 4;

                zoombmp = new Bitmap(orgbmp.Width * zoomratio, orgbmp.Height * zoomratio, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                Graphics g = Graphics.FromImage(zoombmp);
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

                g.DrawImage(orgbmp, new Rectangle(0, 0, zoombmp.Width, zoombmp.Height), new Rectangle(0, 0, orgbmp.Width, orgbmp.Height), GraphicsUnit.Pixel);
                g.Dispose();

                this.pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;

                this.pictureBox1.Image = zoombmp;

            }

            private void pictureBox1_Click(object sender, EventArgs e)
            {

            }

            private int startx;
            private int starty;
            private int endx;
            private int endy;

            private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
            {
                int tempx;
                int tempy;
               
                startx=System.Math.DivRem(e.X , zoomratio,out tempx);
                starty=System.Math.DivRem(e.Y, zoomratio, out tempy);

            }

            private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
            {
                int tempx;
                int tempy;
               
               
                endx=System.Math.DivRem(e.X, zoomratio,out tempx);
                endy=System.Math.DivRem(e.Y, zoomratio, out tempy);

                Graphics g = Graphics.FromImage(orgbmp);
                g.DrawLine(Pens.Red, startx, starty, endx, endy);
                g.Dispose();
                Graphics g2 = Graphics.FromImage(zoombmp);
                g2.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

                g2.DrawImage(orgbmp, new Rectangle(0, 0, zoombmp.Width, zoombmp.Height), new Rectangle(0, 0, orgbmp.Width, orgbmp.Height), GraphicsUnit.Pixel);
                g2.Dispose();
                this.pictureBox1.Image = zoombmp;
            }

     

     

    2008年6月18日 12:13
  • 追記。

     

    二アレストネイバー法の描画と、DivRemで求めた座標がきっちり一致するかは、確認していません。

    ずれるかもしれません。

     

    さらに追記。

     

    単純に割り算して四捨五入で整数に丸めたらいいような気がしてきました。

    確認する気はないです。w

    2008年6月18日 12:27
  • ご返信ありがとうございます!

     

    確認できましたが、思った通りの機能でした。

    ありがとうございました。

    これは、orgbmpに線を描画してから、拡大して再度描画しているということでしょうか。

     

    線の描き方についてですが。

    やりたかったのはマウスを動かすだけで線が描画される方法です。

    よく参考書に書いてあるMouseMoveイベントの度に線を描画していくというものです。

    しかし、書いていただいたソースを変更してMouseMoveイベントで描画してみたのですが、この場合は、どうも処理に時間がかかるようで、スムーズに線が描かれませんでした。

     

    できれば、マウスを動かして線を描く場合にもこのような処理をしたいのですが、基本的には書いていただいた方法でやるしかないのでしょうか?

    よろしくお願いします。

    2008年6月18日 15:02
  • 外池と申します。ひとつ確認させてください。

     

    きょうたさんの仰る「お絵かき」は、ビットマップ画像ですか? ベクトル画像ですか? どちらにするかで、根本的にプログラムが異なってきます。

     

    ビットマップ画像とは、オリジナルのデータ(原寸サイズの画像データ)は、小さな画素、いわゆるピクセルの集合体です。デジカメの画像なんかは、この類です。例えば、ですが、横640ピクセル×縦480ピクセル、というような感じで、もし、「線」を描いたとしても、実際にはピクセルが連なっているだけです。このような画像をズームアップすることは、プログラムとしては簡単なのですが、決定的な欠陥があります。ズームアップすると、輪郭のガタガタが目立つようになります。ガタガタが目立たないようにすることもできますが、その場合は輪郭がボヤケます。「線」を描いたとしても、連なったピクセルとして描きおわったら、どこに線があるのか情報は失われます。Photoshopなどはこのタイプです。

     

    一方で、ベクトル画像とは、オリジナルのデータはすべて「座標」や「大きさ」で格納されています。線(直線)なら、両端の点の座標、線の太さです。円(円周)なら、中心の座標と半径、線の太さです。画像を表示するたびにこれらの線を結んで描画する作業をしなければなりません(そのようなプログラムを書く)。かなり大変ですが、利点があります。ズームアップしても輪郭が汚くなったりボヤけたりしません。Powerpointなどはこのタイプです。CADソフトも。「大変」と書きましたが、基本的な図形を描く機能は.Net Frameworkに備わっていますので・・・、なんとかなると思います。

     

    2008年6月18日 23:44
  •  きょうた さんからの引用

    線の描き方についてですが。

    やりたかったのはマウスを動かすだけで線が描画される方法です。

    よく参考書に書いてあるMouseMoveイベントの度に線を描画していくというものです。

    しかし、書いていただいたソースを変更してMouseMoveイベントで描画してみたのですが、この場合は、どうも処理に時間がかかるようで、スムーズに線が描かれませんでした。

     

    表示する部分のみを拡大するようにすれば早くはなるかもです。

    座標の計算はめんどくさくなりますけど。

    2008年6月19日 2:20
  •  

    外池さん

    お返事ありがとうございます!

     

    ビットマップでおねがいしたいと思います。

    理想を言うと、フォトショップのズームツールという虫眼鏡マークのツールの機能です。

    (フォトショップだと原寸大よりズームダウンすることもできるんですが、それは実現できなくてもけっこうです)

    あくまで、解像度の拡大・縮小という意味合いではなくて、作業中のズームアップ・ズームダウンの機能ということになります。

    機能的には、はなはなはなさんに書いていただいたものでOKなんですが、処理速度がちょっと遅かったので、どうすれば良いかと悩んでます。

    よろしくお願いします!

    2008年6月19日 2:25
  • はなはなはなさん

    線を描くとたぶん一回のマウスの動きで縦横10~40ピクセルとかそのぐらいは動くと思うんですが、その周りの四角形の座標をとって、その部分だけを描画するという方法でやるということでしょうか。

    複雑すぎて、自分でできるかとうかわかりませんが、挑戦してみたいと思います。

    素晴らしいヒントをありがとうございます!

    2008年6月19日 2:38
  • 外池です。作ろうとしている機能のイメージは良く理解できました。

    で、さらに細かく分けて考えると、次のような感じになるのではないかと思います。

    1. 原画のビットマップ画像そのものと、現ズーム画像が原画のどの部分なのかを示す座標の保持。
    2. マウスの位置を、原画の位置に変換する機能。
    3. ズーム画像を描画する機能。(Paintイベントで再描画する機能)
    4. マウスでズーム範囲を指示する機能。(マウスの位置情報をとりつつ、かつ、ズームした範囲を示す四角い枠を表示する)

    で、最後のご質問は、たぶん、4に関することだと思いますが? 私も似たようなプログラムを作ったことがありますが、概要は次のような感じです。

     

    MouseDownで、

    マウス・ドラッグのモードを示すFragを立てる。

    マウス・ドラッグの基点の座標を記録する。

     

    MouseMoveで、

    マウス・ドラッグのモードでなければなにもしない。

    現時点の座標を記録する。

    再描画を指示する。(描画面のFormなり、PictureBoxなりのInvalidateメソッドを呼び出す。この際、Invalidateする範囲を全面にせずに、基点と現時点の座標で囲われる四角形にしておけば、再描画のスピードアップが期待できる。)

     

    MouseUpで、

    マウス・ドラッグの終点の座標を記録する。

    マウス・ドラッグのモードを示すFragを下げる。

    基点と終点の座標を用いて、次のズーム画像を生成する処理を呼び出す。

     

    Paintイベントで、

    現ズーム画像を描画する。(マウス・ドラッグのモードでなければ、ここで終わり。)

    マウス・ドラッグのモードであれば、マウスドラッグの基点と現時点の座標で囲われる四角を描画する。

     

    ミソはですね・・・、Mouse関連のイベントの中で描画するのではなく、座標を記録するだけ。描画が必要な場合は、Paintイベントが起こるように(Paintイベントを呼び出すわけじゃないのも大切なミソ)、Invalidateを呼び出します。Refreshでも良いかもしれません。描画はすべてPaintイベントに書きます。

     

     

    2008年6月20日 1:44
  • 外池さんありがとうございます。

     

    説明していただいたことですが、これはズームアップの機能ということでしょうか。

    Invalidateは参考書なんかでも見たことがなかったので、やってみたいと思います。

     

    線の描画に関しては、はなはなはなさんに教えていただいた様に、画面の一部分だけを小さい画像でビットマップに描いてからその後に、その一部分だけを拡大するようにしてみてみました。

    しかし、どうしても数ピクセル場所がずれて描画される場合があります。

     

    これもゆっくり時間をかけて考えないとダメそうです。

    たぶん人に聞いても理解できないと思いますので、自分でじっくりやってみたいと思います。

     

    そんなわけで、ひとまずまた、自分でがんばってみようと思います。

    ありがとうございました!

    2008年6月20日 8:22
  • 質問見て、とっさに二アレストネイバーで楽できるかも、と思いましたが、

    自前で機能用意するほうが楽かもしれませんね。(かえってそのほうが位置あわせがしやすいかも)

    もうちょっと考えたほうがいい案でる可能性はあります。

    2008年6月20日 9:04
  • はなはなはなさんありがとうございました。

    私はGraphicsやBitmapなんかの機能として、1ピクセルの大きさを違うスケールで扱うことができるようなものがあるんじゃないかと思ってたのですが、そういう簡単な機能はないということなんですね。

    私の技術力ではまだ複雑なことをやるのは無理そうなので、もう少しいろいろ勉強したいと思います。

    2008年6月20日 11:30
  • こんにちは。中川俊輔 です。

     

    はなはなはなさん、外池さん、回答ありがとうございます。

     

    きょうたさん、フォーラムのご利用ありがとうございます。

    有用な情報と思われたため、はなはなはなさん、外池さんの回答へ回答済みチェックをつけさせていただきました。

     

    回答済みチェックが付くことにより、有用な情報を探している方が情報を見つけやすくなります。
    有用な情報と思われる回答があった場合は、なるべく回答済みボタンを押してチェックを付けてください。

    きょうたさんはチェックを解除することもできますので、ご確認ください。

     

    それでは!

    2008年7月4日 9:01