Lazarus

Programming => Graphics and Multimedia => Graphics => Topic started by: Pe3s on December 13, 2024, 09:54:47 pm

Title: [SOLVED] SDL2 image rotation Problem
Post by: Pe3s on December 13, 2024, 09:54:47 pm
Hello forumers I am learning SDL and I have encountered a problem after loading the image format “portrait” rotation and fitting the image to the window margins appear at the top and bottom , when loading the image format landscape the problem does not occur how can I correct the error in the code ?
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8. Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, ExtCtrls,
  9.   StdCtrls, SDL2, SDL2_image, Math;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Button2: TButton;
  18.     Button3: TButton;
  19.     Button4: TButton;
  20.     Button5: TButton;
  21.     Button6: TButton;
  22.     Button7: TButton;
  23.     OpenDialog1: TOpenDialog;
  24.     Panel1: TPanel;
  25.     Panel2: TPanel;
  26.     StatusBar1: TStatusBar;
  27.     procedure Button1Click(Sender: TObject);
  28.     procedure Button2Click(Sender: TObject);
  29.     procedure Button3Click(Sender: TObject);
  30.     procedure Button4Click(Sender: TObject);
  31.     procedure Button5Click(Sender: TObject);
  32.     procedure Button6Click(Sender: TObject);
  33.     procedure Button7Click(Sender: TObject);
  34.     procedure FormDestroy(Sender: TObject);
  35.     procedure FormResize(Sender: TObject);
  36.     procedure FormShow(Sender: TObject);
  37.     procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  38.     procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  39.     procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  40.     procedure Panel1Paint(Sender: TObject);
  41.   private
  42.     SDLWindow: PSDL_Window;
  43.     SDLRenderer: PSDL_Renderer;
  44.     CurrentTexture: PSDL_Texture;
  45.  
  46.     OffsetX, OffsetY, ZoomLevel, MinZoom, MaxZoom, FRotationAngle: Double;
  47.     IsPanning: Boolean;
  48.     LastMouseX, LastMouseY, ImgWidth, ImgHeight: Integer;
  49.  
  50.     procedure InitializeSDL;
  51.     procedure ShutdownSDL;
  52.     procedure LoadAndDisplayImage(const FileName: string);
  53.     procedure RenderImage;
  54.     procedure CenterImage;
  55.     procedure Zoom(Delta: Double; FocusX, FocusY: Integer);
  56.     procedure FitImage;
  57.  
  58.   public
  59.  
  60.   end;
  61.  
  62. var
  63.   Form1: TForm1;
  64.  
  65. implementation
  66.  
  67. {$R *.lfm}
  68.  
  69. { TForm1 }
  70.  
  71. procedure TForm1.FormShow(Sender: TObject);
  72. begin
  73.    InitializeSDL;
  74.   ZoomLevel := 1.0;
  75.   OffsetX := 0;
  76.   OffsetY := 0;
  77.   IsPanning := False;
  78.  
  79.    ImgWidth := 0;  // No image loaded yet
  80.   ImgHeight := 0; // No image loaded yet
  81.   CurrentTexture := nil;
  82. end;
  83.  
  84. procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  85.   Shift: TShiftState; X, Y: Integer);
  86. begin
  87.   if Button = mbLeft then
  88.   begin
  89.     IsPanning := True;
  90.     LastMouseX := X;
  91.     LastMouseY := Y;
  92.   end;
  93. end;
  94.  
  95. procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  96. begin
  97.   if IsPanning then
  98.   begin
  99.     OffsetX := OffsetX + (X - LastMouseX);
  100.     OffsetY := OffsetY + (Y - LastMouseY);
  101.  
  102.     LastMouseX := X;
  103.     LastMouseY := Y;
  104.  
  105.     RenderImage;
  106.   end;
  107. end;
  108.  
  109. procedure TForm1.Panel1MouseUp(Sender: TObject; Button: TMouseButton;
  110.   Shift: TShiftState; X, Y: Integer);
  111. begin
  112.     if Button = mbLeft then
  113.     IsPanning := False;
  114. end;
  115.  
  116. procedure TForm1.Panel1Paint(Sender: TObject);
  117. begin
  118.    // Draw a red rectangle (example)
  119.   SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255);
  120.   SDL_RenderFillRect(sdlRenderer, nil); // Full screen fill
  121.   SDL_RenderPresent(sdlRenderer);
  122.  
  123.    RenderImage;
  124. end;
  125.  
  126. procedure TForm1.FormDestroy(Sender: TObject);
  127. begin
  128.   ShutdownSDL;
  129. end;
  130.  
  131. procedure TForm1.FormResize(Sender: TObject);
  132.  var
  133.   PanelWidth, PanelHeight: Integer;
  134. begin
  135.   PanelWidth := Panel1.Width;
  136.   PanelHeight := Panel1.Height;
  137.  
  138.   // Skip fitting or rendering if no image is loaded
  139.   if (ImgWidth = 0) or (ImgHeight = 0) then Exit;
  140.  
  141.   // Check for valid dimensions before fitting or rendering
  142.   if (PanelWidth > 0) and (PanelHeight > 0) then
  143.   begin
  144.     FitImage; // Automatically fit the image on resize
  145.   end;
  146. end;
  147.  
  148. procedure TForm1.Button1Click(Sender: TObject);
  149. begin
  150.    if OpenDialog1.Execute then
  151.     LoadAndDisplayImage(OpenDialog1.FileName);
  152. end;
  153.  
  154. procedure TForm1.Button2Click(Sender: TObject);
  155. begin
  156.  Zoom(1, Panel1.Width div 2, Panel1.Height div 2); // Zoom in towards the center
  157. end;
  158.  
  159. procedure TForm1.Button3Click(Sender: TObject);
  160. begin
  161.    Zoom(-1, Panel1.Width div 2, Panel1.Height div 2); // Zoom out towards the cente
  162. end;
  163.  
  164. procedure TForm1.Button4Click(Sender: TObject);
  165. begin
  166.   ZoomLevel:= 1.0;
  167.   CenterImage;
  168.   RenderImage;
  169. end;
  170.  
  171. procedure TForm1.Button5Click(Sender: TObject);
  172. begin
  173.      FitImage; // Reset zoom and center the image
  174. end;
  175.  
  176. procedure TForm1.Button6Click(Sender: TObject);
  177. begin
  178.   FRotationAngle := (FRotationAngle -90) mod 360;
  179.   RenderImage;
  180. end;
  181.  
  182. procedure TForm1.Button7Click(Sender: TObject);
  183. begin
  184.   FRotationAngle := (FRotationAngle +90) mod 360;
  185.   RenderImage;
  186. end;
  187.  
  188. procedure TForm1.InitializeSDL;
  189. begin
  190.   if SDL_Init(SDL_INIT_VIDEO) < 0 then
  191.     raise Exception.Create('Nie udało się zainicjować SDL: ' + SDL_GetError);
  192.  
  193.   SDLWindow := SDL_CreateWindowFrom(Pointer(Panel1.Handle));
  194.   if SDLWindow = nil then
  195.     raise Exception.Create('Nie udało się utworzyć okna SDL: ' + SDL_GetError);
  196.  
  197.   SDLRenderer := SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED or SDL_RENDERER_PRESENTVSYNC);
  198.   if SDLRenderer = nil then
  199.     raise Exception.Create('Nie można utworzyć renderowania SDL: ' + SDL_GetError);
  200.  
  201.   ZoomLevel := 1.0; // Initialize zoom level
  202.  
  203. end;
  204.  
  205. procedure TForm1.ShutdownSDL;
  206. begin
  207.    if CurrentTexture <> nil then
  208.     SDL_DestroyTexture(CurrentTexture);
  209.   if SDLRenderer <> nil then
  210.     SDL_DestroyRenderer(SDLRenderer);
  211.   if SDLWindow <> nil then
  212.     SDL_DestroyWindow(SDLWindow);
  213.   IMG_Quit;
  214.   SDL_Quit;
  215. end;
  216.  
  217. procedure TForm1.LoadAndDisplayImage(const FileName: string);
  218.   var
  219.   PanelWidth, PanelHeight: Integer;
  220.   FitZoom: Double;
  221. begin
  222.   if CurrentTexture <> nil then
  223.     SDL_DestroyTexture(CurrentTexture);
  224.  
  225.   CurrentTexture := IMG_LoadTexture(SDLRenderer, PChar(FileName));
  226.   if CurrentTexture = nil then
  227.     raise Exception.Create('Nie udało się wczytać tekstury: ' + IMG_GetError);
  228.  
  229.   // Query the texture for its dimensions
  230.   SDL_QueryTexture(CurrentTexture, nil, nil, @ImgWidth, @ImgHeight);
  231.  
  232.  
  233.   // Calculate initial zoom to fit the image in the panel
  234.   PanelWidth := Panel1.Width;
  235.   PanelHeight := Panel1.Height;
  236.   FitZoom := Min(PanelWidth / ImgWidth, PanelHeight / ImgHeight);
  237.  
  238.   // Set zoom level and center offsets
  239.   ZoomLevel := FitZoom;
  240.   OffsetX := (PanelWidth - Round(ImgWidth * ZoomLevel)) div 2;
  241.   OffsetY := (PanelHeight - Round(ImgHeight * ZoomLevel)) div 2;
  242.  
  243.   // Set zoom limits
  244.   MinZoom := FitZoom * 0.5; // Allow zooming out to 50% of fit size
  245.   MaxZoom := 3.0; // Allow zooming in up to 300%
  246.  
  247.   RenderImage;
  248.   FitImage;
  249. end;
  250.  
  251. procedure TForm1.RenderImage;
  252.   var
  253.     DstRect: TSDL_Rect;
  254.     PanelWidth, PanelHeight: Integer;
  255.   begin
  256.     if CurrentTexture = nil then Exit; // No texture to render
  257.  
  258.     PanelWidth := Panel1.Width;
  259.     PanelHeight := Panel1.Height;
  260.  
  261.     if (PanelWidth <= 0) or (PanelHeight <= 0) then Exit; // Avoid rendering if panel is invalid
  262.  
  263.     // Calculate destination rectangle with zoom and offsets
  264.     DstRect.w := Round(ImgWidth * ZoomLevel);
  265.     DstRect.h := Round(ImgHeight * ZoomLevel);
  266.  
  267.     // Constrain panning to avoid blank spaces
  268.     if DstRect.w <= PanelWidth then
  269.       OffsetX := (PanelWidth - DstRect.w) / 2
  270.     else
  271.       OffsetX := Max(Min(OffsetX, 0), PanelWidth - DstRect.w);
  272.  
  273.     if DstRect.h <= PanelHeight then
  274.       OffsetY := (PanelHeight - DstRect.h) / 2
  275.     else
  276.       OffsetY := Max(Min(OffsetY, 0), PanelHeight - DstRect.h);
  277.  
  278.     DstRect.x := Round(OffsetX);
  279.     DstRect.y := Round(OffsetY);
  280.  
  281.     SDL_RenderClear(SDLRenderer);
  282.  
  283.     // Render the image
  284.     SDL_RenderCopyEx(SDLRenderer, CurrentTexture, nil, @DstRect, FRotationAngle, nil, SDL_FLIP_NONE);
  285.     SDL_RenderPresent(SDLRenderer);
  286. end;
  287.  
  288. procedure TForm1.CenterImage;
  289.  var
  290.   PanelWidth, PanelHeight: Integer;
  291. begin
  292.   if (ImgWidth = 0) or (ImgHeight = 0) or (CurrentTexture = nil) then Exit;
  293.  
  294.   PanelWidth := Panel1.Width;
  295.   PanelHeight := Panel1.Height;
  296.  
  297.   // Reset offsets to center the image
  298.   OffsetX := (PanelWidth - ImgWidth * ZoomLevel) / 2;
  299.   OffsetY := (PanelHeight - ImgHeight * ZoomLevel) / 2;
  300.  
  301.   RenderImage;
  302. end;
  303.  
  304. procedure TForm1.Zoom(Delta: Double; FocusX, FocusY: Integer);
  305.  var
  306.   OldZoom: Double;
  307.   PanelWidth, PanelHeight: Integer;
  308.   ImageCenterX, ImageCenterY: Double;
  309. begin
  310.   if (ImgWidth = 0) or (ImgHeight = 0) or (CurrentTexture = nil) then Exit;
  311.  
  312.   OldZoom := ZoomLevel;
  313.   PanelWidth := Panel1.Width;
  314.   PanelHeight := Panel1.Height;
  315.  
  316.   // Adjust zoom level with bounds
  317.   ZoomLevel := Max(MinZoom, Min(MaxZoom, ZoomLevel + Delta * 0.1));
  318.  
  319.   // Compute focus point scaling
  320.   if ZoomLevel <> OldZoom then
  321.   begin
  322.     ImageCenterX := (FocusX - OffsetX) / OldZoom;
  323.     ImageCenterY := (FocusY - OffsetY) / OldZoom;
  324.  
  325.     OffsetX := FocusX - ImageCenterX * ZoomLevel;
  326.     OffsetY := FocusY - ImageCenterY * ZoomLevel;
  327.   end;
  328.  
  329.   // Update caption
  330.   Form1.Caption := Format('Zoom: %.0f%%', [ZoomLevel * 100]);
  331.  
  332.   RenderImage;
  333. end;
  334.  
  335. procedure TForm1.FitImage;
  336.  var
  337.   RotatedWidth, RotatedHeight: Double;
  338.   ScalingFactor: Double;
  339.   AngleRadians: Double;
  340. begin
  341.   if (ImgWidth = 0) or (ImgHeight = 0) or (CurrentTexture = nil) then Exit;
  342.  
  343.   // Normalize the rotation angle to the range [0, 360)
  344.   AngleRadians := DegToRad((FRotationAngle mod 360 + 360) mod 360);
  345.  
  346.   // Calculate rotated bounding box dimensions
  347.   RotatedWidth := Abs(ImgWidth * Cos(AngleRadians)) + Abs(ImgHeight * Sin(AngleRadians));
  348.   RotatedHeight := Abs(ImgWidth * Sin(AngleRadians)) + Abs(ImgHeight * Cos(AngleRadians));
  349.  
  350.   // Calculate scaling factor to fit the panel's height
  351.   ScalingFactor := Panel1.Height / RotatedHeight;
  352.  
  353.   // Update the zoom level to maintain proportional scaling
  354.   ZoomLevel := ScalingFactor;
  355.  
  356.   // Recalculate offsets to ensure proper centering
  357.   CenterImage;
  358. end;
  359.  
  360. end.
  361.  
  362.  
  363.  
Greetings
Title: Re: SDL2 Fit image rotation bug
Post by: bobihot on December 13, 2024, 10:31:27 pm
Try with
Lazarus-SDL3.0-Packages_and_Examples

https://github.com/sechshelme/Lazarus-SDL3.0-Packages_and_Examples

Me also interested, because I was used on Delphi. But I do not try now.
Thanks
Title: Re: SDL2 Fit image rotation bug
Post by: Pe3s on December 14, 2024, 07:18:57 pm
This is not an SDL bug rather my code.
The loaded image in landscape aspect ratio works , but when I load a portrait there is a problem when rotating.
Title: Re: SDL2 Fit image rotation bug
Post by: Pe3s on December 18, 2024, 12:08:48 pm
How can I dynamically swap the height with the width and update the sdl window ?
So that the rotated image will adjust to the window
Title: Re: SDL2 Fit image rotation bug
Post by: Roland57 on December 19, 2024, 02:21:28 am
Hello!

By the way, I can compile your project, but I get this error when I try to run it:

[roland@localhost sdl_image]$ ./project1
The program 'project1' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadWindow (invalid Window parameter)'.
  (Details: serial 88 error_code 3 request_code 20 minor_code 0)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the --sync command line
   option to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)


I took a look at your code. The logic is too complicated for me.  :)
Title: Re: SDL2 Fit image rotation bug
Post by: Pe3s on December 19, 2024, 02:30:31 pm
Under Windows it compiles and runs correctly
Title: Re: SDL2 Fit image rotation bug
Post by: Fred vS on December 19, 2024, 03:15:56 pm
Hello!

By the way, I can compile your project, but I get this error when I try to run it:

[roland@localhost sdl_image]$ ./project1
The program 'project1' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadWindow (invalid Window parameter)'.
  (Details: serial 88 error_code 3 request_code 20 minor_code 0)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the --sync command line
   option to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)


I took a look at your code. The logic is too complicated for me.  :)

Hello Roland.

On Linux, maybe you can try with msegui:
https://github.com/mse-org/mseide-msegui/discussions/94

 ;)

Fre;D
Title: Re: SDL2 image rotation
Post by: Pe3s on December 30, 2024, 07:40:26 am
Below I present a visualization of image rotation as SDL performs it.
The problem that I can't solve is this image rotation with swapping image dimensions.
Title: Re: SDL2 image rotation Problem
Post by: Pe3s on January 03, 2025, 07:52:38 am
Below are two images the first loaded not rotated and the second rotated with incorrect proportions which I am trying to fix
Title: Re: SDL2 image rotation Problem
Post by: Seenkao on January 04, 2025, 12:11:05 am
Pe3s, вам ни кто не сможет помочь, хотя бы по простой причине, что непонятно, как вы поворачиваете свой объект. Даже люди использующие SDL вряд ли смогут вам помочь.

Поворот может быть выполнен несколькими способами:
- повернули весь экран.
- повернули текстуру.
- повернули объект.
- какой-то другой способ?

Если вы повернули весь экран, то это означает, что вы не учитываете при повороте ни какого масштаба. И просто повернули экран так, будь-то он  квадратный, а не прямоугольный (надо либо обрезать, либо масштабировать экран), для прямоугольного экрана, при повороте, надо пересчитывать все объекты согласно поворота и согласно центра экрана.

Если вы повернули объект или текстуру, то вероятно вы та же проблема что и выше, только тут вам для поворота надо пересчитывать координаты объекта или текстуры соответственно (помнить, что вы поворачиваете не квадрат, а прямоугольник).

Без вашего кода, сложно что-то вообще говорить о вашей проблеме.

------------------------------------------------
Google translate:
Pe3s, no one can help you, at least for the simple reason that it is not clear how you rotate your object. Even people using SDL are unlikely to be able to help you.

Rotation can be done in several ways:
- rotated the entire screen.
- rotated the texture.
- rotated the object.
- some other way?

If you rotated the entire screen, this means that you do not take into account any scale when rotating. And you simply rotated the screen, whether it is square, and not rectangular (you either need to crop or scale the screen), for a rectangular screen, when rotating, you need to recalculate all objects according to the rotation and according to the center of the screen.

If you rotated an object or texture, then you probably have the same problem as above, only here you need to recalculate the coordinates of the object or texture accordingly (remember that you are rotating not a square, but a rectangle).

Without your code, it is difficult to say anything about your problem.
Title: Re: SDL2 image rotation Problem
Post by: Pe3s on January 05, 2025, 08:02:07 am
@Seenkao, If you are able to help, please provide the entire code
Title: Re: SDL2 image rotation Problem
Post by: Pe3s on January 11, 2025, 06:08:53 pm
Can anyone help or is it over with the code questions :)
Title: Re: SDL2 image rotation Problem
Post by: Fred vS on January 12, 2025, 09:05:55 pm
Can anyone help or is it over with the code questions :)
Maybe if you add a valid tar it would be easier. (and dont use space " " but "_" instead :-X )
Code: Pascal  [Select][+][-]
  1. $ tar -xvzf Image_Browser_0.3.rar
  2.  
  3. gzip: stdin: not in gzip format
  4. tar: Child returned status 1
  5. tar: Error is not recoverable: exiting now

About rotation of image, I cannot see your code but if you use that method to load it:
Code: Pascal  [Select][+][-]
  1. SDL_LoadBMPFromFile('aimage.bmp');
A workaround would be to use BGRABitmap and do the rotation of the original file or already loaded bitmap and save it as tempory bmp.
Then reload the new rotated image with SDL_LoadBMPFromFile('arotatedimage.bmp') then delete it.
But I agree it is a workaround.

Note that I am a new explorer of SDL2 and I tested it only using MSEgui.
https://github.com/mse-org/mseide-msegui/discussions/94
Title: Re: SDL2 image rotation Problem
Post by: TRon on January 12, 2025, 09:14:15 pm
gzip: stdin: not in gzip format
tar: Child returned status 1
tar: Error is not recoverable: exiting now
You could use either 7z or (un)rar to extract rar archives. tar is not able to do that.
Title: Re: SDL2 image rotation Problem
Post by: Fred vS on January 12, 2025, 09:30:11 pm
gzip: stdin: not in gzip format
tar: Child returned status 1
tar: Error is not recoverable: exiting now
You could use either 7z or (un)rar to extract rar archives. tar is not able to do that.

Hello TRon.
unrar did the trick, thanks (but 7z failed, all the files have 0kb).
Title: Re: SDL2 image rotation Problem
Post by: Pe3s on January 13, 2025, 05:07:24 pm
I found a solution to my problem only that in Delphi there is a component ImageEn however, you have to reckon with the cost of buying a license. However, for graphics it is a great solution.
Greetings :)
Title: Re: SDL2 Fit image rotation bug
Post by: TRon on January 13, 2025, 10:00:09 pm
By the way, I can compile your project, but I get this error when I try to run it:
The SDLCreateWindowFrom function in combination with LCL only works for the Windows platform (other platforms/widgetsets require a special handle that allows for graphics to be drawn onto the canvas).

Can anyone help or is it over with the code questions :)
I order to be able to test your code it needs to be rewritten when doing so for a platform besides Windows.

Seenkao's questions are still valid because I also do not have a clue what it is that you are actually trying to achieve. The rotating works with keeping the aspect ratio of the original image. You do not seem to want that but do not mention what the result should become.

Should the image be resized to match the canvas, should the image be scaled to match the largest dimensions keeping the original aspect ratio and cut off everything that is out of range, should it resize the size of the canvas to match the (rotated)_ picture etc. etc.

No answers means we would have to come up with all possible scenario's while you seem to fancy only one of them (whatever that scenario might be) :)
Title: Re: SDL2 image rotation Problem
Post by: Pe3s on January 14, 2025, 08:44:26 am
@TRon I want that after rotation the image would fit according to the proportions of the window, plot, etc.

Let's take such an example, for example, from the IrfanView viewer if we rotate the image, then the image dimensions swap places and the image is adjusted to the window.

In my code if we read an image for example in portrait format then after rotation the image is smaller and does not fit the window.

Greetings

Title: Re: SDL2 image rotation Problem
Post by: Pe3s on January 14, 2025, 10:19:23 am
so I wonder if it would not help to dynamically swap the dimensions of the plot, only that sdl permanently holds the dimension of the loaded image.

- If we load an image with dimensions: 200x300px then after a 90 degree rotation the dimensions should change to 300x200, after another rotation 200x300, after another 300x200 and finally return to 200x300, and the image should be adjusted to the proportions of the window in the case of large images.
Title: Re: SDL2 image rotation Problem
Post by: Fred vS on January 14, 2025, 11:18:08 pm
Hello Pe3s.

In case you are not angry with me, here code to rotate image with SDL2.
Yes, Grok helps me but I must help him a lot too.
There are 2 images rendered, one from the png and the other is a rotation of the first image.

Code: Pascal  [Select][+][-]
  1. var
  2.  image_texture: PSDL_Texture = nil;
  3.  rotated_image_texture: PSDL_Texture = nil;
  4.  image_rect: TSDL_Rect = (x: 0; y: 0; w: 0; h: 0);
  5.  rotated_image_rect: TSDL_Rect = (x: 0; y: 0; w: 0; h: 0);
  6.  
  7. ...
  8.  
  9. procedure load_images;
  10. var
  11. image_surface: PSDL_Surface;
  12. begin
  13.   // Load first image
  14.   image_surface := IMG_Load('/path/of/image.png');
  15.   if image_surface = nil then
  16.   begin
  17.     WriteLn('Unable to load image! SDL_image Error: ', IMG_GetError());
  18.     Exit;
  19.   end else
  20.   begin
  21.     WriteLn('First Image loaded successfully.');
  22.   end;
  23.  
  24.   image_texture := SDL_CreateTextureFromSurface(renderer, image_surface);
  25.   SDL_QueryTexture(image_texture, nil, nil, @image_rect.w, @image_rect.h);
  26.  
  27.   // Use the same texture for rotation during rendering
  28.   rotated_image_texture := image_texture;
  29.   SDL_QueryTexture(rotated_image_texture, nil, nil, @rotated_image_rect.w, @rotated_image_rect.h);
  30.  
  31.   SDL_FreeSurface(image_surface);
  32. end;
  33.  
  34. procedure render;
  35. begin
  36.   SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
  37.   SDL_RenderClear(renderer);
  38.  
  39.   // Draw images first from png
  40.   if Assigned(image_texture) then
  41.   begin
  42.     SDL_RenderCopy(renderer, image_texture, nil, @image_rect);
  43.   end;
  44.  
  45.   // Draw rotated image
  46.   if Assigned(rotated_image_texture) then
  47.   begin
  48.     SDL_RenderCopyEx(renderer, rotated_image_texture, nil, @rotated_image_rect, 90, nil, SDL_FLIP_NONE);
  49.   end;
  50.  
  51.    SDL_QueryTexture(rotated_image_texture, nil, nil, @rotated_width, @rotated_height);
  52.    
  53.     // Adjust the rectangle for left alignment
  54.     rotated_image_rect.x := image_rect.w + 150 ;// Left align
  55.     rotated_image_rect.y := 120; // Position as before or adjust vertically if needed
  56.     rotated_image_rect.w := rotated_width; // Width stays the same as it was after rotation
  57.     rotated_image_rect.h := rotated_height; // Height stays the same as it was after rotation
  58. end;
  59.  
Title: Re: SDL2 image rotation Problem
Post by: Pe3s on January 15, 2025, 10:12:11 am
@Fred vS, I am not angry, I adhere to the principle that a smile is better than anger. I'm just looking for a solution.
I am grateful for any comment and help .
Thank you and Greetings with a smile.
Title: Re: SDL2 image rotation Problem
Post by: Fred vS on January 15, 2025, 12:23:28 pm
@Fred vS, I am not angry, I adhere to the principle that a smile is better than anger. I'm just looking for a solution.
I am grateful for any comment and help .
Thank you and Greetings with a smile.

Hello Pe3s.
Nice answer and good feeling. ;D
I am in the same boat as you, exploring sdl2.

In a previous post I told you about the fantastic graphical tool: BGRABitmap.
It can be perfectly used with sdl2 and will allow to do complicated image manipulations.
Here is the code that loads a image.png into an sdl2 image and rotates it using BGRABitmap and this rotated bitmap is filled into another sdl2 image.

Code: Pascal  [Select][+][-]
  1. uses
  2.   SysUtils, SDL2, SDL2_image, BGRABitmap, BGRABitmapTypes;
  3.  
  4. ...
  5.  
  6. var
  7.   window: PSDL_Window;
  8.   renderer: PSDL_Renderer;
  9.   running: Boolean = True;
  10.   original_texture, rotated_texture: PSDL_Texture;
  11.   original_rect, rotated_rect: TSDL_Rect;
  12.  
  13. ...
  14.  
  15. procedure load_and_process_images;
  16. var
  17.   surface: PSDL_Surface;
  18.   bgraBitmap, rotatedBitmap: TBGRABitmap;
  19.   rotated_surface: PSDL_Surface;
  20. begin
  21.   // Load image with SDL2
  22.   surface := IMG_Load('/path/to/image.png');
  23.   if surface = nil then
  24.   begin
  25.     WriteLn('Unable to load image! SDL_image Error: ', IMG_GetError());
  26.     Exit;
  27.   end;
  28.  
  29.   try
  30.     // Convert SDL_Surface to BGRABitmap for manipulation
  31.     bgraBitmap := TBGRABitmap.Create(surface^.w, surface^.h, BGRAWhite);
  32.     Move(surface^.pixels^, bgraBitmap.Data^, surface^.w * surface^.h * 4);
  33.  
  34.     // Rotate using BGRABitmap
  35.     rotatedBitmap := bgraBitmap.RotateCW;
  36.     try
  37.       // Convert back to SDL textures
  38.       original_texture := SDL_CreateTextureFromSurface(renderer, surface);
  39.       SDL_QueryTexture(original_texture, nil, nil, @original_rect.w, @original_rect.h);
  40.       original_rect.x := 10;
  41.       original_rect.y := 10;
  42.  
  43.       // Create a new surface for the rotated image with the same pixel format as the original
  44.       rotated_surface := SDL_CreateRGBSurface(0, rotatedBitmap.Width, rotatedBitmap.Height,
  45.                                                surface^.format^.BitsPerPixel,
  46.                                                surface^.format^.Rmask,
  47.                                                surface^.format^.Gmask,
  48.                                                surface^.format^.Bmask,
  49.                                                surface^.format^.Amask);
  50.       if rotated_surface <> nil then
  51.       begin
  52.         // Copy the bitmap data, making sure color channels are in the correct order
  53.         Move(rotatedBitmap.Data^, rotated_surface^.pixels^, rotatedBitmap.NbPixels * 4);
  54.         rotated_texture := SDL_CreateTextureFromSurface(renderer, rotated_surface);
  55.         SDL_QueryTexture(rotated_texture, nil, nil, @rotated_rect.w, @rotated_rect.h);
  56.         rotated_rect.x := 10;
  57.         rotated_rect.y := original_rect.y + original_rect.h + 10;
  58.         SDL_FreeSurface(rotated_surface);
  59.       end else
  60.       begin
  61.         WriteLn('Failed to create surface for rotated image: ', SDL_GetError());
  62.       end;
  63.     finally
  64.       rotatedBitmap.Free;
  65.     end;
  66.   finally
  67.     bgraBitmap.Free;
  68.     SDL_FreeSurface(surface);
  69.   end;
  70. end;
  71.  
  72. procedure render;
  73. begin
  74.   SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
  75.   SDL_RenderClear(renderer);
  76.  
  77.   if Assigned(original_texture) then
  78.     SDL_RenderCopy(renderer, original_texture, nil, @original_rect);
  79.  
  80.   if Assigned(rotated_texture) then
  81.     SDL_RenderCopy(renderer, rotated_texture, nil, @rotated_rect);
  82.  
  83.   SDL_RenderPresent(renderer);
  84. end;
  85.  
Title: Re: SDL2 image rotation Problem
Post by: TRon on January 15, 2025, 07:55:17 pm
@TRon I want that after rotation the image would fit according to the proportions of the window, plot, etc.
Ok, in that case you need to be prepared and expect that the image is stretched. But then the question becomes how to treat the contents of the picture itself. In case the dimensions of the graphical content do not match the window then you can either opt for resizing the image to the smallest available window height/width and cut of the other direction or you would have to deal with the fact that there are either horizontal or vertical borders. The other option is the fit the image into the window dimensions but then the image will need to be resized to match those dimensions (in that process the aspect ratio of the contents of the graphics will change accordingly).

@Fred:
Nice solution (and I did not know that you could mix bgra with sdl) but realize that the original code in the rar archive from TS already rotated the image correctly. The problem lies in how to present the rotated picture in to the available canvas space (window). TS is unclear on how that should behave and there are several situations possible.
Title: Re: SDL2 image rotation Problem
Post by: Fred vS on January 16, 2025, 01:13:15 am
@Fred:
 (and I did not know that you could mix bgra with sdl)
BGRABitmap is fabulous.  ;)
There is the full demo with SDL2 pascal headers here. (https://github.com/fredvs/sdl2_pas)

@Fred:
 but realize that the original code in the rar archive from TS already rotated the image correctly.
Yes but the result is stretched using width and length of original picture.
With the code I provided it is rotated with width = original height and height = original width.
Title: Re: SDL2 image rotation Problem
Post by: TRon on January 19, 2025, 12:39:30 am
Yes but the result is stretched using width and length of original picture.
With the code I provided it is rotated with width = original height and height = original width.
Leaving out the LCL related code and only focusing on the SDL side of the graphics the code already works correctly. That is what I am trying to tell  :)

PS: in case someone from FPC meets SDL happens to read this: The 2.2 stable release (SDL2-for-Pascal-2.2-stable.tar.gz) seems to be missing SDL_BlitScaled, e.g.
Code: Pascal  [Select][+][-]
  1. function SDL_BlitScaled(src: PSDL_Surface; const srcrect: PSDL_Rect; dst: PSDL_Surface; dstrect: PSDL_Rect): cint; cdecl; inline;
  2. begin
  3.   result := SDL_BlitSurfaceScaled(src, srcrect, dst, dstrect);
  4. end;
  5.  
Title: Re: SDL2 image rotation Problem
Post by: Pe3s on February 14, 2025, 07:28:30 am
Thank you for your discussion and help  :)
TinyPortal © 2005-2018