[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
[platform-swt-dev] Image caching on Mac OS X Mountain Lion
|
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 = (Long) bitmapDataMethod.invoke(
nsBitmapImageRepObject, (Object[]) null);
bpr = (Long) bytesPerRowMethod.invoke(
nsBitmapImageRepObject, (Object[]) null);
} catch (ClassCastException e) {
// Retry assuming 32-Bit SWT version
data = (Integer) 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();
}