Jump to content
Guest

How to keep hdc alive

Recommended Posts

Guest

I am using .canvas bitmap handle to do a basic task with GDI routines but when I split the task into two steps only the first is done, the second one fails because hdc becomes invalid.. in the second operation I can't provide hdc again and the objects created by the first step still refer to the old hdc.

Share this post


Link to post
17 minutes ago, Ledklom said:

I am using .canvas bitmap handle to do a basic task with GDI routines but when I split the task into two steps only the first is done,

VCL implements "garbage collection" for unused device contexts and it periodically releases them. To prevent that you need to call Lock on Canvas, and call Unlock when you no longer need the same device context.

 

But, without seeing your code it is hard to say whether there is more going on.

Edited by Dalija Prasnikar
  • Like 4

Share this post


Link to post
Guest

Tanks for your response, currently I use this code, it works as expected but  not sure if implemented correctly since I don't know how TBitmap & TCanves work internally
In th current code the bitmap"s size is updated whenever the client area of the control changes and assigns a new device context to Canvas.handle to replace the original if existed ...what I don't understand, how to prevent copying data from the old bitmap to the new one, this operation isn't necessary in my case

 

procedure TTextPanel.WMSize(var Msg: TMessage);
begin
  inherited;
  if Dc <> 0 then
  begin
    SelectObject(Dc, OldBMP);
    DeleteDC(Dc);
    Dc := 0;
  end;

  with ClientRect do
    FBmp.SetSize(Width, Height);

  Dc := CreateCompatibleDC(0);
  OldBMP := SelectObject(Dc, FBmp.Handle);
  FBmp.Canvas.Handle := Dc;
  
  UpdateData;
end;

 

Edited by Guest

Share this post


Link to post
41 minutes ago, Ledklom said:

currently I use this code, it works as expected but  not sure if implemented correctly since I don't know how TBitmap & TCanves work internally

Why are you creating an HDC to begin with?  TBitmap already has its own HDC that it manages for you.  Just draw on your TBitmap whenever you need to.  What are you trying to accomplish exactly by managing your own HDC manually?

41 minutes ago, Ledklom said:

In th current code the bitmap"s size is updated whenever the client area of the control changes and assigns a new device context to Canvas.handle to replace the original if existed

You don't need that 2nd step.

41 minutes ago, Ledklom said:

how to prevent copying data from the old bitmap to the new one

You can't.  Resizing a bitmap will destroy its old data.  But TBitmap will handle copying the old data into the new memory for you.  You would have had to do that yourself with a manual HDC, so better to just let TBitmap do it for you.

Edited by Remy Lebeau
  • Like 2

Share this post


Link to post
Guest
Quote

TBitmap already has its own HDC that it manages for you.  Just draw on your TBitmap whenever you need to.  What are you trying to accomplish exactly by managing your own HDC manually?

But I have to use the same HDC as said above: first the code processes the text based on the selected HDC by ScriptStringAnalyse and then when the paint event occurs the object returned by ScriptStringAnalyse is sent  to ScriptStringOut to be drawn, so the second operation   still refer to the oringinal HDC that no longer exists.

 

Quote

To prevent that you need to call Lock on Canvas, and call Unlock when you no longer need the same device context.

Indeed, locking canvas prevents its destruction, when I lock the canvas in the constructor it works as expected.

Thanks

Edited by Guest

Share this post


Link to post
6 hours ago, Ledklom said:

But I have to use the same HDC as said above

But you didn't explain earlier WHY you need to preserve the HDC, only that you want to use it across "two tasks", without any example or explanation of what those tasks actually are.  And, as Delija already mentioned, you can simply Lock the TBitmap's Canvas to prevent the VCL from releasing the HDC automatically.  Just make sure you Unlock it before you resize or free the TBitmap, and re-Lock it again after resizing.

6 hours ago, Ledklom said:

first the code processes the text based on the selected HDC by ScriptStringAnalyse and then when the paint event occurs the object returned by ScriptStringAnalyse is sent  to ScriptStringOut to be drawn, so the second operation   still refer to the oringinal HDC that no longer exists.

That makes no sense.  If you are using the TBitmap's HDC for both functions, then there is no need to wait for a paint event to draw the string onto the TBitmap.  Since you are not drawing the string onto a display HDC directly, there is no need to wait for a paint event.  You can just draw onto the TBitmap whenever you want, since it is all just in-memory data.  If you want to draw the TBitmap onto a display Canvas so the user can see the image, that certainly needs to be done in a paint event, but that does not mean you have to draw onto the TBitmap itself in the paint event.  And in fact, you shouldn't do it that way.  Prepare the TBitmap image ahead of time, and then just draw the TBitmap as-is whenever a paint event occurs, and then invalidate the display window whenever the TBitmap image changes so a new paint event can display the updated image.  And this way, you don't even need the TBitmap's HDC to be persistent over time, you only need it when you are updating your image.

 

For example:

type
  TTextPanel = class(...)
  private
    procedure BmpChanged(Sender: TObject);
    procedure UpdateData;
  protected
    procedure Paint; override;
    procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TTextPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FBmp := TBitmap.Create;  
  FBmp.OnChange := BmpChanged;
end;

destructor TTextPanel.Destroy;
begin
  FBmp.Free;
  inherited Destroy;
end;

procedure TTextPanel.BmpChanged(Sender: TObject);
begin
  Invalidate;
end;

procedure TTextPanel.Paint;
begin
  inherited Paint;
  ...
  Canvas.Draw(0, 0, FBmp);
  ...
end;

procedure TTextPanel.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
  inherited SetBounds(ALeft, ATop, AWidth, AHeight);
  FBmp.SetSize(Width, Height);
  UpdateData;
end;

procedure TTextPanel.UpdateData;
begin
  // update FBmp image as needed...
end;

I think you are making this task harder than it really needs to be.  If you think I'm wrong, feel free to provide an example demonstrating exactly what you are trying to accomplish.

  • Like 1

Share this post


Link to post
Guest
On 12/17/2023 at 7:08 PM, Remy Lebeau said:

That makes no sense.  If you are using the TBitmap's HDC for both functions,

Merci pour votre réponse ...

Comme je l'avais bien précisé dans mon premier message quand j'exécute le code en une seule passe ça marche  sans problème, au moment ou j'avais écrite le code je savais le point que tu évoques le Bitmap n'affiche que la partie visible tu texte sinon il existe plains d'autres APIs windows qui puissent contourner le problème, j'ai besoin de récupérer les informations sur le positionnement et le déplacement du caret et ces informations ne sont disponibles que via ScriptStringAnalyse .

Share this post


Link to post

This is an English forum, please.

 

I'm not familiar with the ScriptStringAnalyze API or how it works, so I can't help you further with this.  I don't understand what you are trying to accomplish.  Good luck with your journey.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×