ButtonImageGadget - SetGadgetAttribute doesn't copy image

Just starting out? Need help? Post your questions and find answers here.
bytecave
User
User
Posts: 40
Joined: Mon Aug 13, 2012 7:26 am

ButtonImageGadget - SetGadgetAttribute doesn't copy image

Post by bytecave »

When setting the image of a ButtonImageGadget via SetGadgetAttribute, the underlying image is not copied into the gadget, but instead just the handle to the image is saved in the gadget. This means if you LoadImage an image, you can't call FreeImage on it after setting it in the ButtonImageGadget.

The code below verifies this. In my application (the larger one, not the repro snippet below) I have a thread that repeatedly posts an event to change the image on a ButtonImageGadget, and I think I may have a memory leak because I never free the previous image before loading/setting the next one.

Just wondering if this is by design. "This" meaning that the full image isn't copied into the ButtonImageGadget, just the handle to an image. Perfectly fine and dandy if it's by design, just looking for confirmation.

Code: Select all

Global btn.i
UseJPEGImageDecoder()

Procedure ImageTest()
  Protected img.i

  img = LoadImage(#PB_Any, "<path to any valid jpg>")
  
  If IsImage(img)
    ResizeImage(img, 100, 100, #PB_Image_Raw)
    SetGadgetAttribute(btn, #PB_Button_Image, ImageID(img))
    
    ;FreeImage(img)    ;Uncomment this line to see issue
  EndIf
EndProcedure
  
OpenWindow(0, 0, 0, 120, 120, "ImageTest", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
btn = ButtonImageGadget(#PB_Any, 10, 10, 100, 100, 0)

ImageTest()

;if FreeImage line is uncommented, hovering over buttonimagegadget displays blank button

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by idle »

It makes way more sense that it only sets the pointer to the image rather than copying it.
so yes it's just setting a pointer
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by netmaestro »

PureBasic is designed for speed and efficiency. If there is no need to keep an image in memory in two places, Fred won't do it. You'll come across a lot of the same concept if you use PB much.
BERESHEIT
bytecave
User
User
Posts: 40
Joined: Mon Aug 13, 2012 7:26 am

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by bytecave »

Thank you both for your feedback. In my scenario, where setting a different image into a buttonimagegadget every several seconds, I now save the last img# (returned from LoadImage) and before I set a new image (on the next call to the function), I FreeImage the previous one. This should prevent the leak I was seeing. --Rich
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by TI-994A »

bytecave wrote:...I may have a memory leak because I never free the previous image before loading/setting the next one...
The memory leak is caused by the dynamic allocation of the image each time it's loaded, essentially creating a new and separate image resource with every load. The issue is simply resolved by manually assigning the image number, which effectively overwrites the previously loaded image of the same number.

So, instead of:

Code: Select all

img = LoadImage(#PB_Any, "<path to any valid jpg>")
assign the image to a constant (or constant value), like so:

Code: Select all

LoadImage(#imgConstant, "<path to any valid jpg>")
This renders the call to FreeImage() redundant, and is therefore not required.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
bytecave
User
User
Posts: 40
Joined: Mon Aug 13, 2012 7:26 am

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by bytecave »

Thanks TI-994A! I am using PB's Form Editor for this application, and it uses LoadImage on 5 different images, in the format Img_wndMain_1 = LoadImage(...). Since I can't assume the internal implementation (can't know which values it chooses for image numbers) of LoadImage, I'd have to chose image_constants at runtime, by generating a (random probably) number and testing the values against each of the Img_wndMain_* values to make sure there was no collision.

Now, if there is a way to ensure that my constant values don't interfere with the range that PB uses for its LoadImages calls, perhaps a LoadImage corollary to #PB_Event_FirstCustomValue, then I could define my constants in that range.

Thanks again! --Rich
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by TI-994A »

bytecave wrote:I am using PB's Form Editor for this application, and it uses LoadImage on 5 different images, in the format Img_wndMain_1 = LoadImage(...)...
That's quite alright, as the images would still be loaded only once. The dynamically-assigned image numbers can then be used to set the relevant image gadgets without the need to reload them each time. Alternatively, there is also the option of using static image numbers by deselecting the #PB_Any option for the images in the Form > Image Manager menu.
bytecave wrote:...Since I can't assume the internal implementation (can't know which values it chooses for image numbers) of LoadImage, I'd have to chose image_constants at runtime, by generating a (random probably) number and testing the values against each of the Img_wndMain_* values to make sure there was no collision...
There's no risk of that as the #PB_Any directive assigns object numbers quite beyond the recommended limits of manually assigned ones.

Code: Select all

imageFile$ = #PB_Compiler_Home + "Examples\Sources\Data\Purebasic.bmp"
dynamicNumber = LoadImage(#PB_Any, imageFile$)
Debug dynamicNumber   ; very high number
Now, try this (with the debugger on):

Code: Select all

imageFile$ = #PB_Compiler_Home + "Examples\Sources\Data\Purebasic.bmp"
LoadImage(100000, imageFile$)
The debugger will throw this error:

Code: Select all

[ERROR] #Image object number is very high (over 100000), are You sure of that ?
So, as long as the recommendations are adhered to, static and dynamic object numbers can be safely used alongside each other without risk of overlapping.

Another noteworthy point: object numbers are referenced by their object types, meaning that the same object number can be used simultaneously for different object types. A window, a gadget, an image, a font, and so forth, can have identical object numbers without them overwriting one another.

Code: Select all

commonObjectNumber = 0

LoadFont(commonObjectNumber, "Arial", 18, #PB_Font_Italic)
LoadImage(commonObjectNumber, #PB_Compiler_Home + "Examples\Sources\Data\Purebasic.bmp")

wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(commonObjectNumber, 0, 0, 400, 100, "Object Numbers", wFlags)

ImageGadget(commonObjectNumber, 0, 0, 0, 0, ImageID(commonObjectNumber))

; must use a different object number or it will overwrite the image gadget
TextGadget(1, 0, 50, 380, 50, "Arial Italic Font Size 18", #PB_Text_Right)
SetGadgetFont(1, FontID(commonObjectNumber))

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
bytecave
User
User
Posts: 40
Joined: Mon Aug 13, 2012 7:26 am

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by bytecave »

Quick wrap-up thank you. :)

This convinced me:
Alternatively, there is also the option of using static image numbers by deselecting the #PB_Any option for the images in the Form > Image Manager menu.
It's obvious now that you've pointed it out :wink: and totally under my control; I can ensure no conflict between Form Editor objects and my own objects.

You did a lot of work clarifying this next bit, and I really appreciate the time you took and the sharing of your knowledge. I am, however, a bit gun shy about depending on #PB_Any always returning numbers in a range that doesn't conflict with my own static assignments. Unless that's in the documentation, it's a PB internal operation that is (technically) subject to change at any time. Not saying that it's not in the documentation (I couldn't find it though), and definitely not saying that you aren't correct about how it works now, just that I prefer the conservative route of not depending upon undocumented behaviors.

Anyhow, my app has been running for days with no memory leak and my lovely wall tablet mounted picture slideshows it enables are all merrily slideshowing all over the house!

YOU, my friend, are awesome. :)
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ButtonImageGadget - SetGadgetAttribute doesn't copy imag

Post by TI-994A »

bytecave wrote:...gun shy about depending on #PB_Any always returning numbers in a range that doesn't conflict with my own static assignments...
You're right; it's not impossible. Always good to stick to our preferred programming styles.

And thank you for your extremely kind words. They're truly appreciated and I'm glad to have been of any help. :D
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
Post Reply