表示画像の中心を原点に画像の拡大・縮小表示するには?

解決


HMA  2010-12-22 01:54:43  No: 39746

ScrollBoxにImageを乗せ、下の例で「左上角を原点」に画像の拡大表示が出来ました。

この画像の拡大表示で、ScrollBoxの「表示中心を原点」に拡大・縮小をしたくて、
HorzScrollBar.PositionやVertScrollBar.PositionのPositionを変えてみたり、ImageのTopやLeftを変えたりしています。

画像がScrollBoxより大きい場合、小さい場合、ScrollBar.Positionを動かしスクロールした場合
・・・と考え試してみていますが全く思うようにはいきません。

ImageやScrollBoxのサイズ、HorzScrollBar.Position 、Range 等が関係した計算問題のような気が
しますが、正解がわからず困っています。
どなたか「表示中心を原点に拡大、縮小」のアドバイスをよろしくお願いします。

var
  Rate: Real;

procedure TForm1.Button2Click(Sender: TObject);
begin //拡大
  Rate := Rate + 0.2;
  if Rate > 2.0 then Rate := 2.0; //最大倍率の指定

  Image1.Width := Trunc(Image1.Picture.Width * Rate);
  Image1.Height := Trunc(Image1.Picture.Height * Rate);
end;


  2010-12-22 03:26:04  No: 39747

VertScrollBar.Positionなどをいじったら幸せになれるかも。


  2010-12-22 03:35:28  No: 39748

失礼。既にいじってたんですね。

1  マウスの位置を取得  (Mouse.CursorPos  →  TPoint型変数に保持)
2  Image1上の座標に変換  (Image1.ScreenToClientを利用)

ここまで来たら、後は単純に計算するだけかと。
Image1の大きさと、ScrollBoxのRangeの大きさが一緒だと分かりやすいと
思います。まずそれでやってみてはいかがでしょうか。


TS  2010-12-22 03:41:31  No: 39749

昔TrackBarを使って拡大、縮小をさせた時の抜粋です。
これだけで、役に立つかどうか疑問ですが、参考程度に。

  BMPWidth:=BMP.Width;
  BMPHeight:=BMP.Height;
  Form1.TrackBar1.Max:=BMPWidth;
  Form1.ScrollBox1.VertScrollBar.Position:=0;
  Form1.ScrollBox1.HorzScrollBar.Position:=0;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  Image1.SetBounds(0, 0,TrackBar1.Position
          ,Round(TrackBar1.Position /BMPWidth * BMPHeight));
end;


tor  2010-12-22 06:12:33  No: 39750

「表示中心を原点に拡大・縮小」というのが
「いまScrollBoxの中心に表示されている画像上の一点が
拡大・縮小後もScrollBoxの中心にいるようにしたい」ということだと解釈します。

話を簡単にするため、ここではX座標についてだけ考えます。
まず、ScrollBoxの中心の座標 SX = ScroolBox.ClientWidth / 2
この座標をImageの座標系に換算します。
PX = SX + ScrollBox.HorzScrollBar.Position - Image1.Left ... (1)

ここでImageをRate倍に拡大すると、Imageの座標系でPX にあった点は PX*Rate の位置に移動します。
PX' = PX * Rate
これをScrollBoxの座標系に変換すると
SX' = Image1.Left(変更後) + PX' - ScrollBox.HorzScrollBar.Position(変更後) ... (3)

拡大前後で中心座標が同じになるようにしたいわけですから
SX=SX' とおいて(1)と(3)を解いてやることになります。

拡大・縮小後の中心座標を同じにする方法としては「Imageの位置をずらす」と
「スクロールバーの位置をずらす」の2通りが考えられます。
前者の場合、変更前後でスクロールバーの位置が同じなので:
Image1.Left(変更後) = PX * (1 - Rate) + Image1.Left(変更前)
後者の場合、変更前後でImage1.Leftが同じなので:
ScrollBox.HorzScrollBar.Position(変更後) = PX * (Rate - 1) + ScrollBox.HorzScrollBar.Position(変更前)

もし拡大・縮小に合わせてスクロールバーの範囲も変えたいということだと、
Rangeもからんでくるのでさらにややこしい計算になりますけれど。


HMA  2010-12-22 19:29:14  No: 39751

'あ'さん、TSさん、torさん、早速ありがとうございます。

>これだけで、役に立つかどうか疑問ですが
こんなに早くヒントをいただける事は大変ありがたい。

'あ'さんが言われるように、「後は単純に計算するだけ」なのですが、
元々それぞれのプロパティの意味をよく理解していないために、こんがらかってしまいました。
それが、torさんの「後者の場合」ScrollBoxのScrollBar.Positionをどう動かしたら・・・
・・・の詳細解説付きの解答で、思いどおりの拡大・縮小ができました。

//スクロールバーの位置をずらす
PX := SX + ScrollBox1.HorzScrollBar.Position - Image1.Left;
ScrollBox1.HorzScrollBar.Position{変更後} := Trunc(PX * (Rate - 1)) + ScrollBox1.HorzScrollBar.Position{変更前};
PY := SY + ScrollBox1.HorzScrollBar.Position - Image1.Left;
ScrollBox1.HorzScrollBar.Position{変更後} := Trunc(PY * (Rate - 1)) + ScrollBox1.HorzScrollBar.Position{変更前};

最初に書いておけば良かったのですが、全体を表示するには縮小が必要な、
図面のような大きい画像の表示を想定していました。
小さい画像は、サイズの1/2をScrollBar.Position入れて、中心に表示しようと思います。

「Imageの位置をずらす方法」は、ScrollBoxより小さいサイズの画像を予め、
中央あたりに表示しておいた場合は、あまり問題を感じないのですが、
図面が大きいとImage1.Leftは大きなマイナスの値になってしまいます。

この状態で、ScrollBoxのScrollBar.Positionを動かすと左はじにある画像が表示されなくなり、
拡大の後、移動(Imageの位置をずらす)、縮小・・・等、繰り返すと終いには何も表示されなくなりました。

torさんが最後におっしゃる事はこの事でしょうか?
>Rangeもからんでくるのでさらにややこしい計算になりますけれど

この事の意味をもう少し教えていただけませんか?
よろしくお願いします。


tor  2010-12-22 21:10:31  No: 39752

HMAさんがやろうとしていることは
(1) 画像が表示領域より大きいとき、スクロールバーで画像を動かして好きな部分を見られる
(2) 画像が表示領域より小さいとき、スクロールバーで画像を領域内の好きな位置に移動できる
ということで合っているでしょうか?

そもそもTScrollBoxは「コンポーネント群が表示領域内に収まらないとき
スクロールして見られるようにする」機能を提供するものです。
つまり上記(1)に特化したものなので、(2)を実現するには不向きです。
(スクロールバーが正しい範囲で動くようにするにはMinとMaxを計算して設定しないといけませんが、
TScrollBoxだと最小値が0固定で最大値しかコントロールできないのがネックになります)
TScrollBarを縦・横にくっつけて自前でスクロールした方が、
細かいコントロールがきくので楽なのではないかと思います。

いずれにしても、領域内に表示されている内容からスクロール範囲を計算する必要があります。
で、念のために確認しますが、ScrollBoxの中にあるのはImageが一つだけですか?


HMA  2010-12-22 22:46:44  No: 39753

(1)・・・そのとおりです。
(2)・・・画像が表示領域より小さいときは、スクロールバーが出ないから、TScrollBoxの
中央に表示されれば良いと思っていて、「 画像が表示領域より小さいとき、スクロールバーで
画像を領域内の好きな位置に移動できる」ということは考えていませんでした。

でも、ScrollBoxより大きな画像の一部を段々に縮小し、ScrollBoxより小さくなると、
いきなり中央へ行くのも変かも知れません。
自分で思い付くことを一つ一つ試してみることは時間がかかり、容易なことではありません。
しかも大抵無駄になってしまいます。(^^ゞ

>ScrollBoxの中にあるのはImageが一つだけですか?
はい、一つだけです。

よろしくお願いします。

〔訂正〕
 PY := SY + ScrollBox1.VertScrollBar.Position - Image1.Top;
 ScrollBox1.HorzScrollBar.Position := Trunc(PY * (Rate - 1)) + ScrollBox1.VertScrollBar.Position;


HMA  2010-12-23 17:52:43  No: 39754

TScrollBoxより小さな画像と大きな画像、ScrollBar.Positionを動かした後、Image(Left,Top)を動かした後の拡大、縮小・・・と、当たり前ですがそれぞれの場面で計算が必要なようです。

>TScrollBoxだと最小値が0固定で最大値しかコントロールできない
TScrollBarだとマイナス値でも使えるんですね。

そうすると'TS'さんのTrackBarを使ったサンプルもそのまま使ってテストできそうです。
PanelにImageを載せ、TScrollBarでコントロールするサンプルも幾つかありました。
こちらも試してみたいと思います。

でも、時間がかかりそうですし、質問の拡大・縮小表示する方法は出来ましたので取りあえず、
解決とさせていただきます。
皆さん本当にありがとうございました。
チョットしたヒントでまた、前へ進めます。


KHE00221  2010-12-23 18:12:20  No: 39755

こーゆことかね?
type
  TForm2 = class(TForm)
    ScrollBox1: TScrollBox;
    Image1: TImage;
    Image2: TImage;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private 宣言 }
  public
    procedure ResizeImage;
  end;

var
  Form2: TForm2;
  V: Integer;

implementation

{$R *.dfm}

procedure TForm2.ResizeImage;
begin
    Image1.Width  := Image1.Picture.Width  * V;
    Image1.Height := Image1.Picture.Height * V;
    Image1.Left := ((Image2.Width  - Image1.Width ) div 2) - ScrollBox1.HorzScrollBar.Position;
    Image1.Top  := ((Image2.Height - Image1.Height) div 2) - ScrollBox1.VertScrollBar.Position;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
    V := V + 1;
    ResizeImage;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
    if V > 1 then V := V - 1;
    ResizeImage;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
    Image2.Left := 0;
    Image2.Top  := 0;
    Image2.Width := 6400;
    Image2.Height := 4000;
    V := 1;
end;

procedure TForm2.FormShow(Sender: TObject);
begin
    ScrollBox1.HorzScrollBar.Position := (Image2.Width div 2) - (Width div 2);
    ScrollBox1.VertScrollBar.Position := (Image2.Height div 2) - (Height div 2);
    ResizeImage;
end;

6


HMA  2010-12-23 20:47:20  No: 39756

KHE00221 さんいつもお世話になります。

{サンプルの利用前に}
Image1のPictureに画像を読んでおいて、
Image1とImage2は、両方ScrollBox1に貼り付けて良いでしょうか?

コピペして、TForm2をTForm1 に変えて動かしてみたところ、エラーなく動いたのですが・・・、
印刷して考えていますが、能力不足で申し訳ありません。


HMA  2010-12-23 21:38:44  No: 39757

>こーゆことかね?
そういう事でした。

すいません。よく見たら意味がわかりました。
Image1は元の大きさ、Image2は拡大後の大きさ、Vは拡大率でした。
上手く計算式が作れないのは、れぞれのプロパティの意味をよく理解していない事が問題のようです。


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

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






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