Skip to content

Commit 6e865bc

Browse files
committed
[Win32] Ensure consistent image data returned for filename/data provider
Due to the on-demand creation of image handles, there is not necessarily a handles anymore from which image data is retrieved when requesting is via the getImageData(...) methods. This results in potentially different kinds of image data (including different anti-aliasing results) depending on whether a handle has already been created for an image at the given zoom or not. This change adapts the Image implementation for ImageDataProvider and ImageFileNameProvider to always use the image data retrieved from a native handle. To this end, it temporarily creates a handle if necessary. In order to avoid repeated loading and handle creation for the same source of image, a cache for the already retrieved image data is introduced. For both the consistent retrieval of image data and the avoidance of repeated loading of image data according test cases are added.
1 parent 0f21a6e commit 6e865bc

File tree

2 files changed

+98
-41
lines changed

2 files changed

+98
-41
lines changed

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import java.io.*;
1818
import java.util.*;
19+
import java.util.function.*;
1920

2021
import org.eclipse.swt.*;
2122
import org.eclipse.swt.internal.*;
@@ -2176,8 +2177,9 @@ public boolean equals(Object otherProvider) {
21762177
}
21772178

21782179
private abstract class BaseImageProviderWrapper<T> extends DynamicImageProviderWrapper {
2180+
private final Map<Integer, ImageData> cachedImageData = new HashMap<>();
2181+
21792182
protected final T provider;
2180-
private final Map<Integer, Rectangle> zoomToBounds = new HashMap<>();
21812183

21822184
BaseImageProviderWrapper(T provider, Class<T> expectedClass) {
21832185
checkProvider(provider, expectedClass);
@@ -2190,17 +2192,38 @@ Object getProvider() {
21902192
}
21912193

21922194
@Override
2193-
protected Rectangle getBounds(int zoom) {
2194-
if (zoomLevelToImageHandle.containsKey(zoom)) {
2195-
ImageHandle imgHandle = zoomLevelToImageHandle.get(zoom);
2196-
return new Rectangle(0, 0, imgHandle.width, imgHandle.height);
2197-
}
2198-
if (!zoomToBounds.containsKey(zoom)) {
2199-
ImageData imageData = getImageData(zoom);
2200-
Rectangle rectangle = new Rectangle(0, 0, imageData.width, imageData.height);
2201-
zoomToBounds.put(zoom, rectangle);
2195+
final ImageData getImageData(int zoom) {
2196+
Function<Integer, ImageData> imageDataRetrival = zoomToRetrieve -> {
2197+
ImageHandle handle = initializeHandleFromSource(zoomToRetrieve);
2198+
ImageData data = handle.getImageData();
2199+
destroyHandleForZoom(zoomToRetrieve);
2200+
return data;
2201+
};
2202+
return cachedImageData.computeIfAbsent(zoom, imageDataRetrival);
2203+
}
2204+
2205+
@Override
2206+
final ImageHandle getImageMetadata(int zoom) {
2207+
ImageData cachedData = cachedImageData.remove(zoom);
2208+
if (cachedData != null) {
2209+
return init(cachedData, zoom);
22022210
}
2203-
return zoomToBounds.get(zoom);
2211+
return initializeHandleFromSource(zoom);
2212+
}
2213+
2214+
private ImageHandle initializeHandleFromSource(int zoom) {
2215+
ElementAtZoom<ImageData> imageDataAtZoom = loadImageData(zoom);
2216+
ImageData imageData = scaleImageData(imageDataAtZoom.element(), zoom, imageDataAtZoom.zoom());
2217+
imageData = adaptImageDataIfDisabledOrGray(imageData);
2218+
return init(imageData, zoom);
2219+
}
2220+
2221+
protected abstract ElementAtZoom<ImageData> loadImageData(int zoom);
2222+
2223+
@Override
2224+
protected Rectangle getBounds(int zoom) {
2225+
ImageData imageData = getImageData(zoom);
2226+
return new Rectangle(0, 0, imageData.width, imageData.height);
22042227
}
22052228
}
22062229

@@ -2210,7 +2233,7 @@ private class ImageFileNameProviderWrapper extends BaseImageProviderWrapper<Imag
22102233
}
22112234

22122235
@Override
2213-
ImageData getImageData(int zoom) {
2236+
protected ElementAtZoom<ImageData> loadImageData(int zoom) {
22142237
ElementAtZoom<String> fileForZoom = DPIUtil.validateAndGetImagePathAtZoom(provider, zoom);
22152238
ImageHandle nativeInitializedImage;
22162239
if (zoomLevelToImageHandle.containsKey(fileForZoom.zoom())) {
@@ -2226,23 +2249,7 @@ ImageData getImageData(int zoom) {
22262249
imageDataAtZoom = new ElementAtZoom<>(nativeInitializedImage.getImageData(), fileForZoom.zoom());
22272250
destroyHandleForZoom(zoom);
22282251
}
2229-
ImageData imageData = scaleIfNecessary(imageDataAtZoom, zoom);
2230-
imageData = adaptImageDataIfDisabledOrGray(imageData);
2231-
return imageData;
2232-
}
2233-
2234-
private ImageData scaleIfNecessary(ElementAtZoom<ImageData> imageDataAtZoom, int zoom) {
2235-
if (imageDataAtZoom.zoom() != zoom) {
2236-
return scaleImageData(imageDataAtZoom.element(), zoom, imageDataAtZoom.zoom());
2237-
}
2238-
return imageDataAtZoom.element();
2239-
}
2240-
2241-
@Override
2242-
ImageHandle getImageMetadata(int zoom) {
2243-
ImageData imageData = getImageData(zoom);
2244-
init(imageData, zoom);
2245-
return zoomLevelToImageHandle.get(zoom);
2252+
return imageDataAtZoom;
22462253
}
22472254

22482255
@Override
@@ -2455,18 +2462,8 @@ private class ImageDataProviderWrapper extends BaseImageProviderWrapper<ImageDat
24552462
}
24562463

24572464
@Override
2458-
ImageData getImageData(int zoom) {
2459-
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2460-
return scaleImageData(data.element(), zoom, data.zoom());
2461-
}
2462-
2463-
@Override
2464-
ImageHandle getImageMetadata(int zoom) {
2465-
ElementAtZoom<ImageData> imageCandidate = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2466-
ImageData resizedData = scaleImageData(imageCandidate.element(), zoom, imageCandidate.zoom());
2467-
ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
2468-
init(newData, zoom);
2469-
return zoomLevelToImageHandle.get(zoom);
2465+
protected ElementAtZoom<ImageData> loadImageData(int zoom) {
2466+
return DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
24702467
}
24712468

24722469
@Override

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@
2020
import static org.junit.Assert.assertFalse;
2121
import static org.junit.Assert.assertNotNull;
2222
import static org.junit.Assert.assertNull;
23+
import static org.junit.Assert.assertSame;
2324
import static org.junit.Assert.assertTrue;
2425
import static org.junit.Assert.fail;
26+
import static org.junit.Assume.assumeFalse;
27+
import static org.junit.Assume.assumeTrue;
2528

2629
import java.io.File;
2730
import java.io.IOException;
2831
import java.io.InputStream;
2932
import java.net.URL;
33+
import java.nio.file.Files;
34+
import java.nio.file.Path;
35+
import java.util.Comparator;
3036
import java.util.function.Consumer;
3137

3238
import org.eclipse.swt.SWT;
@@ -1262,4 +1268,58 @@ public void test_updateWidthHeightAfterDPIChange() {
12621268
DPIUtil.setDeviceZoom(deviceZoom);
12631269
}
12641270
}
1271+
1272+
@Test
1273+
public void test_imageDataIsCached() {
1274+
assumeTrue("On-demand image creation only implemented for Windows", SwtTestUtil.isWindows);
1275+
String imagePath = getPath("collapseall.png");
1276+
ImageFileNameProvider imageFileNameProvider = __ -> {
1277+
return imagePath;
1278+
};
1279+
Image fileNameProviderImage = new Image(display, imageFileNameProvider);
1280+
assertSame(fileNameProviderImage.getImageData(100), fileNameProviderImage.getImageData(100));
1281+
}
1282+
1283+
@Test
1284+
public void test_imageDataSameViaDifferentProviders() {
1285+
assumeFalse("Cocoa generates inconsistent image data", SwtTestUtil.isCocoa);
1286+
String imagePath = getPath("collapseall.png");
1287+
ImageFileNameProvider imageFileNameProvider = __ -> {
1288+
return imagePath;
1289+
};
1290+
ImageDataProvider dataProvider = __ -> {
1291+
try (InputStream imageStream = Files.newInputStream(Path.of(imagePath))) {
1292+
return new ImageData(imageStream);
1293+
} catch (IOException e) {
1294+
}
1295+
return null;
1296+
};
1297+
Image fileNameProviderImage = new Image(display, imageFileNameProvider);
1298+
Image dataProviderImage = new Image(display, dataProvider);
1299+
ImageData dataFromFileNameProviderImage = fileNameProviderImage.getImageData(100);
1300+
ImageData dataFromImageDescriptorImage = dataProviderImage.getImageData(100);
1301+
assertEquals(0, imageDataComparator().compare(dataFromFileNameProviderImage, dataFromImageDescriptorImage));
1302+
1303+
fileNameProviderImage.dispose();
1304+
dataProviderImage.dispose();
1305+
}
1306+
1307+
private Comparator<ImageData> imageDataComparator() {
1308+
return Comparator.<ImageData>comparingInt(d -> d.width) //
1309+
.thenComparing(d -> d.height) //
1310+
.thenComparing((ImageData firstData, ImageData secondData) -> {
1311+
for (int x = 0; x < firstData.width; x++) {
1312+
for (int y = 0; y < firstData.height; y++) {
1313+
if (firstData.getPixel(x, y) != secondData.getPixel(x, y)) {
1314+
return -1;
1315+
}
1316+
if (firstData.getAlpha(x, y) != secondData.getAlpha(x, y)) {
1317+
return -1;
1318+
}
1319+
}
1320+
}
1321+
return 0;
1322+
});
1323+
}
1324+
12651325
}

0 commit comments

Comments
 (0)