Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [platform-swt-dev] Image caching on Mac OS X Mountain Lion

I have fixed a bug on this area a couple of weeks ago. Take a look at:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=339132

More specifically on commet#42

https://bugs.eclipse.org/bugs/show_bug.cgi?id=339132#c42

The way I got the cache to be flushed was by calling NSBitmapImageRep.bitmapData() when the GC drawing on the image is destroy.  Hopefully this will work for you as well.

Silenio



From:        Stephan Aßmus <superstippi@xxxxxx>
To:        "platform-swt-dev@xxxxxxxxxxx" <platform-swt-dev@xxxxxxxxxxx>,
Date:        12/03/2012 12:27 PM
Subject:        [platform-swt-dev] Image caching on Mac OS X Mountain Lion
Sent by:        platform-swt-dev-bounces@xxxxxxxxxxx




Hello all,

I am having an unfortunate problem with my SWT based application on OS X Mountain Lion.

I have written a native lib that attaches to the buffer of an SWT Image, and renders/generates the image contents. One of the reasons I am doing this, is because the image contents are frequently updated. (The other reason is I have very specific needs on text rendering which are not satisfied by SWT.)

I create the SWT Image with the ImageData constructor:

 PaletteData palette = new PaletteData(0xff000000, 0x00ff0000, 0x0000ff00);
                                                 
 ImageData imageData = new ImageData(
     bounds.width, bounds.height, 32, palette);

 fImage = new Image(getDisplay(), imageData);

On SWT-Mac, this creates an NSImage with caching disabled, if I read the code right:

 ...
 handle = (NSImage)new NSImage().alloc();
 NSSize size = new NSSize();
 size.width = width;
 size.height = height;
 handle = handle.initWithSize(size);
 NSBitmapImageRep rep = (NSBitmapImageRep)new NSBitmapImageRep().alloc();
 rep = rep.initWithBitmapDataPlanes(0, width, height, 8, hasAlpha ? 4 : 3,
     hasAlpha, false, OS.NSDeviceRGBColorSpace,
     OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, bpr, 32);
 OS.memmove(rep.bitmapData(), buffer, dataSize);                
 handle.addRepresentation(rep);
 rep.release();
 handle.setCacheMode(OS.NSImageCacheNever);

The last call is the interesting one.

But regardless, the problem I seem to be observing is that caching is interfering with what I am doing. I.e. the initial contents that I render into the Image buffer show up on screen, and any updates don't. As if the OS had copied the buffer somewhere else and doesn't become aware of the changed contents of the original buffer. This is only a problem starting in Mountain Lion. My code works on earlier OS versions.

I am also aware that an NSImage may have multiple buffers associated with it. For example, to support a Retina display, there are supposed to be two buffers which the OS can pick depending on where the image is displayed. My code would currently not support this, but I am not declaring support for high-resolution in my app bundle, and I confirmed this is not the problem.

My fix is to always create a new SWT Image before I generate the updated content. This degrades performance notably, and I would love to skip this part, like I am doing in Windows and Linux.

Below, you can find my code how I attach to the bitmap buffer. It's obfuscated by handling multiple SWT platforms via Reflection, hopefully you can find your way. I am also thankful for any errors you can point out in this snippet.

Any solution to my problem, though? Please do not suggest I don't need to hack around in the SWT guts like I am doing, I really need to, there is no other way.

Thanks a best regards,
-Stephan


---

Class<? extends Image> c = image.getClass();

String platform = SWT.getPlatform();
try {
 if (platform.equals("cocoa")) {
     Class<?> nsImageClass = Class.forName(
         "org.eclipse.swt.internal.cocoa.NSImage");
     Class<?> nsDictionaryClass = Class.forName(
         "org.eclipse.swt.internal.cocoa.NSDictionary");
     Class<?> nsBitmapImageRepClass = Class.forName(
         "org.eclipse.swt.internal.cocoa.NSBitmapImageRep");
     Class<?> idClass = Class.forName(
         "org.eclipse.swt.internal.cocoa.id");
     Object nsImage = c.getDeclaredField("handle").get(image);

     Method bestRepresentationForDeviceMethod
         = nsImageClass.getDeclaredMethod(
             "bestRepresentationForDevice", nsDictionaryClass);
     Object nsImageRepObject
         = bestRepresentationForDeviceMethod.invoke(
             nsImage, new Object[] { null });

     Constructor<?>[] constructors
         = nsBitmapImageRepClass.getConstructors();

     Object nsBitmapImageRepObject = null;

     // Find the constructor that takes an NSImageRep object id
     // and construct the NSBitmapImageRep object.
     for (int i = 0; i < constructors.length; i++) {
         Class<?>[] parameterTypes
             = constructors[i].getParameterTypes();
         if (parameterTypes.length == 1
             && parameterTypes[0] == idClass) {
             nsBitmapImageRepObject = constructors[i].newInstance(
                 nsImageRepObject);
             break;
         }
     }
     if (nsBitmapImageRepObject == null)
         return;

     Method bitmapDataMethod
         = nsBitmapImageRepClass.getDeclaredMethod("bitmapData");
     Method bytesPerRowMethod
         = nsBitmapImageRepClass.getDeclaredMethod("bytesPerRow");

     long data;
     long bpr;

     try {
         // Try 64-Bit SWT version first
         data = "" bitmapDataMethod.invoke(
             nsBitmapImageRepObject, (Object[]) null);
         bpr = (Long) bytesPerRowMethod.invoke(
             nsBitmapImageRepObject, (Object[]) null);
     } catch (ClassCastException e) {
         // Retry assuming 32-Bit SWT version
         data = "" bitmapDataMethod.invoke(
             nsBitmapImageRepObject, (Object[]) null);
         bpr = (Integer) bytesPerRowMethod.invoke(
             nsBitmapImageRepObject, (Object[]) null);
     }

     Rectangle bounds = image.getBounds();

     attachToMemory(fAggContext, data, bounds.width, bounds.height, bpr);

 } else if (platform.equals("gtk")) {
     try {
         // 32-Bit SWT
         int surface = c.getDeclaredField("surface").getInt(image);
         if (surface == 0)
             return;
         attachToCairoSurface(fAggContext, surface);
     } catch (NoSuchFieldException e) {
         // 64-Bit SWT
         long surface = c.getDeclaredField("surface").getLong(image);
         if (surface == 0)
             return;
         attachToCairoSurface(fAggContext, surface);
     }
 } else if (platform.equals("win32")) {
     try {
         // 32-Bit SWT
         int handle = c.getDeclaredField("handle").getInt(image);
         if (handle == 0)
             return;
         attachToBitmapHandle(fAggContext, handle);
     } catch (NoSuchFieldException e) {
         // 64-Bit SWT
         long handle = c.getDeclaredField("handle").getLong(image);
         if (handle == 0)
             return;
         attachToBitmapHandle(fAggContext, handle);
     }
 }

} catch (IllegalArgumentException e) {
 e.printStackTrace();
} catch (SecurityException e) {
 e.printStackTrace();
} catch (IllegalAccessException e) {
 e.printStackTrace();
} catch (NoSuchFieldException e) {
 e.printStackTrace();
} catch (ClassNotFoundException e) {
 e.printStackTrace();
} catch (NoSuchMethodException e) {
 e.printStackTrace();
} catch (InvocationTargetException e) {
 e.printStackTrace();
} catch (InstantiationException e) {
 e.printStackTrace();
}
_______________________________________________
platform-swt-dev mailing list
platform-swt-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/platform-swt-dev



Back to the top