bobD 6 Posted August 8, 2021 (edited) I'm working on a quick photo browsing app, using a TObjectDictionary<string, TJPEGImage> as a cache device. Images are requested for display using the path to the jpg file like this: procedure TJpgStore.LoadPicture(aPicture: TPicture; const aSource: string); var img : TJPEGImage; begin if not FDict.TryGetValue(aSource, img) then begin img := TJPEGImage.Create; img.Scale := jsEighth; img.Performance := jpBestSpeed; img.LoadFromFile(aSource); FDict.Add(aSource, img); end; aPicture.Assign(img); end; This all seems to work fine, but two questions (since I'm not really a graphics guy): Am using Scale and Performance correctly? IOW, does it make any difference being here or after the LoadFromFile call? How much memory does a loaded TJPGImage take up? ('m thinking about a least-recently-used cache size governor. The jpgs are commonly 15-25MB each, and a source directory might contain 1-5,000 picture, so caching them all doesn't seem realistic...) Those are specific questions, but glad to entertain other comments/suggestions/alternative approaches. Thanks, bobD Edited August 8, 2021 by bobD Share this post Link to post
David Heffernan 2353 Posted August 8, 2021 If you are going to cache them, cache the jpeg stream rather than the decoded image. Then decode the image on demand with LoadFromStream. Share this post Link to post
bobD 6 Posted August 8, 2021 59 minutes ago, David Heffernan said: If you are going to cache them, cache the jpeg stream rather than the decoded image. Then decode the image on demand with LoadFromStream. I can do that. Is that suggested for performance or memory footprint? It bugs me that I can't see what I'm doing. InstanceSize returns 100 bytes for the img, 24 bytes for the stream. Obviously neither really accounts for the data. bobD Share this post Link to post
David Heffernan 2353 Posted August 9, 2021 7 hours ago, bobD said: performance or memory footprint? Memory footprint. InstanceSize is just the instance and not the dynamically allocated data it owns. Share this post Link to post
dummzeuch 1517 Posted August 9, 2021 (edited) 13 hours ago, bobD said: I can do that. Is that suggested for performance or memory footprint? It bugs me that I can't see what I'm doing. InstanceSize returns 100 bytes for the img, 24 bytes for the stream. Obviously neither really accounts for the data. To get the size of a memory stream, add InstanceSize and Capacity. Unfortunately capacity is usually larger than the actual size of the data and of I remember correctly, TMemoryStream does not allow setting the capacity beforehand. But you can easily add that functionality. I think I've got such a class in my dzlib. Setting the size also sets the capacity, so If you know the expected size beforehand, set it first before writing to the stream to avoid allocating more memory than necessary. I have no idea how to calculate the memory used by a TJpegImage. If you want better performance, don't use TJpegImage for decoding but LibJpeg-turbo. I haven't measured it, but subjectively the difference is staggering. Edited August 9, 2021 by dummzeuch Share this post Link to post
Fr0sT.Brutal 900 Posted August 9, 2021 21 hours ago, bobD said: How much memory does a loaded TJPGImage take up? ('m thinking about a least-recently-used cache size governor. The jpgs are commonly 15-25MB each, and a source directory might contain 1-5,000 picture, so caching them all doesn't seem realistic...) Save an image as bitmap, then you'll get an idea how much it takes in memory. Share this post Link to post
mvanrijnen 123 Posted August 9, 2021 (edited) The TJPGImage has a TBitmap in it which will take the memory i think. This is a simple calculation, width,height,bits/pixel or am i mistaken? mistaken 🙂 Edited August 9, 2021 by mvanrijnen Share this post Link to post
bobD 6 Posted August 9, 2021 6 hours ago, mvanrijnen said: The TJPGImage has a TBitmap in it which will take the memory i think. This is a simple calculation, width,height,bits/pixel or am i mistaken? mistaken 🙂 In what regard? What I know so far is this: the lines img.Scale := jsEighth; img.Performance := jpBestSpeed; speed up the load/display of the jpg file as advertised Using the following code, bmp := TBitMap.Create(img.Width, img.Height); bmp.Assign(img); bmp.SaveToFile('C:\Users\bobd\Desktop\TestBMP.bmp'); and starting with a jpg file of 15.4 MB results in a bmp file of about 1.14 MB if Scale and Performance are so specified, or with the lines commented out 73MB (!). That suggests to me that there's no direct calculation of the JPGImage size from the bitmap it produces. You can see either a > 10x reduction, or a 4-5x expansion. Interestingly though, the TJPGImage object saving itself to file after the scale and performance are reduced for load results in a file the same size as the original. So we can't assume that the TJPGImage so loaded is any smaller in memory that one loaded without the jsEighth reduction. That's interesting. It appears to me that the most memory efficient cache (and thus the one with the largest practical capacity) for this app is going to be loading at jsEighth, then creating and caching the bitmap. Since the original image can be 6192 x 4192, I don't really need all that resolution anyway. Unfortunately, this presumes something not in evidence: that the size of the TBitmap in memory has a direct relationship to its file size. As with the source TJPGImage, it doesn't have an accessible size-in-memory property. Share this post Link to post
dummzeuch 1517 Posted August 10, 2021 (edited) 9 hours ago, bobD said: It appears to me that the most memory efficient cache (and thus the one with the largest practical capacity) for this app is going to be loading at jsEighth, then creating and caching the bitmap. If you only need thumbnails, why not explicitly scale your picture to a bitmap of whatever size you need and cache that? Of course that takes time. Edited August 10, 2021 by dummzeuch Share this post Link to post
Fr0sT.Brutal 900 Posted August 10, 2021 (edited) 9 hours ago, bobD said: It appears to me that the most memory efficient cache (and thus the one with the largest practical capacity) for this app is going to be loading at jsEighth, then creating and caching the bitmap. Since the original image can be 6192 x 4192, I don't really need all that resolution anyway. Unfortunately, this presumes something not in evidence: that the size of the TBitmap in memory has a direct relationship to its file size. As with the source TJPGImage, it doesn't have an accessible size-in-memory property. Solving similar issue in my map control,. I came to 3-level cache: - Hot: Tbitmaps - Medium: PNGImages - Cold: TMemoryStreams Capacity of all levels is limited by memory and GDI handles. Comments in OSM.TileStorage.pas unit explain decisions I made Edited August 10, 2021 by Fr0sT.Brutal 1 Share this post Link to post
bobD 6 Posted August 10, 2021 13 hours ago, Fr0sT.Brutal said: Solving similar issue in my map control,. I came to 3-level cache: - Hot: Tbitmaps - Medium: PNGImages - Cold: TMemoryStreams Capacity of all levels is limited by memory and GDI handles. Comments in OSM.TileStorage.pas unit explain decisions I made That encourages me that I'm on the right track with bitmaps. I'll report back with some further testing. Share this post Link to post