Skip to content

Commit 8dcb140

Browse files
committed
Fix stack overflow in DPIUtil.autoScaleImageData
In PR #2249, a strict check was introduced that ImageData should be linearly scaled for other zoom levels. And imageDataProviders in SWT repo not following this strict checks were modified (eg to return only imageData 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. The current 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.
1 parent ebcb6b9 commit 8dcb140

File tree

5 files changed

+49
-17
lines changed

5 files changed

+49
-17
lines changed

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

Lines changed: 24 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,27 @@ 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 autoScaleImageData0(float scaleFactor, String autoScale, boolean monitorSpecificScaling) {
117+
DPIUtil.setMonitorSpecificScaling(monitorSpecificScaling);
118+
DPIUtil.setAutoScaleValue(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+
110134
record FontComparison(int originalFontHeight, int currentFontHeight) {
111135
}
112136

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,18 +1811,19 @@ 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
*/

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ public static Optional<AutoScaleMethod> forString(String s) {
108108
*/
109109
private static final String SWT_AUTOSCALE_UPDATE_ON_RUNTIME = "swt.autoScale.updateOnRuntime";
110110
static {
111-
autoScaleValue = System.getProperty (SWT_AUTOSCALE);
112-
111+
setAutoScaleValue(System.getProperty (SWT_AUTOSCALE));
112+
113113
String value = System.getProperty (SWT_AUTOSCALE_METHOD);
114114
AUTO_SCALE_METHOD_SETTING = AutoScaleMethod.forString(value).orElse(AutoScaleMethod.AUTO);
115115
autoScaleMethod = AUTO_SCALE_METHOD_SETTING != AutoScaleMethod.AUTO ? AUTO_SCALE_METHOD_SETTING : AutoScaleMethod.NEAREST;
@@ -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 {
@@ -394,6 +392,10 @@ public static void setMonitorSpecificScaling(boolean activate) {
394392
System.setProperty(SWT_AUTOSCALE_UPDATE_ON_RUNTIME, Boolean.toString(activate));
395393
}
396394

395+
public static void setAutoScaleValue(String autoScaleValue) {
396+
DPIUtil.autoScaleValue = autoScaleValue;
397+
}
398+
397399
public static boolean isMonitorSpecificScalingActive() {
398400
boolean updateOnRuntimeValue = Boolean.getBoolean (SWT_AUTOSCALE_UPDATE_ON_RUNTIME);
399401
return updateOnRuntimeValue;

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,18 +1574,19 @@ public String toString () {
15741574
* API for Image. It is marked public only so that it
15751575
* can be shared within the packages provided by SWT.
15761576
*
1577-
* Draws a scaled image using the GC by another image.
1577+
* Draws a scaled image using the GC for a given imageData.
15781578
*
15791579
* @param gc the GC to draw on the resulting image
1580-
* @param original the image which is supposed to be scaled and drawn on the resulting image
1580+
* @param imageData the imageData which is used to draw the scaled Image
15811581
* @param width the width of the original image
15821582
* @param height the height of the original image
15831583
* @param scaleFactor the factor with which the image is supposed to be scaled
15841584
*
15851585
* @noreference This method is not intended to be referenced by clients.
15861586
*/
1587-
public static void drawScaled(GC gc, Image original, int width, int height, float scaleFactor) {
1588-
gc.drawImage (original, 0, 0, width, height,
1587+
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
1588+
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
1589+
gc.drawImage (imageToDraw, 0, 0, width, height,
15891590
/* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but avoiding rounding errors.
15901591
* Nevertheless, we still have some rounding errors due to the point-based API GC#drawImage(..).
15911592
*/

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -839,19 +839,23 @@ 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;
855859
}
856860

857861
long [] createGdipImage(Integer zoom) {

0 commit comments

Comments
 (0)