[SOLVED] How to move an image? lazarus / 39-Graphics ---------------------------------------------------------------------- avv210 · 2021-12-15 13:38 Hello folks, I am new to this forum. I have Digital Image Processing Course in my college. So I make a GUI Software for my assignment. I use TEdit as an input box (for X axis and Y axis). Then I want to process the input to make my image move to the coordinated value. As an example, I have an image size 100x177. Then I want to move the image to coordinated location, X = 10 and Y = 28. The translation formula is: x' = Tx + x --> for X coordinate y' = Ty + y --> for Y coordinate where, x' and y' is the new coordinate, Tx and Ty is an input value, x and y is the original coordinate. How can I achieve that? Source code: https://pastebin.com/xExgPZNX *Note: I apologize that I can't attach the project, because the file is too big for the forum to load. ---------------------------------------------------------------------- wp · 2021-12-15 13:57 Welcome here. It's your first post, so you are excused. But I would urge you to post code not on some cloud server somewhere in the internet where it will soon be gone making your post worthless for all future readers of your post. It would be much better if you'd pack .pas, .lfm, .lpr and .lpi files (and maybe the image file if it is loaded at runtime) into a single zip which you can upload here under "Attachments and other options". This way your project will always be attached to your post, and - very important - everybody can compile and test your project and can easily propose improvements, help with bugs etc. This is much easier with the help of the compiler than just by reading your code (which is even only half of the story because the property values stored in the lfm file are not available). The first question that I have is: Your code shows that you have a TImage component of the form. Do you want to move the image as a whole, i.e. keep the image contents unchanged and reposition the image relative to its container, the form? Or do you want to move some part of the image contents relative to the rest of the image? ---------------------------------------------------------------------- Handoko · 2021-12-15 14:42 Hello avv210. Because you didn't provide the 'full' source code so I cannot test it. A full Lazarus source code should include .lpi, .lpr, .lps files. Long ago, a fellow asked me to do image zooming and panning for a loaded picture. Luckily I still have the source code. It is tested only on Linux but I think it should work on Windows too. Maybe it can be useful for you: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Mainform; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,  ComCtrls, Buttons, StdCtrls; type   { TForm1 }   TForm1 = class(TForm)    btnOpen: TBitBtn;    chkCenter: TCheckBox;    lblYPos: TLabel;    lblZoom: TLabel;    lblXPos: TLabel;    OpenDialog1: TOpenDialog;    PageControl1: TPageControl;    PaintBox1: TPaintBox;    tabImage: TTabSheet;    tabData: TTabSheet;    Panel1: TPanel;    trbYPos: TTrackBar;    trbZoom: TTrackBar;    trbXPos: TTrackBar;    procedure btnOpenClick(Sender: TObject);    procedure chkCenterChange(Sender: TObject);    procedure PaintBox1Paint(Sender: TObject);    procedure PaintBoxUpdate(Sender: TObject);  private    { private declarations }  public    { public declarations }  end; var  Form1: TForm1; implementation {$R *.lfm} var  LoadedImage: TPicture = nil; { TForm1 } procedure TForm1.btnOpenClick(Sender: TObject);begin  if not(OpenDialog1.Execute) then Exit;   if (LoadedImage = nil) then LoadedImage := TPicture.Create;  LoadedImage.LoadFromFile(OpenDialog1.FileName);  trbZoom.Position := 100;  trbXPos.Position := 0;  trbYPos.Position := 0;  PaintBox1.Invalidate;end; procedure TForm1.chkCenterChange(Sender: TObject);begin  PaintBox1.Invalidate;end; procedure TForm1.PaintBox1Paint(Sender: TObject);var  ImagePos:  TRect;  Scale:   Real;  NewWidth:  Integer;  NewHeight: Integer;  X, Y:      Integer;begin   if not(LoadedImage = nil) then  begin    // Calculate zoom and size    Scale     := PaintBox1.Height / LoadedImage.Height;  NewHeight := Round(LoadedImage.Height * (trbZoom.Position/100) * Scale);    NewWidth  := Round(LoadedImage.Width * (trbZoom.Position/100) * Scale);    // Center image with screen viewport    X := (PaintBox1.Width-NewWidth) div 2;    Y := (PaintBox1.Height-NewHeight) div 2;    // Ofset the image position    X := X + Round(LoadedImage.Width*trbXPos.Position*Scale) div 100;    Y := Y + (PaintBox1.Height*trbYPos.Position) div 100;    // Show it    ImagePos.Left   := X;    ImagePos.Right  := ImagePos.Left + NewWidth;    ImagePos.Top    := Y;    ImagePos.Bottom := ImagePos.Top + NewHeight;    PaintBox1.Canvas.StretchDraw(ImagePos, LoadedImage.Graphic);  end;   // Draw center lines  if chkCenter.Checked then    with PaintBox1, PaintBox1.Canvas do    begin      X := Width div 2;      Y := Height div 2;      Pen.Color := clLime;      Line(X, 0, X, Height);      Line(0, Y, Width, Y);    end; end; procedure TForm1.PaintBoxUpdate(Sender: TObject);begin  PaintBox1.Invalidate;end; end. ---------------------------------------------------------------------- wp · 2021-12-15 16:30 Quote from: Handoko on December 15, 2021, 03:42:50 pmA full Lazarus source code should include .lpi, .lpr, .lps files. Sorry, please let me correct and complete this: The session file (.lps) can be omitted. What must be included, besides .lpr and .lpi, are all .pas and .lfm files, as well as any data files used by the program. Do not include files created by the compiler, such as .ppu, .o, .compiled, or - most of all - .exe because we do not need them for compilation, and because your upload will become too large. The easiest way to prepare a project for upload to the forum is to call menu "Project" > "Publish project" which creates a zip with all the files required. ---------------------------------------------------------------------- Handoko · 2021-12-15 16:34 Thank you for the correction. ---------------------------------------------------------------------- avv210 · 2021-12-17 11:24 Quote from: Handoko on December 15, 2021, 03:42:50 pmHello avv210. Because you didn't provide the 'full' source code so I cannot test it. A full Lazarus source code should include .lpi, .lpr, .lps files. Long ago, a fellow asked me to do image zooming and panning for a loaded picture. Luckily I still have the source code. It is tested only on Linux but I think it should work on Windows too. Maybe it can be useful for you: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Mainform; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,  ComCtrls, Buttons, StdCtrls; type   { TForm1 }   TForm1 = class(TForm)    btnOpen: TBitBtn;    chkCenter: TCheckBox;    lblYPos: TLabel;    lblZoom: TLabel;    lblXPos: TLabel;    OpenDialog1: TOpenDialog;    PageControl1: TPageControl;    PaintBox1: TPaintBox;    tabImage: TTabSheet;    tabData: TTabSheet;    Panel1: TPanel;    trbYPos: TTrackBar;    trbZoom: TTrackBar;    trbXPos: TTrackBar;    procedure btnOpenClick(Sender: TObject);    procedure chkCenterChange(Sender: TObject);    procedure PaintBox1Paint(Sender: TObject);    procedure PaintBoxUpdate(Sender: TObject);  private    { private declarations }  public    { public declarations }  end; var  Form1: TForm1; implementation {$R *.lfm} var  LoadedImage: TPicture = nil; { TForm1 } procedure TForm1.btnOpenClick(Sender: TObject);begin  if not(OpenDialog1.Execute) then Exit;   if (LoadedImage = nil) then LoadedImage := TPicture.Create;  LoadedImage.LoadFromFile(OpenDialog1.FileName);  trbZoom.Position := 100;  trbXPos.Position := 0;  trbYPos.Position := 0;  PaintBox1.Invalidate;end; procedure TForm1.chkCenterChange(Sender: TObject);begin  PaintBox1.Invalidate;end; procedure TForm1.PaintBox1Paint(Sender: TObject);var  ImagePos:  TRect;  Scale:   Real;  NewWidth:  Integer;  NewHeight: Integer;  X, Y:      Integer;begin   if not(LoadedImage = nil) then  begin    // Calculate zoom and size    Scale     := PaintBox1.Height / LoadedImage.Height;  NewHeight := Round(LoadedImage.Height * (trbZoom.Position/100) * Scale);    NewWidth  := Round(LoadedImage.Width * (trbZoom.Position/100) * Scale);    // Center image with screen viewport    X := (PaintBox1.Width-NewWidth) div 2;    Y := (PaintBox1.Height-NewHeight) div 2;    // Ofset the image position    X := X + Round(LoadedImage.Width*trbXPos.Position*Scale) div 100;    Y := Y + (PaintBox1.Height*trbYPos.Position) div 100;    // Show it    ImagePos.Left   := X;    ImagePos.Right  := ImagePos.Left + NewWidth;    ImagePos.Top    := Y;    ImagePos.Bottom := ImagePos.Top + NewHeight;    PaintBox1.Canvas.StretchDraw(ImagePos, LoadedImage.Graphic);  end;   // Draw center lines  if chkCenter.Checked then    with PaintBox1, PaintBox1.Canvas do    begin      X := Width div 2;      Y := Height div 2;      Pen.Color := clLime;      Line(X, 0, X, Height);      Line(0, Y, Width, Y);    end; end; procedure TForm1.PaintBoxUpdate(Sender: TObject);begin  PaintBox1.Invalidate;end; end. Hey! Uh..I am bit confuse to use your component, I try to recreate and understand your project. But I am so confused. Can you help me for an explanatory. It'll be helpful. Thank you by the way. ---------------------------------------------------------------------- Handoko · 2021-12-17 13:22 These are the main things in the example: 1. TPaintBox 2. TTrackBar 3. TOpenDialog TOpenDialog It is used to let user to select a file. To use it, you call its Execute method, see line #54. After user select a file, we load and store the image into LoadedImage, see line #56 and #57. And we set the default values for Zoom, X and Y, see lines #58..#60. The line #61 is needed because calling PaintBox.Invalidate will let the painbox to refresh its image. TTrackbar TTrackbar is used to let users to provide a value by using arrow keys and mouse. There are 3 trackbars used in the example, see lines #27..29. The starting values of those trackbars were set on the lines #58..60. When user modify any value of the trackbars, line #33 PaintBoxUpdate will be called because it was set in their OnChange event. The PaintBoxUpdate simply calls Invalidate, see line #113. TPainBox TPainBox is used for showing and modifying image on the screen. Usually we do it by accessing its canvas. In this case we use Canvas.StretchDraw, see line #95. OnPaint event is where you should put the code for showing the image, see line #69. Some calculations needed be done before really showing the image. First we calculate the size based on the zoom trackbar, see line #80. Line #85..86 are for centering the image, first the X then Y. And the calculate the offset, see line #87. After all the needed values have been calculated, now see show the image on the painbox, see line #90. ---------------------------------------------------------------------- Handoko · 2021-12-17 14:08 If you have problem understanding the previous example, try this. This one is simpler. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, ExtCtrls; type   { TForm1 }   TForm1 = class(TForm)    Image1: TImage;    btnLoad: TButton;    btnMove: TButton;    procedure btnMoveClick(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure btnLoadClick(Sender: TObject);  private    procedure ShowPosition;  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.btnLoadClick(Sender: TObject);begin   // Load the image  try  Image1.Picture.LoadFromFile('project1.ico');  except    Halt; // Quit the program if fails to load the image  end;   // Set the image position  Image1.Left := 50; // X  Image1.Top  := 50; // Y  ShowPosition;   // Notify the user  ShowMessage('Image loaded.');   // Set the buttons  btnLoad.Enabled := False;  btnMove.Enabled := True; end; procedure TForm1.ShowPosition;begin  Caption := 'X=' + Image1.Left.ToString + ' | Y=' + Image1.Top.ToString;end; procedure TForm1.FormCreate(Sender: TObject);begin  // Disable btnMove  btnMove.Enabled := False;end; procedure TForm1.btnMoveClick(Sender: TObject);var  strX, strY: string;  X, Y:       Integer;begin   // User input  if not(InputQuery('Move the image', 'X shift =', strX)) then Exit;  if not(InputQuery('Move the image', 'Y shift =', strY)) then Exit;   // Validation  if not(TryStrToInt(strX, X)) then  begin    ShowMessage('Invalid value for X.');    Exit;  end;  if not(TryStrToInt(strY, Y)) then  begin    ShowMessage('Invalid value for Y.');    Exit;  end;  if (X < -100) or (X > 100) then  begin    ShowMessage('-100 <= X <= 100.');    Exit;  end;  if (Y < -100) or (Y > 100) then  begin    ShowMessage('-100 <= Y <= 100.');    Exit;  end;   // Move the image  Image1.Left := Image1.Left + X;  Image1.Top  := Image1.Top  + Y;  ShowPosition; end; end. ---------------------------------------------------------------------- avv210 · 2021-12-18 12:18 Quote from: Handoko on December 17, 2021, 03:08:55 pmIf you have problem understanding the previous example, try this. This one is simpler. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, ExtCtrls; type   { TForm1 }   TForm1 = class(TForm)    Image1: TImage;    btnLoad: TButton;    btnMove: TButton;    procedure btnMoveClick(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure btnLoadClick(Sender: TObject);  private    procedure ShowPosition;  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.btnLoadClick(Sender: TObject);begin   // Load the image  try  Image1.Picture.LoadFromFile('project1.ico');  except    Halt; // Quit the program if fails to load the image  end;   // Set the image position  Image1.Left := 50; // X  Image1.Top  := 50; // Y  ShowPosition;   // Notify the user  ShowMessage('Image loaded.');   // Set the buttons  btnLoad.Enabled := False;  btnMove.Enabled := True; end; procedure TForm1.ShowPosition;begin  Caption := 'X=' + Image1.Left.ToString + ' | Y=' + Image1.Top.ToString;end; procedure TForm1.FormCreate(Sender: TObject);begin  // Disable btnMove  btnMove.Enabled := False;end; procedure TForm1.btnMoveClick(Sender: TObject);var  strX, strY: string;  X, Y:       Integer;begin   // User input  if not(InputQuery('Move the image', 'X shift =', strX)) then Exit;  if not(InputQuery('Move the image', 'Y shift =', strY)) then Exit;   // Validation  if not(TryStrToInt(strX, X)) then  begin    ShowMessage('Invalid value for X.');    Exit;  end;  if not(TryStrToInt(strY, Y)) then  begin    ShowMessage('Invalid value for Y.');    Exit;  end;  if (X < -100) or (X > 100) then  begin    ShowMessage('-100 <= X <= 100.');    Exit;  end;  if (Y < -100) or (Y > 100) then  begin    ShowMessage('-100 <= Y <= 100.');    Exit;  end;   // Move the image  Image1.Left := Image1.Left + X;  Image1.Top  := Image1.Top  + Y;  ShowPosition; end; end. Thank you