Dealing with NSImage

Hello,

the following method stambles at dim icon as NSImage, below.
Since I removed macOSLibrary, I expected it to stumble. Yet, after several attempts (i.e. declares, and adding the required NSSize structure) I failed to have it work.
BTW: it is part of the code to add row, picture and tag to a popupmenu.

Help will be appreciated. Thank you.

[code]Public Sub Icon(extends p as PopupMenu, index as Integer, setTemplate as Boolean = False, assigns value as NSImage)
#if TargetCocoa
declare function menu lib “Cocoa” selector “menu” (obj_id as Integer) as Ptr
declare function itemAtIndex lib “Cocoa” selector “itemAtIndex:” (obj_id as Ptr, index as Integer) as Ptr
declare sub setImage lib “Cocoa” selector “setImage:” (obj_id as Ptr, menuImage as Ptr)
declare sub setSize lib “Cocoa” selector “setSize:” (obj_id as Ptr, aSize as NSSize)

dim m as Ptr = menu(p.Handle)
dim item as Ptr = itemAtIndex(m, index)
dim icon as NSImage//   <<it stambles here
if value <> nil then
  icon = value.Copy
end if

const MenuIconSize = 16

if icon is nil then
  setImage( item, nil )
else
  dim size as NSSize
  size.Width = MenuIconSize
  size.Height = MenuIconSize
  setSize(icon, size)
  icon.Template = setTemplate
  setImage(item, icon)
end if

#endif
End Sub
[/code]

NSSize is a structure of two floating point values.

Structure NSSize width as CGFloat height as CGFloat End Structure

Did you define an NSImage class somewhere in your project?

@Sam Rowlands: Yes, NSSize structure is two floating points value.

@Ulrich Bogun: that is the issue: since I removed macOSLibrary, now I do not know how to define NSImage. I searched here and there (also on the forum) , but my tests missed the point.

Thanks for answering.

https://github.com/macoslib/macoslib/blob/master/macoslib/Cocoa/NSImage.rbbas

Like any object, the core of NSImage is a ptr (or Integer if you prefer it, because Xojo handles are also Integers).
So instead of NSImage, and to follow your approach of Ptr handling, use Value as Ptr and include the copy declare (which I think is behind that) from the removed NSImage class, making it return a Ptr as well.

@Ulrich Bogun: dear me! Your explanation is too high for my low level of understanding (big big smily).
Anyway, here is one specimen of the things I tried, and that does not work:

declare function menu lib “Cocoa” selector “menu” (obj_id as Integer) as Ptr
declare function itemAtIndex lib “Cocoa” selector “itemAtIndex:” (obj_id as Ptr, index as Integer) as Ptr
declare sub setImage lib “Cocoa” selector “setImage:” (obj_id as Ptr, menuImage as Ptr)
declare sub setSize lib “Cocoa” selector “setSize:” (obj_id as Ptr, aSize as NSSize)

Dim NSImage As Ptr = NSClassFromString(“NSImage”)

dim m as Ptr = menu(p.Handle)
dim item as Ptr = itemAtIndex(m, index)
dim icon as NSImage(p.Handle)

Beside the fact that it does not work, looking at the last part of the Public Sub (in my first post, and quoted below), i.e. “assigns value as NSImage”, I take it that NSImage should be something that already exists; I mean: like a property that one can refer to in several places.
Public Sub Icon(extends p as PopupMenu, index as Integer, setTemplate as Boolean = False, assigns value as NSImage).

Sorry for being so thick. Thanks.

Not so easy to tell what you are intending to do, but I’ll try a few lines:
Jim McKay wrote a great writeup on declare basics once. I don’t find the link currently, but someone else here surely will. This might help to clean up some misunderstandings.

Dim NSImage As Ptr = NSClassFromString("NSImage") does not create an NSImage. Instead, it gives you the Ptr to the NSImage class. The way an NSImage is built is either by one of the dedicated init methods which you find in Apple’s NSImage documentation, like

Dim NSImageClass As Ptr = NSClassFromString("NSImage") Dim NSImage as Ptr = InitwithSize(alloc(NSImageClass), mySize)
where mySize is a NSSize structure and alloc another Foundation method you will find in MacOSLib, just like you found NSClassFromString. And initWithSize would be another Cocoa/Appkit declare you’d have to add.

Or you get an NSImage copying its NSImage handle from a Xojo picture.

Please note that creating an NSImage makes you the owner of it, so you’ll have to cast a release method call on it once you’re done with it or you will create memory leaks.

In your code, I don’t see what p is. Do you want get the NSImage from a menu entry?

EDIT: Oh, I missed the last line. Change that to

Public Sub Icon(extends p as PopupMenu, index as Integer, setTemplate as Boolean = False, assigns value as ptr).

And either send the CopyOSHandle(Picture.HandleType.MacOSNSImage) of a Xojo picture to it or nil if you don’t want an icon.

Thank you, Urlich, for your patience.

It was one of my nonsense ventures.

Here is pretty late. Tomorrow morning, with brains-battery recharged, I’ll launch myself in this adventure. Thanks again.

Ignore that sentence, Carlo. Try the addition after my edit. That should do the trick.

Hi Ulrich,
as you said, your addition did the trick.
In the following snippet, I set in bold type the places that I removed (since no NSImage is used).

In your previous post you warned me about casting a release method call. Is this such a case? Or as I guess, such call is necessary only when alloc is called?
Moreover, hoping of not stretching to the limits your patience, is there a simple way to return a Xojo pict from a NSImage?
What I mean, as simple as the one to turn a pict into a NSImage?

Many many thanks.

[code]Public Sub popIcon(extends p as PopupMenu, index as Integer, assigns value as ptr)
#if TargetCocoa
declare function menu lib “Cocoa” selector “menu” (obj_id as Integer) as Ptr
declare function itemAtIndex lib “Cocoa” selector “itemAtIndex:” (obj_id as Ptr, index as Integer) as Ptr
declare sub setImage lib “Cocoa” selector “setImage:” (obj_id as Ptr, menuImage as Ptr)
declare sub setSize lib “Cocoa” selector “setSize:” (obj_id as Ptr, aSize as NSSize)

dim m as Ptr = menu(p.Handle)
dim item as Ptr = itemAtIndex(m, index)
dim icon as ptr = value
[b]if value <> nil then
  //icon = value.Copy
end if[/b]
const MenuIconSize = 16
if icon = nil then
  setImage( item, nil )
else
  dim size as NSSize
  size.Width = MenuIconSize
  size.Height = MenuIconSize
  setSize(icon, size)
  [b]//icon.Template = setTemplate[/b]
  setImage(item, icon)
end if

#endif
End Sub
[/code]

@Carlo Rubini — Real question: why did you stop using macOSLib?

@Stphane Mons : I’m answering you in a private conversation.

Hi Carlo,

you’re right. That is only necessary if you create a macOS object programmatically (with an init/alloc combination or a specified constructor method on a macOS class ptr), or if the docs state explicitly that you have to release an object that was created for you. CopyOSHandle(MacNSImage) is autoreleased, as you can see in the docs. So you don’t have to care about that, the NSImage will be released when the Xojo ptr that you assign it to gets out of scope.

Getting a Xojo picture from a NSImage can be done in several ways, but all include the declaration of some more classes and methods. I think that MacOSLib chose a path via CGImage. For pixel-based images this rough outline works for me. I have created subclasses in this project, so you would have to change it to your ptr handling. Let me know if you need assistance:

Public Function toPicture(extends i as NSImage) as Picture If i <> Nil And i.Valid Then dim p as new picture (i.Width, i.Height) dim g as new CGContext (p.Graphics) dim rect as NSRect = NSMakeRect (0,0,i.width, i.height) NSGraphicsContext.CurrentContext = NSGraphicsContext.ContextWithCGContext(g) i.Draw Rect Return p end if End Function

And I would be interested in why you left MacOSLib too, if you wouldn’t mind telling me.

EDIT: Short explanation of the code above:
Valid (isValid) is a function of NSImage. You cannot use this method for all kinds of NSImages, but at least for those that were created from a Xojo picture / pixel-based ones.
Xojo’s graphics object on macOS is called CGContext. For your ptr handling, you only need Graphics.handle – that is the equivalent.
NSMakeRect cannot be declared. It is a compiler macro, not an API function. I just used the same naming for a function that returns a NSRect with a specified size.
NSImage has a drawRect method that draws the image directly to the current context – but in this case NSGraphicsContext, not CGContext. So we have to set the CurrentContext property of NSGraphicsContext to the CGContext of the Xojo picture, and for this the mentioned properties/creators exists on NSGraphicsContext.
I had a g.SaveState and g.RestoreState in the code too, but I am currently not sure if these are necessary.

Hi Ulrich,
thanks for the “release” explanation.

Before writing my last post, I had already tried to make a function returning a pict from NSImage (in my case ptr). In fact I had copy/pasted in a new method the piece of code (originally form macOSLib) needed to return a systemImage (below). The debugger run without giving warnings, yet a strange thing happened: the target canvas would not get the pict; instead, another canvas that set to show a systemIcon would return blank. I quitted Xojo and re-opened it several times, but to no avail.
That’s why I asked you if there were a simple way to return a pct from ptr.
Now the funny thing: yesterday, as I do every week, I restarted my MBP, and voila, the target canvas was showing the intended pict, and the systemIcon-canvas had its right pict. May be if I had cleared Xojo cache the issue would have disappeared.

Then this morning I got your kind reply with your proposed code, and I see that you use a more direct approch. After a quick test in the debugger I started making some necessary modifications; first of all in the declaration where after replacing NSImage with Ptr, I added also width and Heigh, since a ptr does not have them.
Then, for the same reason I removed i.valid; and after that I fall in the mud…
That makes me uncomfortable. Because asking for help is legit, but pestering for help shows lack of decency.
Therefore, if you agree, and although I like your approach, I’d consider this issue satisfactorily solved.
And I thank you again for your assistance.

Chapter 2: [quote]And I would be interested in why you left MacOSLib too, if you wouldn’t mind telling me.[/quote]
Answer coming.

Public Function NSImageToPicture(extends iPtr as ptr, iW as Integer, iH as Integer) as picture
declare function size lib “Cocoa” selector “size” (obj_id as Ptr) as NSSize
//get the size of the NSImage
dim s As NSSize
s.width = iW
s.height = iH

dim p as new picture(s.width,s.height)
//get the graphics of the picture as CGContext
dim cntx as Ptr=ptr(p.Graphics.Handle(Graphics.HandleTypeCGContextRef))

dim rect As NSRect
rect.x = 0
rect.y = 0
rect.w = iW
rect.h = iH

declare function CGImageForProposedRect lib “Cocoa” _
selector “CGImageForProposedRect:context:hints:” _
(obj_id as Ptr, byref proposedDestRect as NSRect, _
referenceContext as Ptr, hints as Ptr) as Ptr

//convert the NSImage to CGImage
dim cgPtr As ptr=CGImageForProposedRect(iPtr,rect,nil,nil)

soft declare sub CGContextDrawImage lib “Cocoa” _
(context as Ptr, rect as NSRect, image as Ptr)

//Draw the CGImage to our Xojo picture
CGContextDrawImage cntx, rect, cgPtr

Return p
End Function

Perfectly fine, Carlo!
The route via CGImage is another one of those I spoke about as possible approaches. And because you’re mostly on Core Graphics level this way it could also be slightly faster than the higher-level NSGraphicsContext method.

@Ulrich Bogum

That’s what I guessed after reading the explanation of your code. Regards.