Skip to content

Commit 386c90b

Browse files
committed
Adding DynamicallySizableImageDataProvider Interface
Implementations of DynamicallySizableImageDataProvider can provide image data at a requested targetHeight and targetWidth. The behavior of GC#drawImage() is updated: when drawing a dynamically sizeable image, it requests a handle at the target size. If the image can be loaded at that size, it is used directly; otherwise, the image is loaded as before and scaled using zoom. In the Windows implementation, if the source or destination width/height is set to zero, the image will be drawn fully, avoiding the need to call getBounds() to determine the image size.
1 parent 148ea46 commit 386c90b

File tree

9 files changed

+323
-59
lines changed

9 files changed

+323
-59
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,11 +1820,12 @@ public String toString () {
18201820
* @param imageData the imageData which is used to draw the scaled Image
18211821
* @param width the width of the original image
18221822
* @param height the height of the original image
1823-
* @param scaleFactor the factor with which the image is supposed to be scaled
1823+
* @param targetWidth the width to which the image is supposed to be scaled
1824+
* @param targetHeight the height to which the image is supposed to be scaled
18241825
*
18251826
* @noreference This method is not intended to be referenced by clients.
18261827
*/
1827-
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
1828+
public static void drawAtTargetSize(GC gc, ImageData imageData, int width, int height, int targetWidth, int targetHeight) {
18281829
StrictChecks.runWithStrictChecksDisabled(() -> {
18291830
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
18301831
gc.drawImage(imageToDraw, 0, 0, CocoaDPIUtil.pixelToPoint(width), CocoaDPIUtil.pixelToPoint(height),
@@ -1833,8 +1834,7 @@ public static void drawScaled(GC gc, ImageData imageData, int width, int height,
18331834
* avoiding rounding errors. Nevertheless, we still have some rounding errors
18341835
* due to the point-based API GC#drawImage(..).
18351836
*/
1836-
0, 0, Math.round(CocoaDPIUtil.pixelToPoint(width * scaleFactor)),
1837-
Math.round(CocoaDPIUtil.pixelToPoint(height * scaleFactor)));
1837+
0, 0, targetWidth, targetHeight);
18381838
imageToDraw.dispose();
18391839
});
18401840
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Vector Informatik GmbH and others.
3+
*
4+
* This program and the accompanying materials are made available under the terms of the Eclipse
5+
* Public License 2.0 which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Michael Bangas (Vector Informatik GmbH) - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.swt.graphics;
14+
15+
/**
16+
* @since 3.132
17+
*/
18+
public interface ImageDataAtSizeProvider extends ImageDataProvider {
19+
20+
ImageData getImageData(int targetWidth, int targetHeight);
21+
22+
}

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ public static boolean canLoadAtZoom(String filename, int fileZoom, int targetZoo
4545
return ImageLoader.canLoadAtZoom(filename, fileZoom, targetZoom);
4646
}
4747

48+
static boolean isDynamicallySizable(String filename) {
49+
return ImageLoader.isDynamicallySizable(filename);
50+
}
51+
52+
static boolean isDynamicallySizable(InputStream stream) {
53+
return ImageLoader.isDynamicallySizable(stream);
54+
}
55+
4856
public static ElementAtZoom<ImageData> loadByZoom(InputStream stream, int fileZoom, int targetZoom) {
4957
List<ElementAtZoom<ImageData>> data = new ImageLoader().loadByZoom(stream, fileZoom, targetZoom);
5058
if (data.isEmpty()) SWT.error(SWT.ERROR_INVALID_IMAGE);

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,19 @@ static boolean canLoadAtZoom(String filename, int fileZoom, int targetZoom) {
229229
return false;
230230
}
231231

232+
static boolean isDynamicallySizable(String filename) {
233+
try (InputStream stream = new FileInputStream(filename)) {
234+
return FileFormat.isDynamicallySizableFormat(stream);
235+
} catch (IOException e) {
236+
SWT.error(SWT.ERROR_IO, e);
237+
}
238+
return false;
239+
}
240+
241+
static boolean isDynamicallySizable(InputStream stream) {
242+
return FileFormat.isDynamicallySizableFormat(stream);
243+
}
244+
232245
/**
233246
* Saves the image data in this ImageLoader to the specified stream.
234247
* The format parameter can have one of the following values:

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,24 @@ public static ImageData autoScaleImageData (Device device, final ImageData image
146146
int height = imageData.height;
147147
int scaledWidth = Math.round (width * scaleFactor);
148148
int scaledHeight = Math.round (height * scaleFactor);
149+
return scaleImage(device, imageData, Image::drawAtTargetSize, width, height, scaledWidth, scaledHeight);
150+
}
151+
152+
@FunctionalInterface
153+
private interface ImageDrawFunction {
154+
void draw(GC gc, ImageData imageData, int originalWidth, int originalHeight, int targetWidth, int targetHeight);
155+
}
156+
157+
private static ImageData scaleImage(Device device, final ImageData imageData, ImageDrawFunction drawFunction, int width, int height,
158+
int scaledWidth, int scaledHeight) {
149159
int defaultZoomLevel = 100;
150160
boolean useSmoothScaling = isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
151161
if (useSmoothScaling) {
152162
ImageGcDrawer drawer = new ImageGcDrawer() {
153163
@Override
154164
public void drawOn(GC gc, int imageWidth, int imageHeight) {
155165
gc.setAntialias (SWT.ON);
156-
Image.drawScaled(gc, imageData, width, height, scaleFactor);
166+
drawFunction.draw(gc, imageData, width, height, imageWidth, imageHeight);
157167
};
158168

159169
@Override
@@ -170,6 +180,10 @@ public int getGcStyle() {
170180
}
171181
}
172182

183+
public static ImageData autoScaleImageData(Device device, final ImageData imageData, int targetWidth, int targetHeight) {
184+
return scaleImage(device, imageData, Image::drawAtTargetSize, imageData.width, imageData.height, targetWidth, targetHeight);
185+
}
186+
173187
public static boolean isSmoothScalingEnabled() {
174188
return autoScaleMethod == AutoScaleMethod.SMOOTH;
175189
}

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,16 @@ private static Optional<FileFormat> determineFileFormat(LEDataInputStream stream
7373
private static final int MAX_SIGNATURE_BYTES = 18 + 2; // e.g. Win-BMP or OS2-BMP plus a safety-margin
7474

7575
public static boolean isDynamicallySizableFormat(InputStream is) {
76-
Optional<FileFormat> format = determineFileFormat(new LEDataInputStream(is, MAX_SIGNATURE_BYTES));
76+
if (!is.markSupported()) {
77+
is = new BufferedInputStream(is);
78+
}
79+
is.mark(MAX_SIGNATURE_BYTES);
80+
Optional<FileFormat> format = determineFileFormat(new LEDataInputStream(is, MAX_SIGNATURE_BYTES));
81+
try {
82+
is.reset();
83+
} catch (IOException e) {
84+
e.printStackTrace();
85+
}
7786
return format.isPresent() && !(format.get() instanceof StaticImageFileFormat);
7887
}
7988

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,11 +1581,12 @@ public String toString () {
15811581
* @param imageData the imageData which is used to draw the scaled Image
15821582
* @param width the width of the original image
15831583
* @param height the height of the original image
1584-
* @param scaleFactor the factor with which the image is supposed to be scaled
1584+
* @param targetWidth the width to which the image is supposed to be scaled
1585+
* @param targetHeight the height to which the image is supposed to be scaled
15851586
*
15861587
* @noreference This method is not intended to be referenced by clients.
15871588
*/
1588-
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
1589+
public static void drawAtTargetSize(GC gc, ImageData imageData, int width, int height, int targetWidth, int targetHeight) {
15891590
StrictChecks.runWithStrictChecksDisabled(() -> {
15901591
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
15911592
gc.drawImage(imageToDraw, 0, 0, width, height,
@@ -1594,7 +1595,7 @@ public static void drawScaled(GC gc, ImageData imageData, int width, int height,
15941595
* avoiding rounding errors. Nevertheless, we still have some rounding errors
15951596
* due to the point-based API GC#drawImage(..).
15961597
*/
1597-
0, 0, Math.round(width * scaleFactor), Math.round(height * scaleFactor));
1598+
0, 0, targetWidth, targetHeight);
15981599
imageToDraw.dispose();
15991600
});
16001601
}

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

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,8 @@ void apply() {
10611061

10621062
private void drawImageInPixels(Image image, Point location) {
10631063
if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1064-
drawImage(image, 0, 0, -1, -1, location.x, location.y, -1, -1, true, getZoom());
1064+
long handle = Image.win32_getHandle(image, getZoom());
1065+
drawImage(image, 0, 0, -1, -1, location.x, location.y, -1, -1, true, handle);
10651066
}
10661067
}
10671068

@@ -1154,8 +1155,7 @@ public void drawImage(Image image, int destX, int destY, int destWidth, int dest
11541155
if (image.isDisposed())
11551156
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
11561157

1157-
storeAndApplyOperationForExistingHandle(new DrawScalingImageToImageOperation(image, new Rectangle(0, 0, 0, 0),
1158-
new Rectangle(destX, destY, destWidth, destHeight)));
1158+
storeAndApplyOperationForExistingHandle(new DrawScaledImageOperation(image, new Rectangle(destX, destY, destWidth, destHeight)));
11591159
}
11601160

11611161
void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) {
@@ -1213,15 +1213,40 @@ private int calculateZoomForImage(int gcZoom, int srcWidth, int srcHeight, int d
12131213
}
12141214
}
12151215

1216+
private class DrawScaledImageOperation extends ImageOperation {
1217+
private final Rectangle destination;
1218+
1219+
DrawScaledImageOperation(Image image, Rectangle destination) {
1220+
super(image);
1221+
this.destination = destination;
1222+
}
1223+
1224+
@Override
1225+
void apply() {
1226+
int gcZoom = getZoom();
1227+
drawImage(getImage(), destination.x, destination.y, destination.width, destination.height, gcZoom);
1228+
}
1229+
}
1230+
1231+
private void drawImage(Image image, int destX, int destY, int destWidth, int destHeight, int imageZoom) {
1232+
Rectangle destPixels = Win32DPIUtils.pointToPixel(drawable, new Rectangle(destX, destY, destWidth, destHeight), imageZoom);
1233+
image.executeOnImageHandleAtSizeWithZoomFallback((tempHandle, handleSize) -> {
1234+
drawImage(image, 0, 0, handleSize.x, handleSize.y, destPixels.x, destPixels.y, destPixels.width,
1235+
destPixels.height, false, tempHandle);
1236+
}, destPixels.width, destPixels.height);
1237+
}
1238+
12161239
private void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY,
12171240
int destWidth, int destHeight, int imageZoom, int scaledImageZoom) {
1218-
Rectangle src = Win32DPIUtils.pointToPixel(drawable, new Rectangle(srcX, srcY, srcWidth, srcHeight), scaledImageZoom);
1219-
Rectangle dest = Win32DPIUtils.pointToPixel(drawable, new Rectangle(destX, destY, destWidth, destHeight), imageZoom);
1220-
if (scaledImageZoom != 100) {
1241+
Rectangle src = Win32DPIUtils.pointToPixel(drawable, new Rectangle(srcX, srcY, srcWidth, srcHeight),
1242+
scaledImageZoom);
1243+
Rectangle destPixels = Win32DPIUtils.pointToPixel(drawable, new Rectangle(destX, destY, destWidth, destHeight),
1244+
imageZoom);
1245+
if (scaledImageZoom % 100 != 0) {
12211246
/*
1222-
* This is a HACK! Due to rounding errors at fractional scale factors,
1223-
* the coordinates may be slightly off. The workaround is to restrict
1224-
* coordinates to the allowed bounds.
1247+
* This is a HACK! Due to rounding errors at fractional scale factors, the
1248+
* coordinates may be slightly off. The workaround is to restrict coordinates to
1249+
* the allowed bounds.
12251250
*/
12261251
Rectangle b = image.getBounds(scaledImageZoom);
12271252
int errX = src.x + src.width - b.width;
@@ -1234,7 +1259,24 @@ private void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHei
12341259
}
12351260
}
12361261
}
1237-
drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height, false, scaledImageZoom);
1262+
int targetWidth;
1263+
int targetHeight;
1264+
Rectangle scaledSrc;
1265+
1266+
float widthScalingFactor = (float) destWidth / srcWidth;
1267+
float heightScalingFactor = (float) destHeight / srcHeight;
1268+
Rectangle fullImageBounds = image.getBounds();
1269+
targetWidth = Math.round(fullImageBounds.width * widthScalingFactor);
1270+
targetHeight = Math.round(fullImageBounds.height * heightScalingFactor);
1271+
1272+
scaledSrc = new Rectangle(Math.round(src.x * widthScalingFactor), Math.round(src.y * heightScalingFactor),
1273+
Math.round(src.width * widthScalingFactor), Math.round(src.height * heightScalingFactor));
1274+
Point targetSize = Win32DPIUtils.pointToPixel(drawable, new Point(targetWidth, targetHeight), scaledImageZoom);
1275+
image.executeOnImageHandleAtSizeOrZoom((tempHandle, handleSize) -> {
1276+
Rectangle srcRect = (handleSize.equals(targetSize)) ? scaledSrc : src;
1277+
drawImage(image, srcRect.x, srcRect.y, srcRect.width, srcRect.height, destPixels.x, destPixels.y,
1278+
destPixels.width, destPixels.height, false, tempHandle);
1279+
}, targetSize.x, targetSize.y, scaledImageZoom);
12381280
}
12391281

12401282
private class DrawImageToImageOperation extends ImageOperation {
@@ -1251,17 +1293,19 @@ private class DrawImageToImageOperation extends ImageOperation {
12511293

12521294
@Override
12531295
void apply() {
1254-
drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, getZoom());
1296+
long handle = Image.win32_getHandle(getImage(), getZoom());
1297+
drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, handle);
12551298
}
12561299
}
12571300

1258-
private void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, int imageZoom) {
1301+
private void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, long tempImageHandle) {
12591302
if (data.gdipGraphics != 0) {
12601303
//TODO - cache bitmap
1261-
long [] gdipImage = srcImage.createGdipImage(imageZoom);
1304+
long [] gdipImage = srcImage.createGdipImageFromHandle(tempImageHandle);
12621305
long img = gdipImage[0];
12631306
int imgWidth = Gdip.Image_GetWidth(img);
12641307
int imgHeight = Gdip.Image_GetHeight(img);
1308+
12651309
if (srcWidth == 0 && srcHeight == 0) {
12661310
srcWidth = imgWidth;
12671311
srcHeight = imgHeight;
@@ -1316,13 +1360,13 @@ private void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int src
13161360
}
13171361
return;
13181362
}
1319-
long imageHandle = srcImage.getHandle(imageZoom, data.nativeZoom);
13201363
switch (srcImage.type) {
13211364
case SWT.BITMAP:
1322-
drawBitmap(srcImage, imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
1365+
drawBitmap(srcImage, tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight,
1366+
simple);
13231367
break;
13241368
case SWT.ICON:
1325-
drawIcon(imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
1369+
drawIcon(tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
13261370
break;
13271371
}
13281372
}

0 commit comments

Comments
 (0)