Imageに描画する際のWidthに関して


UI  2012-05-31 00:18:08  No: 42393

Imageに描画する際のWidthの値に関して、あいまいな部分があるので教えてください。
以下サンプルです。

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  with Image1.Canvas do
  begin
    Pen.Width := 10;
    Image1.Canvas.Rectangle(-5, -5, Image1.width+5,Image1.height+5 );
     
    //MoveTo(Image1.width+5,0);
    //LineTo(Image1.width+5,Image1.Height);
 end;
end;

①Image1.Canvas.Rectangle(-5, -5, Image1.width+5,Image1.height+5 );
②LineTo(Image1.width+5,Image1.Height);
の比較で考えます。

Pen.Widthは10なのでその中心は5になります。
Imageに何も描画されない設定で描くことを考えます。

②の設定はImage1.width+5なので、直線はちょうど見えない位置に描画され、なにも見えません。
①も同じだと思いきや、Widthの部分に直線が幅1で描画されます。
Rectangleの場合は
Image1.Canvas.Rectangle(-5, -5, Image1.width+6,Image1.height+6 );
としないと同じ結果が得られません。

他は試していませんが、これはどういった要因からこうなるのでしょうか?


tor  2012-05-31 05:32:43  No: 42394

Windowsで四角形を描画する場合、右と下の端は除外されます。
ヘルプでFillRectをひくとそういう説明があると思いますが、Rectangleにも同じことがあてはまります。
これはDelphiに限ったことではなく、Windowsの一般的なルールです。

例えばImageのサイズが4×4だとしましょう。
(0,0)-(4,4)という矩形を指定して描画すると、次のような4×4の大きさの枠が描画されます。
この時、右の端は枠の内側、つまり3の位置にきます。下の端も同様です。

  01234
0■■■■|
1■□□■|
2■□□■|
3■■■■|

一方、(4,0)-(4,4)という座標を指定して線を引くと、これは指定どおり座標4に描かれます(上の図の縦棒の部分)。
これはImageの外なので見えなくなるわけです。

Windowsプログラミングでは、矩形の座標を次のように計算するよう定義しています。
Right = Left + Width
Bottom = Top + Height
しかし、方眼紙などに描いて考えてみるとわかると思いますが、本当は1を引かないと右と下の座標になりません。
描画の際に右と下が除外されるので、特に意識しなくても辻褄が合っているわけです。

もっとも、この仕様は別に計算を楽にするためではなく、スケーリングを正しく行うためにあります。
このあたりの事情は The Old New Thing で解説されていますね(英語の記事ですが)
http://blogs.msdn.com/b/oldnewthing/archive/2004/02/18/75652.aspx

蛇足ついでに、直線を引くときにも実は最後の1ピクセルは描画されません。
MoveTo(0,0);
LineTo(100, 0);
LineTo(100, 100);
とすると、最初の直線は手前の(99,0)で終わります。
しかし2本目の最初のピクセルが(100,0)になるので隙間はできません。
これはご想像の通り、同じピクセルを二重に描画するのを避けるための仕様です。


UI  2012-05-31 18:21:54  No: 42395

torさんありがとうございます。

(0,0)-(4,4)場合3の部分に線が引かれる。
LineToと引く場所は違うのはわかりました。

あと、PenのWidthが奇数と偶数の場合について

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  with Image1.Canvas do
  begin
    Pen.Width := 11;  ←ここを変更
    Image1.Canvas.Rectangle(-5, -5, Image1.width+5,Image1.height+5 );

    //MoveTo(Image1.width+5,0);
    //LineTo(Image1.width+5,Image1.Height);
 end;
end;

こうすると先ほどのPen.Width := 10のPen.Width := 11のときで右と下の描画が同じでした。※上と左はPen.Width=11の場合描画される

この結果から以下試しました。
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  with Image1.Canvas do
  begin
    Pen.Width := 11;
    Image1.Canvas.Rectangle(-5, -5, Image1.width-6,Image1.height-6 );

    //MoveTo(Image1.width+5,0);
    //LineTo(Image1.width+5,Image1.Height);
 end;
end;
この場合 Pen.Width := 10;とすると左と下は1の大きさの空白ができます。
 Pen.Width := 11;とすると空白なく埋まる。

□結論として
基本的に四角を描く場合は「Canvas.width-1」の部分に線の中心が引かれる。
ただ線が奇数の場合は線の中心より右側が1太くなる。

このことはWindowsのプログラミングの共通認識でしょうか?


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加