Forum > LCL
[SOLVED] 2 problems with this procedure to rotate a Bitmap by 90/180/270°
Hartmut:
I am looking for a procedure to rotate a Bitmap by 90/180/270°. In this forum I found such a procedure made by wp in reply #16 of https://forum.lazarus.freepascal.org/index.php/topic,63277.0.html with a small improvement in reply #19.
On Windows it works perfectly, but on Linux I face 2 problems:
a) when I rotate by 180° and 'ABitmap.Width' is not divisible by 8, then the result is an empty (black) Bitmap.
b) when I rotate by 90° or 270° and 'ABitmap.Height' is not divisible by 8, then the result is an empty (black) Bitmap.
I attached a compilable project to test this:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program project1; {$mode objfpc}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset Forms, // Unit1, { you can add units after this } sysutils, Graphics, LCLType, IntfGraphics;{$R *.res} type TImgRotation = ( irError, irNormal, irMirrorHor, irRotate180, irMirrorVert, irMirrorHorRot270, irRotate90, irMirrorHorRot90, irRotate270 ); // all angle are clockwise procedure RotateBitmap(const ABitmap: TBitmap; Angle: TImgRotation);Var bmp: TBitmap; srcImg, dstImg: TLazIntfImage; imgHandle, imgMaskHandle: HBitmap; i, j: integer; w1, h1: Integer; // Input bitmap width and height diminished by 1Begin Assert(ABitmap <> nil, 'RotateBitmap: Input bitmap is expected not to be nil.'); if (Angle = irError) or (Angle = irNormal) then exit; w1 := ABitmap.Width - 1; h1 := ABitmap.Height - 1; srcImg := TLazIntfImage.Create(0, 0); try srcImg.LoadFromBitmap(ABitmap.Handle, ABitmap.MaskHandle); bmp := TBitmap.Create; try bmp.PixelFormat := pf32Bit; // added due to reply #19 dstImg := TLazIntfImage.Create(0, 0); try if Angle in [irRotate90, irRotate270, irMirrorHorRot90, irMirrorHorRot270] then begin bmp.SetSize(ABitmap.Height, ABitmap.Width); dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle); case Angle of irRotate90: for i:=0 to w1 do for j:=0 to h1 do dstImg.Colors[h1-j, i] := srcImg.Colors[i, j]; irRotate270: for i:=0 to w1 do for j:=0 to h1 do dstImg.Colors[j, w1-i] := srcImg.Colors[i, j]; irMirrorHorRot90: for i:=0 to w1 do for j:=0 to h1 do dstImg.Colors[h1-j, w1-i] := srcImg.Colors[i, j]; irMirrorHorRot270: for i:=0 to w1 do for j:=0 to h1 do dstImg.Colors[j, i] := srcImg.Colors[i, j]; end; end else if Angle in [irRotate180, irMirrorHor, irMirrorVert] then begin bmp.SetSize(ABitmap.Width, ABitmap.Height); dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle); case Angle of irRotate180: for i:=0 to w1 do for j:=0 to h1 do dstImg.Colors[w1-i, h1-j] := srcImg.Colors[i, j]; irMirrorHor: for j:=0 to h1 do for i:=0 to w1 do dstImg.Colors[w1-i, j] := srcImg.Colors[i, j]; irMirrorVert: for i:=0 to w1 do for j:=0 to h1 do dstImg.Colors[i, h1-j] := srcImg.Colors[i, j]; end; end; dstImg.CreateBitmaps(imgHandle, imgMaskHandle, false); bmp.Handle := ImgHandle; bmp.MaskHandle := ImgMaskHandle; finally dstImg.Free; end; ABitmap.Assign(bmp); finally bmp.Free; end; finally srcImg.Free; end;end; {RotateBitmap} procedure convert(fspecIn,fspecOut: string; rot: TImgRotation); {converts Graphic file 'fspecIn' to 'fspecOut' and does rotation by 'rot'} var PT: TPicture; begin PT:=TPicture.Create; PT.LoadFromFile(fspecIn); RotateBitmap(PT.Bitmap,rot); PT.SaveToFile(fspecOut); PT.Free; writeln('done'); end; {convert} begin {main}if ParamCount <> 2 then begin writeln('Usage: <inputfile> <outputfile>'); exit; end; if not FileExists(ParamStr(1)) then begin writeln('Inputfile not found!'); exit; end; convert(ParamStr(1),ParamStr(2),irRotate180); // use 90/180/270°end.
And I attached 3 demo files:
- one has 400x400 pixels and rotate by 90/180/270° works perfectly.
- one has 399x400 pixels and rotate by 180° results is an empty (black) Bitmap.
- one has 400x399 pixels and rotate by 90 or 270° results is an empty (black) Bitmap.
Versions:
- Windows 7 (32-bit) with Lazarus 2.0.10
- Linux Ubuntu 22.04 (64-bit) with Lazarus 3.4.0 (same results with Lazarus 2.0.10)
I'm still a beginner to Graphics. Can please somebody help to fix this problem? Thanks in advance.
Hartmut:
In reply #7 from VTwin in https://forum.lazarus.freepascal.org/index.php/topic,8282.0.html I found another procedure to rotate a Bitmap:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure RotateBitmap90(const bitmap: TBitmap); {rotates a Bitmap 90° to the left}var tmp: TBitmap; src, dst: TLazIntfImage; ImgHandle, ImgMaskHandle: HBitmap; i, j, t, u, v: integer;begin tmp := TBitmap.create; tmp.Width := Bitmap.Height; tmp.Height := Bitmap.Width; dst := TLazIntfImage.Create(0, 0); dst.LoadFromBitmap(tmp.Handle, tmp.MaskHandle); src := TLazIntfImage.Create(0, 0); src.LoadFromBitmap(bitmap.Handle, bitmap.MaskHandle); u := bitmap.width - 1; v := bitmap.height - 1; for i := 0 to u do begin t := u - i; for j := 0 to v do dst.Colors[j, t] := src.Colors[i, j]; end; dst.CreateBitmaps(ImgHandle, ImgMaskHandle, false); tmp.Handle := ImgHandle; tmp.MaskHandle := ImgMaskHandle; dst.Free; bitmap.Assign(tmp); tmp.Free; src.Free;end;
I tested this new procedure and it does not have my problem, that the result is an empty (black) Bitmap, when 'Bitmap.Height' is not divisible by 8. But it has another problem: with this new procedure a lot of picture files become a wrong background color.
I compared this new procedure with the old one from wp in my 1st post and the only real difference I found, is this line which only exists in the 1st procedure:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---bmp.PixelFormat := pf32Bit; // see line 41
I tested the procedure in my 1st post without this command and then it behaves identical to the new procedure:
- it does not longer have the problem, that the result is an empty (black) Bitmap, when 'ABitmap.Height' is not divisible by 8
- but it has the other problem: a lot of picture files become a wrong background color.
Again: the problems only occour on Linux, not on Windows. Does this new information help to fix this problem on Linux?
marcov:
I don't use tbitmap, so unfortunately can't exactly pinpoint where the problem is, but it seems you copy over pixel data (color[]), but not other properties like transparency, bit depth etc. Probably the problem lies there.
OTOH, the linux only problem seems to indicate to your widgetset's LCL backend or the kit itself (GTK or QT)
On Windows bitmaps are defined as having a width that is divisible by 4. Maybe the Linux graphic system has native size or so
wp:
This seems to be yet another gtk2 issue: I can confirm the issue only on Linux gtk2, but not on qt5 and qt6.
You should file a bugreport.
Hartmut:
Thanks a lot to marcov and wp for your replies. I tested with qt5 and can confirm, that the issue there not exists. I will file a bug report. Unfortunately I cannot switch my whole project to qt5 (too many other problems).
--- Quote from: marcov on November 14, 2024, 09:45:29 pm ---On Windows bitmaps are defined as having a width that is divisible by 4. Maybe the Linux graphic system has native size or so
--- End quote ---
When I omit command 'bmp.PixelFormat := pf32Bit' (see line #41 in code) then the "size divisible by 8 problem" disappears. So from my point of view I don't see a Linux OS limitation (but I maybe wrong).
--- Quote ---I don't use tbitmap, so unfortunately can't exactly pinpoint where the problem is, but it seems you copy over pixel data (color[]), but not other properties like transparency, bit depth etc. Probably the problem lies there.
--- End quote ---
This sounds plausible. Does someone know how to copy other properties like transparency, bit depth etc.?
Navigation
[0] Message Index
[#] Next page