Bug 496409: [HiDPI][API] Provide Image#getImageData(int zoom)
- Cocoa and GTK implementation for getImageData(int zoom) method.
Change-Id: I8db02a3c4dad01813f1d9b46bd6518c9624d9ede
Also-by: Niraj Modi <niraj.modi@in.ibm.com>
Signed-off-by: Markus Keller <markus_keller@ch.ibm.com>
Signed-off-by: Lakshmi Shanmugam <lshanmug@in.ibm.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java
index 615da81..b52ae6f 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java
@@ -1248,20 +1248,7 @@
* @see ImageData
*/
public ImageData getImageData() {
- if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
- NSAutoreleasePool pool = null;
- if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
- try {
- NSBitmapImageRep imageRep;
- if (imageFileNameProvider == null && imageDataProvider == null) {
- imageRep = getRepresentation();
- } else {
- imageRep = getRepresentation_100();
- }
- return _getImageData(imageRep, this.alphaInfo_100);
- } finally {
- if (pool != null) pool.release();
- }
+ return getImageData(100);
}
/**
@@ -1271,8 +1258,6 @@
* <p>
* <b>Warning:</b> This API doesn't make sense and will be replaced, see
* <a href="https://bugs.eclipse.org/496409">bug 496409</a>.
- * Until then, it will return an ImageData for the highest supported resolution
- * (e.g. always the 200% version on macOS).
* </p>
*
* @return an <code>ImageData</code> containing the image's data
@@ -1285,44 +1270,70 @@
*
* @see ImageData
* @since 3.105
+ * @deprecated use {@link #getImageData(int)} instead
*/
+@Deprecated
public ImageData getImageDataAtCurrentZoom() {
- /*
- * Bug 496409 in SWT: getImageDataAtCurrentZoom() doesn't make sense.
- *
- * The current implementation on cocoa returns the 200% representation if there's an image provider,
- * even if DPIUtil.getDeviceZoom() == 100. That sounds wrong, but it's crucial for clients
- * that this behavior keeps working.
- *
- * Reason: The Image constructor wrongly creates 200% images even if
- * DPIUtil.getDeviceZoom() == 100 (bug 462555). A consequence of that bug is that
- * clients get callbacks to ImageDataProvider#getImageData(int) for zoom == 200.
- * To compute e.g. a composite image, clients need access to the 200% image data.
- * Currently, the only way to do that is via this method.
- */
+ return getImageData(DPIUtil.getDeviceZoom());
+}
+
+/**
+ * Returns an {@link ImageData} for the given zoom level based on the
+ * receiver.
+ * <p>
+ * Note that this method is mainly intended to be used by custom
+ * implementations of {@link ImageDataProvider} that draw a composite image
+ * at the requested zoom level based on other images. For custom zoom
+ * levels, the image data may be an auto-scaled version of the native image
+ * and may look more blurred or mangled than expected.
+ * </p>
+ * <p>
+ * Modifications made to the returned {@code ImageData} will not affect this
+ * {@code Image}.
+ * </p>
+ *
+ * @param zoom
+ * The zoom level in % of the standard resolution (which is 1
+ * physical monitor pixel == 1 SWT logical pixel). Typically 100,
+ * 150, or 200.
+ * @return an <code>ImageData</code> containing the image's data and
+ * attributes at the given zoom level
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li>
+ * </ul>
+ *
+ * @since 3.106
+ */
+public ImageData getImageData(int zoom) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
NSAutoreleasePool pool = null;
if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
try {
- NSBitmapImageRep imageRep = getRepresentation();
- ImageData data;
boolean hasImageProvider = imageFileNameProvider != null || imageDataProvider != null;
- if (hasImageProvider) {
- NSBitmapImageRep imageRep200 = getRepresentation_200();
- if (imageRep200 != null) {
- if (alphaInfo_100.alphaData != null && alphaInfo_200 != null) {
- if (alphaInfo_200.alphaData == null) initAlpha_200(imageRep);
+ if (zoom == 100) {
+ NSBitmapImageRep imageRep;
+ imageRep = hasImageProvider ? getRepresentation_100() : getRepresentation();
+ return _getImageData(imageRep, alphaInfo_100);
+ }
+ if (zoom == 200) {
+ if (hasImageProvider) {
+ NSBitmapImageRep imageRep200 = getRepresentation_200();
+ if (imageRep200 != null) {
+ if (alphaInfo_100.alphaData != null && alphaInfo_200 != null) {
+ if (alphaInfo_200.alphaData == null) initAlpha_200(imageRep200);
+ }
+ return _getImageData(imageRep200, alphaInfo_200);
}
- return _getImageData(imageRep, alphaInfo_200);
}
}
- // XXX: No HiDPI representation available: Need to scale 100% rep. Workaround for bug 513129 and bug 513637.
- data = _getImageData(imageRep, this.alphaInfo_100);
- return DPIUtil.autoScaleImageData(device, data, DPIUtil.getDeviceZoom(), 100);
} finally {
if (pool != null) pool.release();
}
+ return DPIUtil.autoScaleImageData (device, getImageData(), zoom, 100);
}
+
/** Returns the best available representation. May be 100% or 200% iff there is an image provider. */
NSBitmapImageRep getRepresentation () {
NSBitmapImageRep rep = new NSBitmapImageRep(handle.bestRepresentationForDevice(null));
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java
index ac9a7b0..520b8d9 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java
@@ -208,7 +208,7 @@
* Auto-scale image with ImageData
*/
public static ImageData autoScaleImageData (Device device, final ImageData imageData, int targetZoom, int currentZoom) {
- if (deviceZoom == 100 || imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+ if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
float scaleFactor = (float) targetZoom / (float) currentZoom;
return autoScaleImageData(device, imageData, scaleFactor);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
index 77abfdc..da93696 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -1335,8 +1335,6 @@
* <p>
* <b>Warning:</b> This API doesn't make sense and will be replaced, see
* <a href="https://bugs.eclipse.org/496409">bug 496409</a>.
- * Until then, it will return an ImageData for the highest supported resolution
- * (e.g. always the 200% version on macOS).
* </p>
*
* @return an <code>ImageData</code> containing the image's data
@@ -1349,7 +1347,9 @@
*
* @see ImageData
* @since 3.105
+ * @deprecated use {@link #getImageData(int)} instead
*/
+@Deprecated
public ImageData getImageDataAtCurrentZoom () {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
@@ -1459,29 +1459,60 @@
}
/**
- * Returns an <code>ImageData</code> for specified zoom, based on the receiver
- * Modifications made to this <code>ImageData</code> will not affect the
- * Image.
+ * Returns an {@link ImageData} for the given zoom level based on the
+ * receiver.
+ * <p>
+ * Note that this method is mainly intended to be used by custom
+ * implementations of {@link ImageDataProvider} that draw a composite image
+ * at the requested zoom level based on other images. For custom zoom
+ * levels, the image data may be an auto-scaled version of the native image
+ * and may look more blurred or mangled than expected.
+ * </p>
+ * <p>
+ * Modifications made to the returned {@code ImageData} will not affect this
+ * {@code Image}.
+ * </p>
*
* @param zoom
* The zoom level in % of the standard resolution (which is 1
* physical monitor pixel == 1 SWT logical pixel). Typically 100,
* 150, or 200.
* @return an <code>ImageData</code> containing the image's data and
- * attributes at specified zoom if present else null is returned.
+ * attributes at the given zoom level
*
* @exception SWTException <ul>
* <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li>
* </ul>
*
- * @see ImageData
- *
- * @since 3.105
+ * @since 3.106
*/
-ImageData getImageData (int zoom) {
+public ImageData getImageData (int zoom) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
- return DPIUtil.autoScaleImageData (device, getImageDataAtCurrentZoom (), zoom, currentDeviceZoom);
+
+ if (zoom == currentDeviceZoom) {
+ return getImageDataAtCurrentZoom();
+ } else if (imageDataProvider != null) {
+ boolean[] found = new boolean[1];
+ ImageData data = DPIUtil.validateAndGetImageDataAtZoom (imageDataProvider, zoom, found);
+ // exact image found
+ if (found[0]) {
+ return data;
+ }
+ // AutoScale the image at 100% zoom
+ return DPIUtil.autoScaleUp (device, data);
+ } else if (imageFileNameProvider != null) {
+ boolean[] found = new boolean[1];
+ String fileName = DPIUtil.validateAndGetImagePathAtZoom (imageFileNameProvider, zoom, found);
+ // exact image found
+ if (found[0]) {
+ return new ImageData (fileName);
+ }
+ // AutoScale the image at 100% zoom
+ return DPIUtil.autoScaleUp (device, new ImageData (fileName));
+ } else {
+ return DPIUtil.autoScaleImageData (device, getImageDataAtCurrentZoom (), zoom, currentDeviceZoom);
+ }
}
/**
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java
index c0015ab..d1b0b55 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java
@@ -132,7 +132,7 @@
}
OS.g_object_unref(maskPixbuf);
} else {
- ImageData data = image.getImageDataAtCurrentZoom ();
+ ImageData data = image.getImageData (DPIUtil.getDeviceZoom ());
boolean hasAlpha = data.getTransparencyType () == SWT.TRANSPARENCY_ALPHA;
pixbuf = OS.gdk_pixbuf_new (OS.GDK_COLORSPACE_RGB, hasAlpha, 8, w [0], h [0]);
if (pixbuf == 0) SWT.error (SWT.ERROR_NO_HANDLES);
diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java
index c990d85..3a068fb 100644
--- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java
+++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java
@@ -798,6 +798,69 @@
}
@Test
+public void test_getImageData_100() {
+ getImageData_int(100);
+}
+
+@Test
+public void test_getImageData_200() {
+ getImageData_int(200);
+}
+
+void getImageData_int(int zoom) {
+ Rectangle bounds = new Rectangle(0, 0, 10, 20);
+ Image image = new Image(display, bounds.width, bounds.height);
+ image.dispose();
+ try {
+ image.getImageData(zoom);
+ fail("No exception thrown for disposed image");
+ } catch (SWTException e) {
+ assertSWTProblem("Incorrect exception thrown for disposed image", SWT.ERROR_GRAPHIC_DISPOSED, e);
+ }
+
+ // creates bitmap image and compare size of imageData
+ image = new Image(display, bounds.width, bounds.height);
+ ImageData imageDataAtZoom = image.getImageData(zoom);
+ image.dispose();
+ Rectangle boundsAtZoom = new Rectangle(0, 0, imageDataAtZoom.width, imageDataAtZoom.height);
+ assertEquals(":a: Size of ImageData returned from Image.getImageData(int) method doesn't return matches with bounds in Pixel values.", scaleBounds(bounds, zoom, 100), boundsAtZoom);
+
+ // create icon image and compare size of imageData
+ ImageData imageData = new ImageData(bounds.width, bounds.height, 1, new PaletteData(new RGB[] {new RGB(0, 0, 0)}));
+ image = new Image(display, imageData);
+ imageDataAtZoom = image.getImageData(zoom);
+ image.dispose();
+ boundsAtZoom = new Rectangle(0, 0, imageDataAtZoom.width, imageDataAtZoom.height);
+ assertEquals(":b: Size of ImageData returned from Image.getImageData(int) method doesn't return matches with bounds in Pixel values.", scaleBounds(bounds, zoom, 100), boundsAtZoom);
+
+ // create image with FileNameProvider
+ image = new Image(display, imageFileNameProvider);
+ imageDataAtZoom = image.getImageData(zoom);
+ boundsAtZoom = new Rectangle(0, 0, imageDataAtZoom.width, imageDataAtZoom.height);
+ bounds = image.getBounds();
+ image.dispose();
+ assertEquals(":c: Size of ImageData returned from Image.getImageData(int) method doesn't return matches with bounds in Pixel values.", scaleBounds(bounds, zoom, 100), boundsAtZoom);
+
+ // create image with ImageDataProvider
+ image = new Image(display, imageDataProvider);
+ imageDataAtZoom = image.getImageData(zoom);
+ boundsAtZoom = new Rectangle(0, 0, imageDataAtZoom.width, imageDataAtZoom.height);
+ bounds = image.getBounds();
+ image.dispose();
+ assertEquals(":d: Size of ImageData returned from Image.getImageData(int) method doesn't return matches with bounds in Pixel values.", scaleBounds(bounds, zoom, 100), boundsAtZoom);
+}
+
+public static Rectangle scaleBounds (Rectangle rect, int targetZoom, int currentZoom) {
+ float scaleFactor = ((float)targetZoom) / (float)currentZoom;
+ Rectangle returnRect = new Rectangle (0,0,0,0);
+ returnRect.x = Math.round (rect.x * scaleFactor);
+ returnRect.y = Math.round (rect.y * scaleFactor);
+ returnRect.width = Math.round (rect.width * scaleFactor);
+ returnRect.height = Math.round (rect.height * scaleFactor);
+ return returnRect;
+}
+
+@Test
public void test_hashCode() {
Image image = null;
Image image1 = null;