Dynamic arrays as parameters to API functions and Delphi - delphi

In my application (Delphi 2010, OpenGL, windows XP), I need to read back the pixels of variable portions of the framebuffer.
The area of interest is input by the user through a selection rectangle (x1, y1, x2, y2).
With this coordinates I do this:
var
pixels : PGLUByte; //pointer to unsigned bytes
begin
[Transformation of coordinates to Opengl viewport offsets]
//reserve a block of memory for readpixels to write to
ReallocMem(pixels, width * height* sizeof(GLUByte)*3); //<<< crash on this line after a few iterations
if not assigned(pixels) then exit;
//read the pixels
glReadPixels(startx, viewport[3] - (starty+height),
width , height,
GL_RGB, GL_UNSIGNED_BYTE,
pixels);
//Processing of the pixel data follows here...
//when done, release the memory
ReallocMem(pixels, 0);
end;
This function seems to work as intended at the first few tries, but after a few calls to it, the application crashes with an Access violation at $0000000 on the first ReallocMem.
I tried using Getmem, Finalize and Freemem, but these functions lead to the same behaviour.
Is my design principially correct? I tried to debug it, but i could not identify the cause of trouble. width and height always have plausible values, and allocating 5-10 blocks of 30 to 120 KiB should not be an issue on a machine with 3 GB of RAM.
Update
Between the calls to this function, The render pipeline might Draw a few frames, objects may be added to the scene - in principle anything the application is capable of, as this function is called when the user decided to select a rectangular portion of my scene for capture through dragging a seelction box over my Canvas.
Here is a sample of widths and heights from a debug session of mine
width : 211 height: 484 size: 306372
width : 162 height: 395 size: 191970
width : 123 height: 275 size: 101475
width : 14 height: 346 size: 14532
The fourth Selection failed in this session. in Other session, more succesive selections were possible, others crashed when trying the second, but none on the first.
Another thing: when I comment out glReadPixels, no more crashes appear.

I found it after all.
My calculation for width and height were of by one, so i needed to change my reallocmem-line to
ReallocMem(pixels, (width+1) * (height+1) * sizeof(GLUByte)*3);
Thank you for you consideration

Do you initialize pixels to nil?
begin
pixels := nil;
...
Do you allocate enough memory? This example
allocates nWidth + 1, nHeight + 1
mentions that OpenGL might align memory to 4 bytes by default (see GL_PACK_ALIGNMENT).

Related

8086 Assembly: wrong data reading video memory

I'm working for a school project on a little game programmed in 8086 Assembly language.
I have to draw on the screen (color some pixels), to do so I use interrupt 10h with mode 13h (ax = 13h). This is a 320px X 200px video mode.
(Note: you can best read the text underneath with the code opened in another tab (you will better understand what I'm explaining in words))
I want to first initialize the screen so I'm sure each pixel is black. To do so I first initialize a palette with black = color number 0.
After that I use a primitive for loop procedure I wrote to initialize the screen (set each pixel black). I pass as arguments respectively the start index (index in video memory (i.e 0 for the first pixel)), the stop index (64 000, last pixel (320px X 200px = 64 000)) and the step size with which the index has to be incremented.
So all it does is looping from the specified begin adres to the specified stop adres in memory and for each adres putting it on 0 (because black = color number 0 of the palette).
So normally now every pixel of my screen is black. Indeed when I launch my little program, the 320x200 video mode appears and the screen is black.
Further in the program I often have to compare the color of a pixel on the screen. Normally when i acces a certain adress in the video memory it has to be 0 (because I initialized the whole screen on black (color number 0)) except if I colored that pixel with another color.
But when testing my program I found out that certain pixels were black on the screen (and since the initialization I never changed their color) but when I displayed their value, it appeared to be 512 instead of 0. I can not understand why, since I never changed the color since I initialized them.
I spent hours trying to debug it but I cannot figure out why that pixel suddenly changes from color number 0 of the palette (black) to 512.
Because the pixel with color value 512 is also black on the screen I suppose that is also a value for that color but I want explicitly use color number 0 for black so that I can compare it (because now there is 0 but also 512 for black and maybe other black values).
Relevant part of the code:
mov ax, 0a000h ; begin address of video memory
mov es, ax
mov ax, [bp+4][0] ; We put the 1st argument (index) in register ax
mov di, ax
;;;; FOR DEBUGGING PURPOSES
mov ax, es:[di]
push ax ; We print the color of the pixel we are checking (normally has to be 0 if that pixel is black on the screen)
call tprint ; 70% of the time the printed color number is 0 but sometimes it prints color number 512 (also a black color but I don't want that, I initialized it to 0!!)
;;;; END DEBUG
;;;; ALSO STRANGE IS THAT WHEN I OUTCOMMENT THESE 3 LINES ABOVE, THE LAST PIXEL OF THE FIRST ROW IS COLORED
;;;; WHEN I LEAVE THESE 3 LINES LIKE NOW (PRINTING THE VALUE OF THAT PIXEL) IT IS THE NEXT PIXEL THAT IS COLORED
;;;; (strange but i don't really care since it was introduced only to debug)
CMP es:[di], 0 ; Comparison to see if the pixel we are checking is black.
; But when it is 512, my program will think it isn't the black color, and will stop executing (because after this call I do a JNZ jump to quit the loop)
Thanks for your help!
As #nrz hinted, the problem is with data size, although slightly different than what he described. Actually you are loading 2 bytes, so 2 pixels at once instead of 1. You get a value of 512 if a pixel with color 0 is beside a pixel with color 2.
You need to change line 182 to movzx ax, byte ptr es:[di] and line 190 to cmp byte ptr es:[di], 0 (use whatever syntax your assembler supports for byte operations).

Converting Delphi TPoint to C# Point

I am trying to convert some Delphi code as we are re-writing a Delphi 6.0 (VCL) application in .Net. I am not sure and could not figure out the comparison between 2 Delphi Tpoints(x,y) with that of C# Point(x,y).
I am trying to draw a line between 2 points but since I have no idea how Delphi draws it, I am not able to set the C# coordinates for it.
The Delphi code is simple:
Canvas.MoveTo(x, y - 128);
Canvas.LineTo(x, y);
I know about the C# coordinates though about 72 Points per inch and need to calculate the pixel density. But I am not sure about the Delphi PPI.
Any would be appreciated. Thanks.
Edit: If someone is wondering what TPoint I am talking when there is none in my code snippet, Canvas.MoveTo sets the PenPos property of the canvas which is of type TPoint.
I'm not sure what the exact question is that's being asked here. You have no Delphi TPoint in your code snippet; you simply have client rect logical coordinates.
The origin is at X = 0, Y = 0, which is the top left corner of the client area. Increasing X moves the position to the right, and increasing Y moves the position down. Logical units are pixels, so starting at the origin of 0, 0, a Canvas.MoveTo(10, 10) would set the new drawing position in from the left edge 10 pixels and down from the top 10 pixels, and a Canvas.LineTo(20, 20) from there would draw a line from the point at 10, 10 to 20, 20.
TCanvas.MoveTo and TCanvas.LineTo are simply wrappers around the underlying Windows GDI functions MoveToEx (with an always NULL third parameter) and LineTo.
As far as the C# equivalent, if you're referring to System.Drawing.Point, the units used are exactly the same (although I'm not sure where the origin is based by default). Given an origin of 0, 0, System.Drawing.Point(10, 10) should be the same position described above - 10 pixels from the left edge and 10 pixels down from the top edge.
A quick check confirms that the origin in a WinForms application is in fact the top left corner of the client area, using:
// Delphi code
procedure TForm3.FormPaint(Sender: TObject);
begin
Canvas.Pen.Color := clRed;
Canvas.MoveTo(0, 0);
Canvas.LineTo(100, 100);
end;
// C# code
private void Form1_Paint(object sender, PaintEventArgs e)
{
Pen newPen = new System.Drawing.Pen(Color.Red);
e.Graphics.DrawLine(newPen, new Point(0, 0), new Point(100, 100));
}
This produces the following output:

Scrolling TImage horizontally or vertically

I am writing 2-3 Trees application in Lazarus for my school project.
Everything's done, now playing with GUI (I get the same number of points for good GUI as I do for a good etc. Insert function, which is weird but nvm).
When I have like 10+ nodes in the tree, my 300*200 image size just isn't large enough.
I would like to have an TImage component which would be like 300*200 on the TForm, but it would be like 10000 * 10000 really and you could scroll in it.
Is it even possible?
Thanks
EDIT TO MAKE THE QUESTION CLEARER
A 2-3 Tree is a data structure. When drawn on a paper to see how it works, it looks like this http://www.cosc.canterbury.ac.nz/research/RG/alg/tree23.gif
As a real noobie in lazarus/delphi (have to do it in lazarus) a use this code to draw it (even if I doubt u need it to answer my question):
procedure TStrom.Paint(Image: TImage);
var
C: TCanvas;
procedure Paint1(V: TNode; Width, X, Y: integer);
begin
if V.L <> nil then //left child
begin
C.MoveTo(X, Y);
C.LineTo(X - Width div 3, Y + 50);
Paint1(V.L, Width div 3, X - Width div 3, Y + 50);
end;
if V.S <> nil then //middle child
begin
C.MoveTo(X, Y);
C.LineTo(X + Width div 3, Y + 50);
Paint1(V.S, Width div 3, X + Width div 3, Y + 50);
end;
if V.P <> nil then //right child
begin
C.MoveTo(X, Y);
C.LineTo(X + Width div 3 + Width div 3, Y + 50);
Paint1(V.P, Width div 3, X + Width div 3 + Sirka div 3, Y + 50);
end;
if V.isLeaf then
begin
C.Ellipse(X - 15, Y - 15, X + 15, Y + 15);
C.TextOut(X - 3, Y - 8, IntToStr(V.Info1));
end
else
begin
C.Rectangle(X - 15, Y - 15, X + 15, Y + 15);
C.TextOut(X - 7, Y - 8, IntToStr(V.Info1));
C.Rectangle(X + 15, Y - 15, X + 50, Y + 15);
if V.Info2 <> 0 then
C.TextOut(X + 27, Y - 8, IntToStr(V.Info2));
end;
The draw function works well, but some (most) of the nodes at the height of 3+ are drawn on other nodes, so it looks bad. The node is sitting on another node and is not 20 pixels next to it.
I thought I'd make the image where the tree is painted real big, but it would be in a small "panel". Like this: the TImage would really be 1000*1000, but in the form you could see only a little part of it. In this part there'd be horizontal and vertical scrollbars, so you could scroll through the image and see what's painted in the sections. (Like when you scroll through a web browser to see the bottom of the page :) )
We are not permitted to use any other code, just built in lazarus components. (nor are we permitted to create new components -> have no idea why)
While I'm still curious about how this could be done, it's no longer necessary for my application ( installed a second monitor to see if it'd help and it wouldn't, so I guess I'd dig through my paint method a bit :-) )
Your edit makes it more clear that you want to draw a schematic like the last tree in the example you link to.
From your code I understand that you are drawing all nodes, childs and leaves, onto a single canvas resulting in one large image/bitmap. Now, how to display only part of that large image with scroll bars next to it?
The obvious choice would be to place a TPaintBox on a TScrollBox. I do not know the default suite of components in Lazarus, but I expect both of them to be present. Give the paint box a size equal to the bounds of your visual tree and you're set: scroll bars will be shown automatically.
The paint box has an OnPaint event in which you do your paint work by drawing to PaintBox.Canvas. To optimize this, you could limit your drawing to PaintBox.Canvas.ClipRect (the part of the paint box that is visible within the scroll box), but I suspect that to be difficult, since you already have difficulty with computing the right distance between adjacent nodes. (About that: I expect the maximum width and height of the total tree to be a function of the total depth, but I could easily be mistaken.)
If Lazarus does not have a TPaintBox, then use a TImage which also has a Canvas property. Downside is that TImage "stores" all drawing operations in one big internal bitmap, which could raise memory or resource issues when your tree expands too much.
Could all images be combined to one large image? If so, then maybe this component answers your question.
It is a descendant of TGraphicControl, capable of animated zooming. Zoom in on a part of the graphic by dragging a selection rectangle, zoom out the whole graphic by double clicking it. Shoot if you need help to update it to be able to perform pan operations.

How can I copy TBitmap memory using with windows CopyMemory function

I have 1 bitmap object witdh : 1024px and height : 768 px
I want to cut this bitmap object to 2 part like left and right but I don't want to use DrawBitmap method in canvas because this method can use more CPU then CopyMemory.
I don't want to use this method ( leftImg.Canvas.DrawBitmap(MainBmp, RectF(0,0, MainBmp.Width div 2, bmp.Height),
RectF(0,0, leftImg.Width, leftImg.Height), 1, True); )
MainBmp := TBitmap.Create(1024, 768);
leftImg := TBitmap.Create(MainBmp.Width div 2, MainBmp.Height);
rightImg := TBitmap.Create(MainBmp.Width div 2, MainBmp.Height);
leftBits := PAlphaColorArray(leftImg.Scanline[0]);
CopyMemory(#leftBits[0], #MainBmp.StartLine[0], (MainBmp.Width div 2) * bmp.Height);
if I am doing like this he can copy but not left part of bitmap :( he copy half of top to bottom.
That drawing is exactly what I want to do.
After cut procces, i need like this without using any loop (like while or for)
Thanks
No can do! As you've found out image data is layout in the memory line by line (hence scanline). What you want could only be possible if it was column by column. Without any loops this is not possible.
As you noticed, a scanline is a row of pixels, from left to right. There is one scanline for each pixel of vertical height in the image.
Your 1024px x 768px images have 768 scanlines. Copying the first half of the data from scanlines yields you the top half of the image.
You wouldn't have to go through every pixel, you can skip ahead since everything is indexed.
However, since you want both halves, you're not wasting any work by going through the whole thing. As you iterate through the data, copy both the left and right parts out at the same time. So, for the first scanline, copy the first half of pixels to the left image and the rest of the pixels to the right image, go to the next line, and repeat.
This should be less work than DrawBitmap twice.
Also, rather than loading the image, displaying it, then splitting it, split it while you're loading the image.
You'll still need a loop, unless you want to write everything 768 times.
Technically, you could rotate the image and do it the way you want, but rotating it would require loops too, and you'd have to rotate it back when you're done.
Use the TCanvas.CopyRect() method to copy portions of one TCanvas to another TCanvas. It allows the two bitmaps to have different pixel formats. The OS will handle the differences internally for you:
MainBmp := TBitmap.Create(1024, 768);
leftImg := TBitmap.Create(MainBmp.Width div 2, MainBmp.Height);
rightImg := TBitmap.Create(MainBmp.Width div 2, MainBmp.Height);
leftImg.Canvas.CopyRect(
Rect(0, 0, leftImg.Width, leftImg.Height),
MainBmp.Canvas,
Rect(0, 0, leftImg.Width, leftImg.Height)
);
rightImg.Canvas.CopyRect(
Rect(0, 0, rightImg.Width, rightImg.Height),
MainBmp.Canvas,
Rect(leftBmp.Width, 0, rightImg.Width, rightImg.Height)
);

My preallocation of a matrix gives out of memory error in MATLAB

I use zeros to initialize my matrix like this:
height = 352
width = 288
nFrames = 120
imgYuv=zeros([height,width,3,nFrames]);
However, when I set the value of nFrames larger than 120, MATLAB gives me an error message saying out of memory.
The original function is
[imgYuv, S, A]= changeYuv(fileName, width, height, idxFrame, nFrames)
my command is
[imgYuv,S,A]=changeYuv('tilt.yuv',352,288,1:120,120);
Can anyone please tell me what's going on here?
PS: one of the purposes of the function is to load a yuv video which consists more than 2000 frames. Is there any possibility to implement that?
There are three ways to avoid the error
Process a limited number of
frames at any given time.
Work
with integer arrays. Most movies are
in 8-bit format, while Matlab
normally works with doubles.
uint8 takes 1 byte per element,
while double takes 8 bytes. Thus,
if you create your array as B =
zeros(height,width,3,nFrames,'uint8)`,
it only uses 1/8th of the memory.
This might work for 120 frames,
though for 2000 frames, you'll run
again into trouble. Note that not
all Matlab functions work for
integer arrays; you may have to
reimplement those that require
double.
Buy more RAM.
Yes, you (or rather, your Matlab session) are running out of memory.
Get out your calculator and find the product height x width x 3 x nFrames x 8 which will tell you how much memory you have tried to get in your call to zeros. That will be a number either close to or in excess of the RAM available to Matlab on your computer.
Your command is:
[imgYuv,S,A]=changeYuv('tilt.yuv',352,288,1:120,120);
That is:
352*288*120*120 = 1459814400
That is 1.4 * 10^9. If one object has 4 bytes, then you need 6GB. That is a lot of memory...
Referencing the code I've seen in your withdrawn post, your calculating the difference between adjacent frame histograms. One option to avoid massive memory allocation might be to just hold two frames in memory, instead of reading all the frames at once.
The function B = zeros([d1 d2 d3...]) creates an multi-dimensional array with dimensions d1*d2*d3*...
Depending on width and height, given the 3rd dimension of 3 and the 4th dimension of 120 (which effectively results in width*height*360), may result in a very huge array. There are certain memory limits on every machine, maybe you reached these... ;)

Resources