Recent

Author Topic: [SOLVED] A story of pointers and bitmap  (Read 5820 times)

KemBill

  • Jr. Member
  • **
  • Posts: 74
[SOLVED] A story of pointers and bitmap
« on: November 20, 2017, 06:35:37 pm »
Hello,

I found this snippet on the forum, it works perfectly, it flips a bitmap by accessing directly the pixel data :

Code: Pascal  [Select][+][-]
  1. procedure FlipVertical (var px: TBitmap);
  2. var
  3.   p: array of byte;
  4.   i, half, b: integer;
  5.   LoPtr, HiPtr: PInteger;
  6. begin
  7.     if px.Height < 3 then exit;
  8.     half := (px.Height div 2);
  9.     b := px.RawImage.Description.BytesPerLine;
  10.     LoPtr := PInteger(px.RawImage.Data);
  11.     HiPtr := PInteger(px.RawImage.Data+ ((px.Height -1) * b));
  12.     setlength(p, b);
  13.     for i := 1 to half do begin
  14.           System.Move(LoPtr^,p[0],b); //(src, dst,sz)
  15.           System.Move(HiPtr^,LoPtr^,b); //(src, dst,sz)
  16.           System.Move(p[0],HiPtr^,b); //(src, dst,sz)
  17.           Inc(PByte(LoPtr), b );
  18.           Dec(PByte(HiPtr), b);
  19.     end;
  20. end;  
  21.  

I'm trying to make it more generic, in order to flip any kind of picture

Code: Pascal  [Select][+][-]
  1. procedure FlipVertical2 (const px: PByte; const height, BytesPerLine: integer);
  2. var
  3.   p: array of byte;
  4.   i, half: integer;
  5.   LoPtr, HiPtr: PInteger;
  6. begin
  7.     if Height < 3 then exit;
  8.     half := (Height div 2);
  9.     LoPtr := PInteger(px);
  10.     HiPtr := PInteger(px+ ((Height -1) * BytesPerLine));
  11.     setlength(p, BytesPerLine);
  12.     for i := 1 to half do begin
  13.           System.Move(LoPtr^,p[0],BytesPerLine); //(src, dst,sz)
  14.           System.Move(HiPtr^,LoPtr^,BytesPerLine); //(src, dst,sz)
  15.           System.Move(p[0],HiPtr^,BytesPerLine); //(src, dst,sz)
  16.           Inc(PByte(LoPtr), BytesPerLine );
  17.           Dec(PByte(HiPtr), BytesPerLine);
  18.     end;
  19. end;
  20.  

I'm getting a sigsegv, i'm somehow reading outside the memory, but can't figure why  :(, any ideas ?
« Last Edit: November 21, 2017, 11:42:47 am by KemBill »

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: A story of pointers and bitmap
« Reply #1 on: November 20, 2017, 08:19:20 pm »
TBitmap and PByte are not compatible. PByte is a pointer that point a byte data, TBitmap is a class, which contains more than simple data.

The lines that contain serious problem in the second code are the value assignment for LoPtr and HiPtr.

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: A story of pointers and bitmap
« Reply #2 on: November 20, 2017, 08:44:45 pm »
TBitmap and PByte are not compatible. PByte is a pointer that point a byte data, TBitmap is a class, which contains more than simple data.

The lines that contain serious problem in the second code are the value assignment for LoPtr and HiPtr.

let's say i'm calling the procedure FlipVertical2 with a TBitmap.RawImage.Data like

Code: Pascal  [Select][+][-]
  1. FlipVertical2 (bmp.RawImage.Data, bmp.height, bmp.RawImage.Description.BytesPerLine);

the parameter is a pbyte, right ?

So I just can't figure I read outside the memory since the values are the same in both case

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: A story of pointers and bitmap
« Reply #3 on: November 20, 2017, 09:33:17 pm »
If data is like a dynamic array you need something like (@bmp.rawimage.data[0]);

Because .data is where the pointer to data[0] is stored, not the address of data[0] itself.

Anyway, I looked through the routine earlier, and it seems fine. The problem is in the calling, not the procedure.

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: A story of pointers and bitmap
« Reply #4 on: November 21, 2017, 12:29:52 am »
I don't know if this works on other platforms but it has worked in Windows for ages..

if you want to flip a bitmap, you simply reverse one of the Rectangles coordinates
use two bitmaps, one as the source and one as the destination..

Using CopyRect(Destination_rect, Source_canvas, Source_Rect);

 Invert ether the Source or destination rectangle...
 Left := Width, Right := 0; etc..
 
 Only one Trect is inverted, the other is normal...

 You can flip the image sideways or vertically and its part of the system..

 for rotations you need to do it via pixels..
The only true wisdom is knowing you know nothing

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: A story of pointers and bitmap
« Reply #5 on: November 21, 2017, 11:41:32 am »
KISS = Keep It Simple Stupid  :o

just add GraphType to the unit and the procedure now becomes

Code: Pascal  [Select][+][-]
  1. procedure FlipVertical2(const px: TRawImage);
  2. var
  3.   p: array of byte;
  4.   i, half: integer;
  5.   LoPtr, HiPtr: PInteger;
  6.   bpl: integer;
  7. begin
  8.   bpl := px.Description.BytesPerLine;
  9.   if px.Description.Height < 3 then
  10.     exit;
  11.   half := (px.Description.Height div 2);
  12.   LoPtr := PInteger(px.Data);
  13.   HiPtr := PInteger(px.Data + ((px.Description.Height - 1) * bpl));
  14.   setlength(p, bpl);
  15.   for i := 1 to half do
  16.   begin
  17.     System.Move(LoPtr^, p[0], bpl); //(src, dst,sz)
  18.     System.Move(HiPtr^, LoPtr^, bpl); //(src, dst,sz)
  19.     System.Move(p[0], HiPtr^, bpl); //(src, dst,sz)
  20.     Inc(PByte(LoPtr), bpl);
  21.     Dec(PByte(HiPtr), bpl);
  22.   end;
  23. end;
  24.  

I don't know if this works on other platforms but it has worked in Windows for ages..

if you want to flip a bitmap, you simply reverse one of the Rectangles coordinates
use two bitmaps, one as the source and one as the destination..

Using CopyRect(Destination_rect, Source_canvas, Source_Rect);

 Invert ether the Source or destination rectangle...
 Left := Width, Right := 0; etc..
 
 Only one Trect is inverted, the other is normal...

 You can flip the image sideways or vertically and its part of the system..

 for rotations you need to do it via pixels..


you need to have 2 bitmaps to do this ?

With this proc, this is an inplace transformation, no additional memory is used except a buffer to swap the lines.

my problem is a kind of solved, but if someone has the answer for the pointer solution, I will be happy to understand what was happening

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: [SOLVED] A story of pointers and bitmap
« Reply #6 on: November 21, 2017, 11:50:50 am »
On Windows you might be able to simply change the definition of the bitmap (from bottom up to up down) by making the height negative (and possibly set other bits in the DIB header)

I used to do that, and had a system that was entirely zero copy (camera images are often topdown, while most windows systems want bottom up). Image storage used modified fcl-image routines to store the image bottom up.

Since 2008 I converted it to opengl though, so windows bitmaps were designed out.

In such regime  the bytesperline can be negative (for bottom up) and postive for topdown.

My image routines are available in this thread
« Last Edit: November 21, 2017, 11:52:27 am by marcov »

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: [SOLVED] A story of pointers and bitmap
« Reply #7 on: November 21, 2017, 01:32:49 pm »
On Windows you might be able to simply change the definition of the bitmap (from bottom up to up down) by making the height negative (and possibly set other bits in the DIB header)

I used to do that, and had a system that was entirely zero copy (camera images are often topdown, while most windows systems want bottom up). Image storage used modified fcl-image routines to store the image bottom up.

Since 2008 I converted it to opengl though, so windows bitmaps were designed out.

In such regime  the bytesperline can be negative (for bottom up) and postive for topdown.

My image routines are available in this thread

it's funny because i'm working on an OpenGL texture loader  :D

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: [SOLVED] A story of pointers and bitmap
« Reply #8 on: November 21, 2017, 03:18:11 pm »
it's funny because i'm working on an OpenGL texture loader  :D

You don't have to flip for opengl, you just adjust the "from"  texture coordinates. Show your loading and drawing code in a new thread.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: [SOLVED] A story of pointers and bitmap
« Reply #9 on: November 22, 2017, 01:54:15 pm »
Rather than just speaking and guessing, I decided to write a test for both the codes in OP's first post.

It works. It really flip the image correctly without error.

Unfortunately, there is a bug there. It only can flip the image once but second click on the buttons give no result. You can use the pas files I provided for testing.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  9.   StdCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Button2: TButton;
  18.     Button3: TButton;
  19.     Image1: TImage;
  20.     OpenDialog1: TOpenDialog;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure Button2Click(Sender: TObject);
  23.     procedure Button3Click(Sender: TObject);
  24.   private
  25.  
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. procedure FlipVertical (var px: TBitmap);
  36. var
  37.   p: array of byte;
  38.   i, half, b: integer;
  39.   LoPtr, HiPtr: PInteger;
  40. begin
  41.     if px.Height < 3 then exit;
  42.     half := (px.Height div 2);
  43.     b := px.RawImage.Description.BytesPerLine;
  44.     LoPtr := PInteger(px.RawImage.Data);
  45.     HiPtr := PInteger(px.RawImage.Data+ ((px.Height -1) * b));
  46.     setlength(p, b);
  47.     for i := 1 to half do begin
  48.           System.Move(LoPtr^,p[0],b); //(src, dst,sz)
  49.           System.Move(HiPtr^,LoPtr^,b); //(src, dst,sz)
  50.           System.Move(p[0],HiPtr^,b); //(src, dst,sz)
  51.           Inc(PByte(LoPtr), b );
  52.           Dec(PByte(HiPtr), b);
  53.     end;
  54. end;
  55.  
  56. procedure FlipVertical2 (const px: PByte; const height, BytesPerLine: integer);
  57. var
  58.   p: array of byte;
  59.   i, half: integer;
  60.   LoPtr, HiPtr: PInteger;
  61. begin
  62.     if Height < 3 then exit;
  63.     half := (Height div 2);
  64.     LoPtr := PInteger(px);
  65.     HiPtr := PInteger(px+ ((Height -1) * BytesPerLine));
  66.     setlength(p, BytesPerLine);
  67.     for i := 1 to half do begin
  68.           System.Move(LoPtr^,p[0],BytesPerLine); //(src, dst,sz)
  69.           System.Move(HiPtr^,LoPtr^,BytesPerLine); //(src, dst,sz)
  70.           System.Move(p[0],HiPtr^,BytesPerLine); //(src, dst,sz)
  71.           Inc(PByte(LoPtr), BytesPerLine );
  72.           Dec(PByte(HiPtr), BytesPerLine);
  73.     end;
  74. end;
  75.  
  76. {$R *.lfm}
  77.  
  78. { TForm1 }
  79.  
  80. procedure TForm1.Button1Click(Sender: TObject);
  81. begin
  82.   if not(OpenDialog1.Execute) then Exit;
  83.   Image1.Picture.LoadFromFile(OpenDialog1.FileName);
  84.   Button2.Enabled := True;
  85.   Button3.Enabled := True;
  86. end;
  87.  
  88. procedure TForm1.Button2Click(Sender: TObject);
  89. var
  90.   TempBitmap: TBitmap;
  91. begin
  92.   TempBitmap := Image1.Picture.Bitmap;
  93.   FlipVertical(TempBitmap);
  94. end;
  95.  
  96. procedure TForm1.Button3Click(Sender: TObject);
  97. var
  98.   TempBitmap: TBitmap;
  99. begin
  100.   TempBitmap := Image1.Picture.Bitmap;
  101.   with TempBitmap do
  102.     FlipVertical2(RawImage.Data, Height, RawImage.Description.BytesPerLine);
  103. end;
  104.  
  105. end.
« Last Edit: November 22, 2017, 01:56:02 pm by Handoko »

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: [SOLVED] A story of pointers and bitmap
« Reply #10 on: November 23, 2017, 07:07:36 pm »

Unfortunately, there is a bug there. It only can flip the image once but second click on the buttons give no result. You can use the pas files I provided for testing.


This is not a bug, it's a feature  :D

you just forgot to put beginupdate and endupdate

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.   TempBitmap: TBitmap;
  4. begin
  5.   TempBitmap := Image1.Picture.Bitmap;
  6.   TempBitmap.BeginUpdate();
  7.   FlipVertical(TempBitmap);
  8.   TempBitmap.EndUpdate();
  9. end;  
  10.  

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: [SOLVED] A story of pointers and bitmap
« Reply #11 on: November 24, 2017, 09:33:36 am »
Oops, I forget it. :-[

Yes, you're right it's working perfectly now. And thank you for sharing that code. I saved it because I believe I will use it someday.

But I wonder why you got sigsegv error.

 

TinyPortal © 2005-2018