Skip to content

Commit 007426f

Browse files
arunjose696HeikoKlare
authored andcommitted
Fix stack overflow in DPIUtil.autoScaleImageData #2353
Recently, a strict check was introduced that ImageData should be linearly scaled for other zoom levels. ImageDataProviders inside SWT not following this strict checks were modified (e.g., to return only image data at 100%). However, this change broke DPIUtil.autoScaleImageData. That function creates an Image using the system zoom level, draws it on a GC at scaled dimensions, and then requests ImageData at 100% zoom from the image. Since the ImageDataProvider was now restricted to 100% zoom only, requesting imageData at other zoom levels while creating an image at system zoom level triggered recursive calls, causing a stack overflow error. This commit reverts the behavior of the ImageDataProvider used in DPIUtil.autoScaleImageData to return the same ImageData at all zoom levels. We also disable the strict zoom check in this specific case, because the scaling is done by GC.drawImage() and the ImageData used to create the is expected to be same. Fixes #2353
1 parent 7f19dc5 commit 007426f

File tree

5 files changed

+47
-15
lines changed

5 files changed

+47
-15
lines changed

bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
package org.eclipse.swt.widgets;
1515

1616
import static org.junit.Assert.*;
17+
import static org.junit.jupiter.api.Assertions.assertEquals;
1718

1819
import org.eclipse.swt.*;
1920
import org.eclipse.swt.graphics.*;
2021
import org.eclipse.swt.internal.*;
2122
import org.junit.jupiter.api.*;
2223
import org.junit.jupiter.api.extension.*;
24+
import org.junit.jupiter.params.*;
25+
import org.junit.jupiter.params.provider.*;
2326

2427
/**
2528
* Automated Tests for class org.eclipse.swt.widgets.Control for Windows
@@ -107,6 +110,28 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() {
107110
new Rectangle(0, 82, 350, 83), button.getBoundsInPixels());
108111
}
109112

113+
@ParameterizedTest
114+
@CsvSource({ "0.5, 100, true", "1.0, 200, true", "2.0, 200, true", "2.0, quarter, true", "0.5, 100, false",
115+
"1.0, 200, false", "2.0, 200, false", "2.0, quarter, false", })
116+
public void testAutoScaleImageData(float scaleFactor, String autoScale, boolean monitorSpecificScaling) {
117+
DPIUtil.setMonitorSpecificScaling(monitorSpecificScaling);
118+
DPIUtil.runWithAutoScaleValue(autoScale, () -> {
119+
Display display = new Display();
120+
try {
121+
ImageData imageData = new ImageData(100, 100, 1, new PaletteData(new RGB(0, 0, 0)));
122+
int width = imageData.width;
123+
int height = imageData.height;
124+
int scaledWidth = Math.round(width * scaleFactor);
125+
int scaledHeight = Math.round(height * scaleFactor);
126+
ImageData scaledImageData = DPIUtil.autoScaleImageData(display, imageData, scaleFactor);
127+
assertEquals(scaledWidth, scaledImageData.width);
128+
assertEquals(scaledHeight, scaledImageData.height);
129+
} finally {
130+
display.dispose();
131+
}
132+
});
133+
}
134+
110135
record FontComparison(int originalFontHeight, int currentFontHeight) {
111136
}
112137

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,22 +1811,24 @@ public String toString () {
18111811
* API for Image. It is marked public only so that it
18121812
* can be shared within the packages provided by SWT.
18131813
*
1814-
* Draws a scaled image using the GC by another image.
1814+
* Draws a scaled image using the GC for a given imageData.
18151815
*
18161816
* @param gc the GC to draw on the resulting image
1817-
* @param original the image which is supposed to be scaled and drawn on the resulting image
1817+
* @param imageData the imageData which is used to draw the scaled Image
18181818
* @param width the width of the original image
18191819
* @param height the height of the original image
18201820
* @param scaleFactor the factor with which the image is supposed to be scaled
18211821
*
18221822
* @noreference This method is not intended to be referenced by clients.
18231823
*/
1824-
public static void drawScaled(GC gc, Image original, int width, int height, float scaleFactor) {
1825-
gc.drawImage (original, 0, 0, CocoaDPIUtil.pixelToPoint (width), CocoaDPIUtil.pixelToPoint (height),
1824+
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
1825+
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
1826+
gc.drawImage (imageToDraw, 0, 0, CocoaDPIUtil.pixelToPoint (width), CocoaDPIUtil.pixelToPoint (height),
18261827
/* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but avoiding rounding errors.
18271828
* Nevertheless, we still have some rounding errors due to the point-based API GC#drawImage(..).
18281829
*/
18291830
0, 0, Math.round (CocoaDPIUtil.pixelToPoint (width * scaleFactor)), Math.round (CocoaDPIUtil.pixelToPoint (height * scaleFactor)));
1831+
imageToDraw.dispose();
18301832
}
18311833

18321834
private final class CocoaDPIUtil {

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,11 @@ public static ImageData autoScaleImageData (Device device, final ImageData image
153153
int defaultZoomLevel = 100;
154154
boolean useSmoothScaling = isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
155155
if (useSmoothScaling) {
156-
Image original = new Image(device, (ImageDataProvider) zoom -> (zoom == defaultZoomLevel) ? imageData : null);
157156
ImageGcDrawer drawer = new ImageGcDrawer() {
158157
@Override
159158
public void drawOn(GC gc, int imageWidth, int imageHeight) {
160159
gc.setAntialias (SWT.ON);
161-
Image.drawScaled(gc, original, width, height, scaleFactor);
160+
Image.drawScaled(gc, imageData, width, height, scaleFactor);
162161
};
163162

164163
@Override
@@ -168,7 +167,6 @@ public int getGcStyle() {
168167
};
169168
Image resultImage = new Image (device, drawer, scaledWidth, scaledHeight);
170169
ImageData result = resultImage.getImageData (defaultZoomLevel);
171-
original.dispose ();
172170
resultImage.dispose ();
173171
return result;
174172
} else {

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,22 +1572,24 @@ public String toString () {
15721572
* API for Image. It is marked public only so that it
15731573
* can be shared within the packages provided by SWT.
15741574
*
1575-
* Draws a scaled image using the GC by another image.
1575+
* Draws a scaled image using the GC for a given imageData.
15761576
*
15771577
* @param gc the GC to draw on the resulting image
1578-
* @param original the image which is supposed to be scaled and drawn on the resulting image
1578+
* @param imageData the imageData which is used to draw the scaled Image
15791579
* @param width the width of the original image
15801580
* @param height the height of the original image
15811581
* @param scaleFactor the factor with which the image is supposed to be scaled
15821582
*
15831583
* @noreference This method is not intended to be referenced by clients.
15841584
*/
1585-
public static void drawScaled(GC gc, Image original, int width, int height, float scaleFactor) {
1586-
gc.drawImage (original, 0, 0, width, height,
1585+
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
1586+
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
1587+
gc.drawImage (imageToDraw, 0, 0, width, height,
15871588
/* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but avoiding rounding errors.
15881589
* Nevertheless, we still have some rounding errors due to the point-based API GC#drawImage(..).
15891590
*/
15901591
0, 0, Math.round (width * scaleFactor), Math.round (height * scaleFactor));
1592+
imageToDraw.dispose();
15911593
}
15921594

15931595
private final class GtkDPIUtil {

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -839,19 +839,24 @@ public static long win32_getHandle (Image image, int zoom) {
839839
* API for Image. It is marked public only so that it
840840
* can be shared within the packages provided by SWT.
841841
*
842-
* Draws a scaled image using the GC by another image.
842+
* Draws a scaled image using the GC for a given imageData.
843843
*
844844
* @param gc the GC to draw on the resulting image
845-
* @param original the image which is supposed to be scaled and drawn on the resulting image
845+
* @param imageData the imageData which is used to draw the scaled Image
846846
* @param width the width of the original image
847847
* @param height the height of the original image
848848
* @param scaleFactor the factor with which the image is supposed to be scaled
849849
*
850850
* @noreference This method is not intended to be referenced by clients.
851851
*/
852-
public static void drawScaled(GC gc, Image original, int width, int height, float scaleFactor) {
853-
gc.drawImage (original, 0, 0, width, height,
852+
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
853+
boolean originalStrictChecks = Device.strictChecks;
854+
Device.strictChecks = false;
855+
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
856+
gc.drawImage (imageToDraw, 0, 0, width, height,
854857
0, 0, Math.round (width * scaleFactor), Math.round (height * scaleFactor), false);
858+
Device.strictChecks = originalStrictChecks;
859+
imageToDraw.dispose();
855860
}
856861

857862
long [] createGdipImage(Integer zoom) {

0 commit comments

Comments
 (0)