aehimself 396 Posted May 25, 2020 Hello, I'm in the process of fixing some unusual amount of memory leaks and code errors in a legacy codebase. We have a custom TDBImage descendant, which was leaking TJPEGImages like there's no tomorrow. The code used to look like this: constructor TMyDBImage.Create(AOwner: TComponent); begin […] Picture.Graphic := TJPEGImage.Create; […] end; procedure TMyDBImage.DataChange(Sender: TObject); begin Picture.Graphic := TJPEGImage.Create; FPictureLoaded := False; if FAutoDisplay and (DataSource.DataSet.State <> dsInactive) then LoadPicture; end; procedure TMyDBImage.LoadPicture(ARefreshDataSet: Boolean = False); begin […] if not DataSource.DataSet.EOF and DataLink.Field.IsBlob then Picture.Graphic.Assign(DataLink.Field) else Picture.Graphic := TJPEGImage.Create; // Empty picture end; I guess they wanted to clear the image, so I changed all those TJPEGImage.Create-s with a simple nil assignments (Picture.Graphic := nil). I also changed the loading mechanism: procedure TMyDBImage.LoadPicture(ARefreshDataSet: Boolean = False); Var jpeg: TJPEGImage; begin [...] if not DataSource.DataSet.EOF and DataLink.Field.IsBlob then Begin jpeg := TJPEGImage.Create; Try jpeg.Assign(DataLink.Field); Picture.Assign(jpeg); // Picture.Bitmap.Assign(jpeg); This does not work either Finally jpeg.Free; End; End else Picture.Graphic := nil; // Empty picture end; The leaks are gone, but the picture is not shown in the component. I already confirmed that "jpeg" is filled correctly (stop with debugger, execute a .SaveToFile and check the result) but I can not really make the DBimage's Picture to load it. Based on my previous codes the Picture.Bitmap.Assign should be fine; I guess in this case it's the nil assignment of the .Graphic property which is not letting it to work? Where is my logic wrong? Thanks! Share this post Link to post
Attila Kovacs 629 Posted May 25, 2020 you changed Picture.Graphic.Assign(DataLink.Field) to Picture.Assign(...); ? Share this post Link to post
aehimself 396 Posted May 25, 2020 Yep. As I'm .nil-ing that, it'd throw an AV. The loading has been re-written, and is now: Picture.Assign(jpeg); Share this post Link to post
Attila Kovacs 629 Posted May 25, 2020 Ah, I see, but, Picture.Assign in this case calls TGraphic.Assign(Source) where source is your "jpeg", and it assigns by reference, so you must not free it, it will be free'd on reassigning or destroying the component. As far as I can see in the sources. Share this post Link to post
aehimself 396 Posted May 25, 2020 Good idea. Commented the .Free out: jpeg := TJPEGImage.Create; Try jpeg.Assign(DataLink.Field); Picture.Assign(jpeg); Finally // jpeg.Free; End; Bad thing is, it still does not work (picture isn't shown) and it leaks a TBitmapImage, a TJPEGImage, a TBitmap, a TJPEGData and a TMemoryStream object upon destruction 😞 Share this post Link to post
Uwe Raabe 2057 Posted May 25, 2020 1 hour ago, aehimself said: Picture.Graphic := TJPEGImage.Create; You should be aware that assigning to TPicture.Graphic will create an internal copy of the source (see TPicture.SetGraphic), so you get a memory leak with the above code. As TPicture.Assign also uses SetGraphic internally, the above ist still valid for that one. Not refreshing the display may have a different cause. Share this post Link to post
Anders Melander 1782 Posted May 25, 2020 1 hour ago, Uwe Raabe said: You should be aware that assigning to TPicture.Graphic will create an internal copy of the source (see TPicture.SetGraphic), so you get a memory leak with the above code. Which the TPicture.Graphic help also clearly state: Quote Note: When assigning the Graphic property, TPicture assigns the properties of a another TGraphic object. It does not take ownership of the specified object. ...and the TPicture.Assign help is also pretty clear: Quote Copies one object to another by copying the contents of that object to the other. Share this post Link to post
aehimself 396 Posted May 25, 2020 1 hour ago, Uwe Raabe said: You should be aware that assigning to TPicture.Graphic will create an internal copy of the source (see TPicture.SetGraphic), so you get a memory leak with the above code. I know. This is why I am attempting to get rid of it. 1 hour ago, Uwe Raabe said: Not refreshing the display may have a different cause. Well, it is working if we are leaking TJPEGImages, which I find really strange. Also attempting to Self.Invalidate / Self.Repaint / Self.Refresh after loading the picture makes no difference. Share this post Link to post
aehimself 396 Posted May 26, 2020 If any future visitor is wondering, a simple Picture.Assign(DataLink.Field); solved the problem, there was no need for a middle TJPEGImage. It's still a mystery for me though, why it is not working with a TJPEGImage and working without. Especially since they were all loaded correctly. Especially since the above works fine with a normal TImage, but not from inside a TDBImage descendant. Share this post Link to post
Guest Posted May 26, 2020 2 hours ago, aehimself said: It's still a mystery for me though, why it is not working with a TJPEGImage and working without. Especially since they were all loaded correctly. I think i saw something similar in the past, but didn't to throw some wild guess, now after you solved this, can you try to call DIBNeeded to enforce the generation of internal bitmap, may be something is wrong in one of those assign(s), one seems to be skipping the bitmap assigning, can you confirm ? of course if you are willing to waste some time on that, for public knowledge 🙂 Share this post Link to post
aehimself 396 Posted May 27, 2020 @Kas Ob. Often the experience gathered during the journey is more valuable than reaching your destination. Noone will actually see that I worked on this component, but I'm richer with a bit of knowledge. Yes, I would be more than willing to check your idea, however... Today, when I restored the faulty TJPEGImage assignments, it simply started to work...?! Noone else touched this code, the image is the same. Yesterday it was an empty white rectangle, today the image shows up... Share this post Link to post
Guest Posted May 27, 2020 42 minutes ago, aehimself said: Today, when I restored the faulty TJPEGImage assignments, it simply started to work...?! Noone else touched this code, the image is the same. Yesterday it was an empty white rectangle, today the image shows up... I saw this behaviour few times, it does happen with PNG and virtual image lists, couldn't track it to specific reason. Share this post Link to post
aehimself 396 Posted May 27, 2020 1 hour ago, Kas Ob. said: I saw this behaviour few times, it does happen with PNG and virtual image lists, couldn't track it to specific reason. This makes no sense to me. I was in the belief that a code either works or not. Unless if it's a problem with TDBImage and/or VCL of course; but that is out of my reach anyway. If I can manage to reproduce the issue somehow, I will attempt to fix it with DIBNeeded and will not forget to share the result. Share this post Link to post
Guest Posted May 27, 2020 11 minutes ago, aehimself said: This makes no sense to me. True, yet there is the OS intraction with its messages, this for me it was more reproducible on Windows 8, later it was harder to reproduce in 8.1 so may be still something is missing in Windows 10, How to reproduce: leave your Delphi built application running and switch users on Windows, if the application is doing redraw images when you are on different user ( like loading compressed from resources or files then update the UI), it might happen and you see that the images are blank sqaures, i saw that with PNG files. Share this post Link to post