Mailing Lists: Apple Mailing Lists

Image of Mac OS face in stamp
 
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: painting in Java to a (native) NSView possible?



Rob Ross wrote:

The long story is, I'm trying to implement the Mac version of the
TrayIcon API of the JDIC project :
  https://jdic.dev.java.net/

It's basically an API to the System Tray on windows, and on the Mac
it's an API to the Status Bar. Some of the notification actions in
Windows OS(the balloon dialog that pops up over a tray icon) map to
the Dock notification action, but most of the TrayIcon API is
appropriate to the Mac Status Bar. I have a Cocoa test app that does
everything I need, so the native part is working.

The Status Bar seems like an odd place to put the System Tray interface. In particular, it raises the question of which status bar -- there could be as many as one on every Finder window. Or are we talking about different things? See:


http://www.apple.com/pro/tips/yourstatus.html

One problem I am having is that the API I have to implement only
provides access to an Icon (javax.swing.Icon). That's just an
interface, so it is a pretty opaque object; all you can really do is
get the size of the Icon, and call paintIcon on it.

If I had access to the image URL, I could of course pass that from
Java to the JNI method and create an NSImage (actually an
NSBitmapImageRep), and I can pass that to my NSStatusItem as the
image. Or if I see the image is multi-framed, I can use a Timer to
periodically update that image and thus provide support for an
animated GIF. Or I could also create a custom NSView with an
NSImageViewer, and if my image is multi-framed, it would
automatically animate it for me.

But, I do NOT have access to the image URL. From an Icon, I would
like to at least get an Image, and maybe from that a BufferedImage,
and have some hope of getting access to the image data. But, there
are at least 8 implementations of Icon, and no guarantee what you get
passed will be an ImageIcon. And even if it IS an ImageIcon, the Mac
implementation (on my machine) shows that the actual class is an
apple.awt.OSXImage, of which I have no official documentation. On
Windows, it would either be a BufferedImage or a VolitleImage. From
the BufferedImage I could do something with the ImageIO classes,
however, there is no GIF writer in the JDK, so even if I can figure
out the image is really a GIF, there's no way to write it to a stream
or a ByteBuffer in order to pass the bytes to JNI.

Of course, it may not be an ImageIcon, and it may not have an URL or other kind of encoded image data. All you can really count on is that there is code behind the paintIcon method that will render the icon.


I have to use the old-school ImageObserver method, and continually
monitor the state of the Image, and when I see the FRAMEBITS flag, I
have to create an offscreen Graphics in Java, paint that single new
frame, then convert that to something I can pass to a JNI method(like
a PNG), that will then install it as the image on the NSStatusItem.

So this is the only thing I can think of that will work, and be safe,
only using public, documented APIs.

Since you've only got an Icon instance to work with, you can't even count on using ImageObserver. If I implemented a VectorIcon class whose paintIcon method only used Graphics.drawLine calls to render a line-art icon, it would not be usable with such an implementation.


I think that the only safe, pure-java way of doing this is to render the Icon to an off-screen BufferedImage and then pass the pixel data from it to the native drawing code. Note that this approach will not automatically animate an animated icon -- you will only get a single frame (although I can think of hacks to detect and handle animation, see below).

Some of the not-so-safe hacks I am also contemplating are :

the ImageIcon constructor takes a String description argument, that I
do not think is used much. If you created the ImageIcon via a URL or
file name, and do not specify the description field, it gets set to
the URL or filename you used to construct the object. So I could get
this value, see if the file/url exists, and pass that to JNI, and I'm
in business.

Or, I could get a BufferedImage version of the ImageIcon by noting
that OSXImage extends sun.awt.image.ToolkitImage, and that class has
a public getBufferedImage() method. I think there may be a way of
getting an IIOImage from a BufferedImage or one of it's subclasses,
and then I could at least access the metadata that tells me how many
frames the image contains. If it's just one, I can just draw it
offscreen, and write it to a PNG (I can write to many different
formats, just not GIF). If it's multiple frames, I'm in the same boat
as above; I either update the image every time my ImageObserver tells
me a new frame is available, or I can extract each frame and pass
them to JNI as an array of PNGs, and then implement my own animation
loops.

Or, finally, I could avoid this by just drawing in Swing/AWT, to a
native NSView, and let Java handle a lot of this image processing for
me. It would require however a means of drawing into an NSView from
Java.

As I'm sure you are aware, all Java drawing is done through a Graphics or Graphics2D instance. Swing/AWT creates these objects as part of its normal rendering process. If you want to render to a non- Swing/AWT graphics context using normal Swing rendering code you will need a Graphics or Graphics2D instance that will draw to the appropriate NSView. Writing one yourself would probably be relatively straightforward JNI, but would be an awful lot of work (especially getting drawImage to work properly for animated Images, so ImageIcon will work right, applying AffineTransforms, etc.). This probably isn't a viable solution for you.


Ultimately, all Swing on-screen rendering boils down to calls to a Graphics2D instance of the AWT Window ancestor in the Swing containment hierarchy. If you could somehow create a Window instance (and perhaps more importantly, its peer) for this NSView, then you could leverage all the existing Swing rendering infrastructure. However, the code that does all that is deep in the bowels of Apple's proprietary JRE implementation, so you can't see how it works. Getting this hack to work would require assistance from Apple. I do not know if DTS would be able to help you out on it, if you had an incident available. You could try asking (see http:// developer.apple.com/faq/techsupport.html, email is email@hidden, but read the FAQ first).

I think you are stuck rendering your icon to an off-screen image, and passing the pixels to native code to be drawn to the NSView. To make animated icons work, you could consider creating a proxy Graphics2D class. Its constructor takes the Graphics2D instance of a BufferedImage (or the BufferedImage itself), and implements every method of the Graphics2D interface and simply calls the same method on the off-screen rendering context, except for the drawImage methods. They all need to provide an ImageObserver that will provide notification of the animation events so that your class can re-render the pixmap and pass the new frame on to the native code. Don't forget to relay any events received by your ImageObserver to any ImageObserver passed in by the caller. You would pass the proxy Graphics2D instance in to the paintIcon method whenever you determine that the NSView needs to be repainted (read the docs for CocoaComponent carefully -- the rules it outlines for mixing AppKit thread code and Swing code must be followed), and then pass the generated pixels down to the native code when it is done, and be aware that there may well be subsequent native repaints required (e.g. due to ImageObserver notifications to the proxy).

As for passing the pixels down to the native code for rendering in the NSView, I wouldn't worry about encoding them as PNG or GIF or anything else -- given how small most "System Tray" icons are, the memory savings would be negligible (even a 128x128 icon is only 16 K- pixels). Just pass in raw RGB-32 data and either blit the pixels directly into the NSView (could get ugly if the pixel formats do not match), or build an NSImage from them and paint it.

_______________________________________________
Do not post admin requests to the list. They will be ignored.
Java-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/java-dev/email@hidden

This email sent to email@hidden


Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Contact Apple | Terms of Use | Privacy Policy

Copyright © 2007 Apple Inc. All rights reserved.