none
[C#]DrawRectangleの描画位置について RRS feed

  • 質問

  • お世話になっております。C#のDrawRectangleメソッドについて質問させていただきます。

    VitualStudio2005でアプリケーションを作成しています。

    C#でDrawRectangleメソッドを使用し四角を描画したのですがPenのAlignmentをInsetに指定した場合、Penのサイズが1と2以上では描画される四角のサイズが異なりました。

    (Penのサイズが1の場合、指定したサイズより1px大きく描画されています)

    調べてみたところ、とあるブログサイトでPneのサイズが1の場合は指定したサイズで表示され、サイズが2以上の場合は指定したサイズより1px小さいく描画するような情報を見つけました。しかし実際の動きは逆です。

    DrawRectangleのリファレンスをみても注意書き等には何も見つかりませんでした。

    どのように動くのが正しいのか、ご存知の方がいらっしゃいましたら教えていただければと思います。

    初歩的な質問になってしまい申し訳ありません。よろしくお願いいたします。


    • 編集済み naka_0229 2013年3月4日 8:38
    2013年3月4日 8:37

回答

  • 外池です。自身の環境(Windows 7、Visual Studio 2008のC#)でごちょごちょいじってみました。naka_0229さんの元のご質問の現象を再現できましたし、理由も説明できそうな気がします。

    Graphicsの座標原点は、ひとつのピクセルの大きさの中心に置かれるようです。

    この前提に立てば、DrawRectangleするとき、PenのAlignmentの設定によって見え方が変わってくる理由が説明できます。

    まず、PenのWidthが1.0、Alignmentがデフォルトのとき、整数で座標とサイズを指定してDrawRectangleすれば、ピクセルの幅のちょうど中心を通る線に沿って、両側0.5ピクセル分ずつ塗りつぶす、つまり、ひとつのピクセルだけを塗りつぶすので綺麗に描かれます。

    AlighmentをInsetにすると、ピクセルの幅のちょうど中心を通る線から、内側に1.0ピクセル分塗りつぶそうとするので、自身のピクセルの半分、隣接するピクセルの半分を塗ることになり・・・、さて、どうなる? 実装はどうなってるの? ということになります。

    GraphicsのSmoothingModeをAntiAliasにしてやると「ピクセルの半分を塗る」操作が、疑似的に、ぼやかして表現されますので、本来意図された結果に近い仕上がりになります。

    • 回答としてマーク naka_0229 2013年9月2日 7:35
    2013年7月4日 2:34

すべての返信

  • C#でDrawRectangleメソッドを使用し四角を描画したのですが

    Graphics.DrawRectangleメソッドですか?

    ソースコードがなければなんとも答えようがないと思いますが

    2013年4月10日 0:46
  • 返信が遅れて申し訳ありません。
    回答ありがとうございます。

    >Graphics.DrawRectangleメソッドですか?
    >ソースコードがなければなんとも答えようがないと思いますが

    大変失礼いたしました。Graphics.DrawRectangleメソッドになります。
    使用したメソッドはDrawRectangle(Pen pen, int x, int y, int width, int height)です。

    以下、実際に試したコードとなります。
    --------------------------------------------------------------------------
    Bitmap canvas = new Bitmap(300, 300);
    Graphics g = Graphics.FromImage(canvas);


    //①ペンのサイズ1で100×100の四角を描画
    //実際には101×101の四角が描画される
    Pen blackPen = new Pen(Color.Black, 1);
    blackPen.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
    g.DrawRectangle(blackPen, 10, 10, 100, 100);

    //②ペンのサイズ2で100×100の四角を描画
    //100×100の四角が描画される
    Pen blackPen2 = new Pen(Color.Pink, 2);
    blackPen2.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
    g.DrawRectangle(blackPen2, 150, 150, 100, 100);

    g.Dispose();
    pictureBox1.Image = canvas;
    --------------------------------------------------------------------------
    widthとheightの指定が同じにも関わらず、描画された四角のサイズが異なることに疑問を感じての質問です。

    DrawRectangleメソッドのバグかと思ったのですが、そういった情報もなく、

    かといって上記動きが仕様であるという情報も見つからず混乱してしまいました。

    よろしくお願いいたします。

    2013年7月2日 9:34
  • 外池と申します。直接の回答ではなく恐縮ですが、「そもそも」論で、ひとつ留意すべきと思われることを挙げておきます。

    Penのサイズの「1」と、Bitmapの「1」ピクセルと、単位が同じであると暗黙のうちに考えておられませんか? ここから見直すべきと思います。

    以下、余談で、あくまで、参考程度に。

    小生は、Graphicsを用いた描画は、float型の座標やサイズに基づいて、仮想的な平面に描かれるもの、とまず考えるようにしています。その上で、Bitmapのピクセルの集合体(座標やサイズはinteger型)にどのように割り当てられていくか? と考えます。

    後者の「割り当て」については、小生も、ドキュメントであまり見たことがありません。で、1ピクセル単位でのズレはしょーがない、自分でいろいろ試してみて経験則で対処するしかない、と諦めています。


    • 編集済み 外池 2013年7月3日 5:09 typo修正
    2013年7月3日 2:57
  • 小生は、Graphicsを用いた描画は、float型の座標やサイズに基づいて、仮想的な平面に描かれるもの、とまず考えるようにしています。その上で、Bitmapのピクセルの集合体(座標やサイズはinteger型)にどのように割り当てられていくか? と考えます。

    後者の「割り当て」については、小生も、ドキュメントであまり見たことがありません。で、1ピクセル単位でのズレはしょーがない、自分でいろいろ試してみて経験則で対処するしかない、と諦めています。

     Graphics クラスでは、PageUnit プロパティで、「1」と指定したときの「1」がどのようなものなのか、指定するのではないでしょうか。数年前、それがかみ合っていないことが原因で、@IT を炎上させてしまいましたが...

     「座標 0,0」から見たとき、座標 1, 0 は、長さ1なんですかね?それとも、2になるんですかね?(10, 10)から長さ(100, 100)と指定して、(111, 111) に引かれるのが“正解”だとすると、ペンの幅2で(150, 150)から長さ(100, 100)と指定したときに(250, 250)に引かれるのは、1小さくないですかね?


    Jitta@わんくま同盟

    2013年7月3日 12:47
  • 外池です。

    Jittaさんの仰ることと、小生の問題意識と、たぶん、起点は同じじゃないかと。仮に、Graphics上での長さ1.0と、Bitmap上の1ピクセル幅が一致しているものとしましょう(PageUnit指定)。でもなお、以下の疑問が残ります。

    Graphics上の座標「0.0, 0.0」は、Bitmap上の一番隅のピクセルの、その「中」の何処なのか? ピクセルは、小さいですが、「大きさ」があります。

    ピクセルの一番隅が「0.0, 0.0」なのか? ピクセルの中心部が「0.0, 0.0」なのか?

    幅1.0の線を描こうとしたとき、後者なら、分かりやすい動作・結果になると思いますが、前者だと「隣り合うピクセルに幅0.5ずつ被って線をひくことになるが、どっちのピクセルを塗る? 両方?」てな感じで気になり始め、結局「実装はどうなってるの?」という疑問になります。小生、ドキュメントで見たことが無い、と申し上げたのは、この部分です。

    もし、小生の勉強不足なら、ごめんなさい。

    1ピクセル単位の描画が気になったときだけの話です。最近のディスプレイはものすごく解像度が高いし、1ピクセル単位で線を引いたら細すぎ、斜めの線もアンチエイリアスで滑らかに描画、というような世界なので、少し太い線を引いとけ、細かい(ピクセル単位)ことは気にするな、なのかもしれません。


    • 編集済み 外池 2013年7月4日 1:12 表現の適正化
    2013年7月4日 1:10
  • 外池です。自身の環境(Windows 7、Visual Studio 2008のC#)でごちょごちょいじってみました。naka_0229さんの元のご質問の現象を再現できましたし、理由も説明できそうな気がします。

    Graphicsの座標原点は、ひとつのピクセルの大きさの中心に置かれるようです。

    この前提に立てば、DrawRectangleするとき、PenのAlignmentの設定によって見え方が変わってくる理由が説明できます。

    まず、PenのWidthが1.0、Alignmentがデフォルトのとき、整数で座標とサイズを指定してDrawRectangleすれば、ピクセルの幅のちょうど中心を通る線に沿って、両側0.5ピクセル分ずつ塗りつぶす、つまり、ひとつのピクセルだけを塗りつぶすので綺麗に描かれます。

    AlighmentをInsetにすると、ピクセルの幅のちょうど中心を通る線から、内側に1.0ピクセル分塗りつぶそうとするので、自身のピクセルの半分、隣接するピクセルの半分を塗ることになり・・・、さて、どうなる? 実装はどうなってるの? ということになります。

    GraphicsのSmoothingModeをAntiAliasにしてやると「ピクセルの半分を塗る」操作が、疑似的に、ぼやかして表現されますので、本来意図された結果に近い仕上がりになります。

    • 回答としてマーク naka_0229 2013年9月2日 7:35
    2013年7月4日 2:34
  • jittaさま 外池さま

    返信が遅れてしまい申し訳ありません。

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


    やはり実装がどうなってるのか…というところにたどりつきそうです。

    起点がどこなのか、調べてみましたが情報が出てきませんでした…(調べ方が甘いのでしょうか…)


    実装する際はPenのサイズ1にした場合のみ微調整をするか、

    アドバイス頂いたGraphicsのSmoothingModeをAntiAliasにしてみたいと思います。

    このたびはありがとうございました。大変勉強になりました。

    2013年8月23日 6:05