Skip to content

Commit e7af9a1

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 using a calculated zoom.
1 parent 148ea46 commit e7af9a1

File tree

8 files changed

+283
-60
lines changed

8 files changed

+283
-60
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/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: 35 additions & 10 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) {
@@ -1197,7 +1197,7 @@ private int calculateZoomForImage(int gcZoom, int srcWidth, int srcHeight, int d
11971197
// unscaled images can use the GC zoom
11981198
return gcZoom;
11991199
}
1200-
if (!drawable.isAutoScalable()) {
1200+
if (drawable != null && !drawable.isAutoScalable()) {
12011201
return gcZoom;
12021202
}
12031203

@@ -1213,6 +1213,29 @@ 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) {
12181241
Rectangle src = Win32DPIUtils.pointToPixel(drawable, new Rectangle(srcX, srcY, srcWidth, srcHeight), scaledImageZoom);
@@ -1251,17 +1274,19 @@ private class DrawImageToImageOperation extends ImageOperation {
12511274

12521275
@Override
12531276
void apply() {
1254-
drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, getZoom());
1277+
long handle = Image.win32_getHandle(getImage(), getZoom());
1278+
drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, handle);
12551279
}
12561280
}
12571281

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) {
1282+
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) {
12591283
if (data.gdipGraphics != 0) {
12601284
//TODO - cache bitmap
1261-
long [] gdipImage = srcImage.createGdipImage(imageZoom);
1285+
long [] gdipImage = srcImage.createGdipImageFromHandle(tempImageHandle);
12621286
long img = gdipImage[0];
12631287
int imgWidth = Gdip.Image_GetWidth(img);
12641288
int imgHeight = Gdip.Image_GetHeight(img);
1289+
12651290
if (srcWidth == 0 && srcHeight == 0) {
12661291
srcWidth = imgWidth;
12671292
srcHeight = imgHeight;
@@ -1316,13 +1341,13 @@ private void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int src
13161341
}
13171342
return;
13181343
}
1319-
long imageHandle = srcImage.getHandle(imageZoom, data.nativeZoom);
13201344
switch (srcImage.type) {
13211345
case SWT.BITMAP:
1322-
drawBitmap(srcImage, imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
1346+
drawBitmap(srcImage, tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight,
1347+
simple);
13231348
break;
13241349
case SWT.ICON:
1325-
drawIcon(imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
1350+
drawIcon(tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
13261351
break;
13271352
}
13281353
}

0 commit comments

Comments
 (0)