Guest Posted December 13, 2023 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
Dalija Prasnikar 1396 Posted December 13, 2023 (edited) 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 December 13, 2023 by Dalija Prasnikar 4 Share this post Link to post
Guest Posted December 16, 2023 (edited) 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 December 16, 2023 by Guest Share this post Link to post
Remy Lebeau 1396 Posted December 16, 2023 (edited) 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 December 16, 2023 by Remy Lebeau 2 Share this post Link to post
Guest Posted December 17, 2023 (edited) 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 December 17, 2023 by Guest Share this post Link to post
Remy Lebeau 1396 Posted December 17, 2023 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. 1 Share this post Link to post
Guest Posted December 24, 2023 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
Remy Lebeau 1396 Posted December 25, 2023 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