How to speed up Drawing of DrawGrid Cells (Delphi) - performance

I have a DrawGrid with 600 x 600 Cells.
Each Cell gets painted with a color dependig on its Value.
Displaying the Grid needs ~1 second.
Question is: how can this be improved, so drawing needs less time?
The OnDrawCell Event looks like:
(MyVal is the Col/Row value and is taken from an 2 dimensional Array)
...
MyVal := MyArray[ACol, ARow];
case MyVal of
0: Drawgrid1.Canvas.brush.Color := clRed;
1: Drawgrid1.Canvas.brush.Color := clBlue;
...
end;
Drawgrid1.Canvas.Brush.Style := bsSolid;
Drawgrid1.Canvas.fillrect(Rect);
Thank you.
Klaus

Related

FMX ViewPort3D performance drop when filled with a large number of 3D shapes

I have a ViewPort3D element in a multi-device application form that being filled with a large number of TRectangle3D elements (anywhere from 1 to 10000) with LightMaterialSource applied to them, which all need to be rendered dynamically since I'm also rotating the camera using the following procedure:
procedure TForm3.Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
var
I: IViewport3D;
begin
if ssRight in shift then
begin
I:=ViewPort3D1;
with tdummy(I.CurrentCamera.Parent) do RotationAngle.X:=RotAng.X - Y;
with tdummy(I.CurrentCamera.Parent.Parent) do RotationAngle.Y:=RotAng.Y + X;
end;
end;
However the performance of the ViewPort3D begins to drop noticeably when the number of rectangles rendered approaches at least several dozen. The camera rotation gets slower and more unresponsive the more rectangles are added to the viewport up to the point of becoming a slideshow.
Is there a way to improve performance of the ViewPort3D without deleting said rectangles?
I've tried using setting the Multisample property to "none": ViewPort3d1.Context.SetMultisample(TMultisample.None) as well as removing MaterialSource from all the rectangles. While it did help a little with the performance, it didn't solve the problem entirely.
For 10k in my tests
Context.Draw/Fill.cube
Drawing in the render event is much more efficient, but the performance drops significantly when the camera angle is changed.
TRectangle3D Creating 10k pieces turns into serious performance problems after 2k created and showed on windows.
if visible=false is set after TRectangle3D create, rendering is completed at very high speed,
however, when visible=true, there is a serious performance degradation when the camera angle is changed.
As far as I can see, the reason for the slowdown is the operations on the CPU, that is, the gpu is not the part that slows down here, but when I examine the codes, there is a constant high amount of event type message notifications going to on mousemove to objects.
my suggestion is if a lot of visible objects are going to be used,
if objects are out of camera view, hide objects(visible=false), hide objects with a loop at each mousemove event, see if objects are in camera area or not, this will help performance.
There is not only 10k object problem here, it has many shortcomings.
basically this one can be used as a simple drawing 3d engine for simple jobs.
As far as I know, in Java, just like the viewport3d object, we can add the unity engine graphical window to a form in a java application,
I don't know if unity engine graphics window can be added to delphi,
u can request the support from embercadero, but for advanced graphics it would be more logical to use engines like unreal unity proffesional optimized engines.
Each high level fmx 3d object make a "drawcall." (wich reprocess all, from cpu (mesh preparation) to gpu (for diplay)
-> You always have to minimize drawcall count.
So, displaying 10000 FMX rectangles is never a solution. :)
You have to make only 1 Tmesh descendant, witch will draw your 10000 "hand-made" rectangles in one call.
Please see TMesh.Data (data.points and data.triangleindice) to undestand how to draw a 3d object in direct mode. Or, more simply, see how "TPlane" (in source) is built.
As a general plan, and as a basic "3d making" approach, making "complex" 3d FMX object is possible, but you have to deal with vertices/indexes to draw much possible things in one call.
As an exemple, here a is the code to realize it :
put TMesh on your viewport, and call this proc :
Procedure PopulateMesh_RectangleMap(aMesh : TMesh; const xCount : integer = 100; const yCount : integer=100; const rectWidth : single = 2.0; const rectHeight : single= 1.0);
var lv,li : integer;
lsx, lsy, xpos, ypos : single;
i,j : integer;
a,b,c,d : TPoint3d; //4 corner of a rect.
begin
Assert(assigned(aMesh));
lsx := rectWidth;
lsy := rectHeight;
li := 0;
lv := 0;
//mem. allocation.
aMesh.Data.Clear;
aMesh.Data.VertexBuffer.Length := 4 * xCount * yCount; //4 vertices by rect, need k*k rects.
aMesh.Data.IndexBuffer.Length := 6 * xCount * yCount; // 6 indices for 2 triangles desc (to make 1 rect) for k*k rects.
for i := 0 to xcount-1 do
for j := 0 to ycount-1 do begin
xpos := -xcount/2 + i + i*lsx;
ypos := -ycount/2 + j + j*lsy;
a := point3d(xpos - lsx/2,ypos - lsy/2,0);
b := point3d(xpos + lsx/2,ypos - lsy/2,0);
c := point3d(xpos + lsx/2,ypos + lsy/2,0);
d := point3d(xpos - lsx/2,ypos + lsy/2,0);
aMesh.Data.VertexBuffer.Vertices[li+0] := a;
aMesh.Data.VertexBuffer.Vertices[li+1] := b;
aMesh.Data.VertexBuffer.Vertices[li+2] := c;
aMesh.Data.VertexBuffer.Vertices[li+3] := d;
aMesh.Data.IndexBuffer.Indices[lv+0] := li+0;
aMesh.Data.IndexBuffer.Indices[lv+1] := li+1;
aMesh.Data.IndexBuffer.Indices[lv+2] := li+2;
aMesh.Data.IndexBuffer.Indices[lv+3] := li+2;
aMesh.Data.IndexBuffer.Indices[lv+4] := li+3;
aMesh.Data.IndexBuffer.Indices[lv+5] := li+0;
inc(li,4);
inc(lv,6);
end;
aMesh.Data.BoundingBoxNeedsUpdate; //We touch data. Update Mesh container.
aMesh.Width := lsx*3; //keep proportion.
aMesh.Height := lsy*3;
aMesh.Repaint;
end;

Drop Shadow with Golang based on alpha

I need to drop shadow from an object of type image.Image which has got an alpha channel. The goal is not to have the rectangle shadowed, but the alpha.
What I wanted to do is:
take out the alpha channel
turn it into a black RGB image
expand to N pixels (N=the size of the blur)
apply a gaussian blur
apply it as a background
The last point is quite simple, thanks to the "image/draw" API (I have no problem to cut an image in circle, and apply the mask, for example).
Outputting the alpha channel seems simple, too (for each pixel, apply a 255*alpha multiplication on R, G, and B, or use a grayscale image, and finally invert the white color to black)
It's clearly the dilation and blurring that I have a problem with.
I have nothing against the fact that the image changes size for this operation (at worst I will reduce the original image before transformation)
The question is "how to dilate and blur the alpha channel" with Go on an image.Image?
OK, after a while, I finally made this.
I rebuild the alpha and devide it by 2 (this will be a paramters).
I'm using github.com/disintegration/imaging package to blur the alpha.
func DropShadow(img image.Image, size float64) image.Image {
bounds := img.Bounds()
sizeInt := int(math.Ceil(size)) * 4
final := imaging.New(bounds.Dx()+sizeInt, bounds.Dy()+sizeInt, color.Alpha{})
for x := 0; x < bounds.Dx(); x++ {
for y := 0; y < bounds.Dy(); y++ {
_, _, _, a := img.At(x, y).RGBA()
final.Set(x+sizeInt/2, y+sizeInt/2, color.RGBA{0x0, 0x0, 0x0, uint8(a / 2)})
}
}
final = imaging.Blur(final, size)
final = imaging.Overlay(final, img, image.Point{sizeInt / 2, sizeInt / 2}, 1)
return final
}
It's only a bit curious that I need to scale by 4 the image to not have the shadow sticking to the border. But it does the job...

DrawFocusRect function

In a picture, I want to draw a selection area with a circular shape. I used to draw rectangular shape but never dealt with any other. Is it possible to be done ? I am coding in Delphi
DrawFocusRect() supports rectangles only. For other shapes, you will have to manually draw them yourself as desired, such as with Ellipse() with an appropriate Brush and Pen.
To have similar appearance and behavior with DrawFocusRect(), use an alternating and XOR pen. E.g.:
var
Brush: TLogBrush;
begin
Brush.lbStyle := BS_SOLID;
Brush.lbColor := clBlack;
Canvas.Pen.Handle := ExtCreatePen(PS_COSMETIC or PS_ALTERNATE, 1, Brush, 0, nil);
Canvas.Pen.Style := psAlternate;
Canvas.Pen.Mode := pmNotXor;
Canvas.Brush.Style := bsClear;
Canvas.Ellipse(...

Make only one round corner antialiased to an image

everyone
I've been trying to solve the following problem in Delphi.
I want to make take an image and to make just one antialiased round corner.
I know how to make all 4 corners round by using RoundRect. However, I don't seem to figure out how to make just one.
I've been trying to solve the problem like this:
procedure RoundCorner(var image: TBitmap; w,h : integer);
//w - width of an image
//h - height of an image
//radius can be set at 150 (rounded rect radius)
// image is the timage object received as var parameter
var
i, j :integer;
x, y :double;
begin
i:= w - Trunc(radius);
x:= 0;
y:= radius - sqrt(sqr(radius) - sqr(x));
while(i < w) do
begin
j:=0;
while(j <= y) do
begin
image.Canvas.Pixels[i-1,j]:=clWhite; //a colour of your choosing or just erase this line
j := j + 1;
end;
y:= radius - sqrt(sqr(radius) - sqr(x));
x := x + 1;
i := i + 1;
end;
end;
This works, however I'm faced with 2 problems:
The corner is not antialiased
I want to fill the cut region with a different colour
Any suggestions are welcomed
Thanks.
First of all in your case "antialiasing" does mean that your image have to have a 32 bit pixel format in order to make a round transparent corner. Second thing is that you cannot access alpha channel via Canvas, you would need to access bitmap pixels directly via Scanline property or maybe to use a library like Graphics32 or AGG.
Another "modern" way to implement round corner is to use FMX and put the image inside a roundrect and clip it by the roundrect.

Modify per-pixel alpha data for TPngImage

Can I directly modify per-pixel alpha data for TPngImage between loading it from somewhere and drawing it somewhere? If so, how? Thanks.
Yes, I think that is easy.
For example, this code will set the opacity to zero (that is, the transparency to 100 %) on every pixel in the upper half of the image:
var
png: TPNGImage;
sl: PByteArray;
...
for y := 0 to png.Height div 2 do
begin
sl := png.AlphaScanline[y];
FillChar(sl^, png.Width, 0);
end;
This will make a linear gradient alpha channel, from full transparency (alpha = 0) to full opacity (alpha = 255) from left to right:
for y := 0 to png.Height do
begin
sl := png.AlphaScanline[y];
for x := 0 to png.Width - 1 do
sl^[x] := byte(round(255*x/png.Width));
end;
Basically, what I am trying to say, is that
(png.AlphaScanline[y]^)[x]
is the alpha value (the opacity), as a byte, of the pixel at row y and col x.
You could use something like this:
for Y := 0 to Image.Height - 1 do begin
Line := Image.AlphaScanline[Y];
for X := 0 to Image.Width - 1 do begin
Line[X] := ALPHA
end;
end;

Resources