image buffer implementation in delphi - delphi

Context
I am drawing to a canvas, this is updated regularly and it flashes.
Logically thinking I assumed this is because my redraw method clears the Canvas then draws one element at a time to the canvas. so my idea was to write to a Timage then set the picture to the Timage.
Information
here is my code
procedure Tmainwindow.Button3Click(Sender: TObject);
var bufferpicture:TImage;
begin
//draw stuff to bufferpicture
//***
//draw stuff to bufferpicture
myrealpicture.picture:=bufferpicture.picture;
end;
Upon running the code I get a error show below.
Question
How do I set the canvas of one to another since canvas is a read only property? or is there a better way to do what i am trying to do?

It looks like you did not create myrealpicture
I would use the method Assign
MyRealPicture.Picture.Assign(BufferPicture.Picture);
You can copy the content of one canvas to another using BitBlt:
var
BackBuffer: TBitmap;
begin
BackBuffer := TBitmap.Create;
try
{ drawing stuff goes here}
BitBlt(Form1.Canvas.Handle, 0, 0, BackBuffer.Width, BackBuffer.Height,
BackBuffer.Canvas.Handle, 0, 0, SRCCOPY);
finally
BackBuffer.Free;
end;
end;
You can just use the DoubleBuffered property

use the DoubleBuffered property

Related

How to read a file in a TTask? (Delphi, fmx)

I need to read bitmap files and then copy them in an image component. I would like to do this in a TTask to keep the GUI responsive. If I run the minimized code below, then sometimes the apple image appears correctly and sometimes it appears without content (a grey square). If I put the bmp.LoadFromFile(..) in the TThread.Synchronize, then it seems to works fine, but is it necessary to put it in the TThread.Synchronize?
I do not understand why it does not work as is, because 1) if I look at the source code of LoadFromFile then the procedure starts with TMonitor.Enter(Self); and 2) when I save the bmp twice as commented out in the code, then the first save always contains the correct image (so the LoadFromFile seems to work), but the second save sometimes gives the correct image and sometimes a black image (but with the correct size)?
I am new to threading, so am hoping that someone can shed some light on this and what the correct way is to read images in a TTask. I am using Delphi 10.3.3.
Thanks,
Gerard
procedure TForm1.Button3Click(Sender: TObject);
begin
TTask.Run(procedure
var bmp: TBitmap;
begin
bmp := TBitmap.Create;
bmp.LoadFromFile('apple.bmp');
// bmp.SaveToFile('apple1.bmp');
// bmp.SaveToFile('apple2.bmp');
TThread.Synchronize(nil,
procedure
begin
Form1.Image1.bitmap.CopyFromBitmap(bmp, Rect(0, 0, bmp.Width, bmp.Height), 100, 100 );
end);
bmp.Free;
end);
end;

Delphi TImage32 - how to make the component invisible if no picture is loaded?

If you place a normal TImage component on a form or panel over other components >> it is invisible at runtime as long no picture is loaded. So other things under it are visible.
But TImage32 is painting a gray box by default.
How do I make the it invisible while leaving the setting: .Visible:=True; if no picture is loaded?
(I still need events working on the component, like OnClick...)
Yes, this is a duplicate question, BUT the solution-link from the previous topic is dead. :(
While I still have access to the newsgroup posts, I don't know how the topic ID relates to the topic title (which is all I have). However, based an a search in the newsgroup I found several places where TImage32Ex was mentioned. I guess that this component (which is not part of the core library) was part of the solution in some way.
So, while the extension pack where this component comes from is no longer maintained, let's dig deeper in what it did.
First, I must say that TImage32 will always paint (copy) the content of its buffer to the display. This means whatever graphic is behind this component will get overwritten by default.
The trick TImage32Ex does is to get the parents content and draws it into the buffer.
With adaptions the code looks like this
var
P: TPoint;
SaveIndex: Integer;
begin
SaveIndex := SaveDC(Buffer.Handle);
try
GetViewportOrgEx(Buffer.Handle, P);
SetViewportOrgEx(Buffer.Handle, P.X - Left, P.Y - Top, nil);
IntersectClipRect(Buffer.Handle, 0, 0, Parent.ClientWidth, Parent.ClientHeight);
Parent.Perform(WM_ERASEBKGND, Buffer.Handle, 0);
Parent.Perform(WM_PAINT, Buffer.Handle, 0);
finally
RestoreDC(Buffer.Handle, SaveIndex);
end;
end;
The above code draws (WM_PAINT) the parent's content onto the buffer.
For example if you want to make the TPaintBox32 instance called PaintBox32 to be transparent just add the following code to the 'PaintBuffer' handler:
procedure TForm3.PaintBox32PaintBuffer(Sender: TObject);
var
P: TPoint;
SaveIndex: Integer;
begin
SaveIndex := SaveDC(PaintBox32.Buffer.Handle);
try
GetViewportOrgEx(PaintBox32.Buffer.Handle, P);
SetViewportOrgEx(PaintBox32.Buffer.Handle, P.X - PaintBox32.Left, P.Y - PaintBox32.Top, nil);
IntersectClipRect(PaintBox32.Buffer.Handle, 0, 0, PaintBox32.Parent.ClientWidth, PaintBox32.Parent.ClientHeight);
PaintBox32.Parent.Perform(WM_ERASEBKGND, PaintBox32.Buffer.Handle, 0);
PaintBox32.Parent.Perform(WM_PAINT, PaintBox32.Buffer.Handle, 0);
finally
RestoreDC(PaintBox32.Buffer.Handle, SaveIndex);
end;
end;
Note: While this works basically, it might not capture the parent's sub controls properly. This is especially true for TWinControl descendants. While there are solutions around to cover this scenario as well, it's far more complicated to cover this in every detail (e.g. the blinking cursor of an underlying TEdit instance)
I use a timage to mask a progress bar and give it a shape.
What I do is load a png with parts that are transparent and then place it over my progress bar.
I think this should achieve your goal. Place a transparent png in your timage.
Cheers,
E.

BitBlt Printer.Canvas to a TBitMap displays as solid white

I am trying to capture Printer.Canvas as a Bitmap using BitBlt. I want to then take that Bitmap and display it on a paintbox. However, when I attempt this I am given only a white rectangle proportionate to the values I entered for Bitmap.SetSize. My printout looks correct, so I am almost positive the canvas of the printer is being properly drawn to. I attempted the following code using the variable bitmap as the destination and the paintbox as the source (in essence I was drawing a simple rectangle and line of text to the Paintbox, bitblt-ing it to a bitmap, clearing it, and then posting it back to the paintbox), but now that Printer.Canvas.Handle is the source it doesn't display.
I understand that the dimensions between the screen and printer are different so I will clearly indicate dimensions, just in case I am doing it wrong.
procedure TForm2.btnDrawClick(Sender: TObject);
begin
Printer.BeginDoc;
Printer.Canvas.Font.Size := 10; //Not Sure if this is necessary
Printer.Canvas.Font.Name := 'Arial'; //Not Sure if this is necessary
Printer.Canvas.Font.Color := clBlack; //Not Sure if this is necessary
Printer.Canvas.Rectangle(100,100,200,200); //Should print very tiny to paper
//But will look bigger when posted to
//The Paintbox
Printer.Canvas.TextOut(120,120,'XRay-Cat');
PCBitmap.SetSize(Paintbox1.Width,Paintbox1.Height); //Paint box is 300W,300H
Application.ProcessMessages;
BitBlt(PCBitmap.Canvas.Handle, //PCBitmap, is created on create, freed on destroy,
//Defined in the private section
0,
0,
PCBitmap.Width, //300
PCBitmap.Height, //300
Printer.Canvas.Handle,
0,
0,
SRCCOPY);
Application.ProcessMessages;
Printer.EndDoc;
procedure TForm2.btnPostBMClick(Sender: TObject);
begin
PaintBox1.Canvas.Draw(0,0,PCBitmap);
end;
I expect that the canvas would be written too, the canvas would be copied to a bitmap, then be available to be drawn on the paintbox. However all I see is a white rectangle. I am setting the dimensions of the bitmap to be the entire paintbox rather than the entire canvas of the printer. I am doing this because if I understand it correctly I should be only be drawing between the printer canvas's TopLeft 0,0 and BottomRight 300,300 the same way I would on my paint box. I would expect to see the same results as I would if I did this directly to the Paintbox.
Any help would be greatly appreciated. Thanks in advance.
Given the comments I've received it seems what I was trying to do was not possible. What I wanted to do was to write to a printers canvas and then get the image data of that canvas and store it in a bitmap. Since BitBlt can't be used is there a way to do what I wanted? I assume not, as I was told Printer.Canvas cannot be read from. At this point I have Found a way around it but I am just curious.
Switch your logic...draw to the PaintBox...and Print the PaintBox
procedure TForm55.Button1Click(Sender: TObject);
var
a_BM: TBitMap;
begin
a_BM := TBitmap.Create;
try
PaintBox1.Canvas.Font.Size := 10; //Not Sure if this is necessary
PaintBox1.Canvas.Font.Name := 'Arial'; //Not Sure if this is necessary
PaintBox1.Canvas.Font.Color := clBlack; //Not Sure if this is necessary
PaintBox1.Canvas.Rectangle(0,0,300,300); //Should print very tiny to paper
//But will look bigger when posted to
//The Paintbox
PaintBox1.Canvas.TextOut(120,120,'XRay-Cat');
PaintBox1.Width := 300;
PaintBox1.Height := 300;
a_BM.SetSize(PaintBox1.Width, PaintBox1.Height);
BitBlt(a_BM.Canvas.Handle, 0, 0, a_BM.Width, a_BM.Height, PaintBox1.Canvas.Handle, 0, 0, SRCCOPY);
Application.ProcessMessages;
Printer.BeginDoc;
Printer.Canvas.Draw(a_BM.Canvas.ClipRect.Left, a_BM.Canvas.ClipRect.Top, a_BM);
Printer.EndDoc;
Application.ProcessMessages;
finally
a_BM.Free;
end;
end;

How to load a transparent Image from ImageList?

I want to load a picture (32 bit-depth, transparent) from a TImageList to an TImage. The standard approach would be ImageList.GetBitmap(Index, Image.Picture.Bitmap);. However the GetBitmap method doesn't work with transparency, so I always get a non-transparent bitmap.
The workaround is rather simple - ImageList offers another method, GetIcon, which works OK with transparency. Code to load a transparent Image would be:
ImageList.GetIcon(Index, Image.Picture.Icon);
And don't forget to set proper ImageList properties:
ImageList.ColorDepth:=cd32bit;
ImageList.DrawingStyle:=dsTransparent;
I too have had various issues with passing in images from the a tImageList. So I have a simple wrapper routine that generally does the job and it enforces the transparency. The code below is Delphi 2005 and imlActiveView is the tImageList component that has my set of button glyph images.
procedure TfrmForm.LoadBitmap (Number : integer; bmp : tBitMap);
var
ActiveBitmap : TBitMap;
begin
ActiveBitmap := TBitMap.Create;
try
imlActiveView.GetBitmap (Number, ActiveBitmap);
bmp.Transparent := true;
bmp.Height := ActiveBitmap.Height;
bmp.Width := ActiveBitmap.Width;
bmp.Canvas.Draw (0, 0, ActiveBitmap);
finally
ActiveBitmap.Free;
end
end;
Here is an example of use where the 5th imlActiveView image is passed into the btnNavigate.Glyph.
LoadBitmap (5, btnNavigate.Glyph)

Setting up background images for forms in Delphi

How can I make my program load an image and make it the background for a form?
I need the exact code for it. I've looked all over the internet and the only things I've found are various tweaks and fixes to make backgrounds work as intended in special circumstances. I've also tried some Delphi books I have and I can't find it anywhere.
Put a TImageon your form. Make sure it's behind all other controls on the form. You can right-click it and choose the "send to back" menu option.
Load a graphic.
var
img: TBitmap;
begin
img := TBitmap.Create;
try
img.LoadFromFile('S:\background.bmp');
Assign it to the image control.
Image1.Picture := img;
Clean up.
finally
img.Free;
end;
end;
You can also combine the last three steps to load the graphic and put it in the image control all at once. Thanks to Jon for the suggestion.
Image1.Picture.LoadFromFile('B:\background.bmp');
See also: How to add background images to Delphi forms
What I would do is use the forms OnPaint event, get the canvas (Form1.Canvas), and then use the Draw method (which takes an image) to draw the image you want. Something like the following:
procedure TForm1.FormPaint(Sender: TObject);
var
mypic: TBitMap;
begin
mypic := TBitMap.Create;
try
mypic.LoadFromFile('cant.bmp');
Form1.Canvas.Draw(0, 0, mypic);
finally
FreeAndNil(mypic);
end;
end;
Note that this could be extremely slow.
This is the way all my applications show a form image. I load the image at form creation or when the application calls a specific showing event
var
vDest, vRect: TRect;
begin
vRect := Rect(0, 0, FBackgroundImage.Width, FBackgroundImage.Height);
vDest := Rect(0,0,Self.Width, Self.Height);
Canvas.StretchDraw(vDest, FBackgroundImage);
if FileExists(this) then
FBackgroundImage.LoadFromFile(this);
#Brendan
thanks
//from Brendan code;
var
vDest, vRect: TRect;
FBackgroundImage: TGraphic;
begin
FBackgroundImage := image1.Picture.Graphic; //LOAD from invisible image
vRect := Rect(0, 0, FBackgroundImage.Width, FBackgroundImage.Height);
vDest := Rect(0,0,Self.Width, Self.Height);
Canvas.StretchDraw(vDest, FBackgroundImage);
end;

Resources