Skip to content

Commit 30de03f

Browse files
committed
DrawImage API in Gc now loads images at the requested size when possible.
The DrawImage API with just destination coordinates now tries to load images at the requested size. If the requested size is not available, it falls back to the closest available size by calculating the appropriate zoom. Images are loadable at the target size if the image is created from an SVG stream or filename, or The image is created with an ImageDataProvider that is ImageDataAtSizeProvider. This commit also introduces the ImageDataAtSizeProvider interface to support dynamic sizing.
1 parent 5ec4863 commit 30de03f

File tree

8 files changed

+218
-34
lines changed

8 files changed

+218
-34
lines changed

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,23 +1818,21 @@ public String toString () {
18181818
*
18191819
* @param gc the GC to draw on the resulting image
18201820
* @param imageData the imageData which is used to draw the scaled Image
1821-
* @param width the width of the original image
1822-
* @param height the height of the original image
1823-
* @param scaleFactor the factor with which the image is supposed to be scaled
1821+
* @param width the width to which the image is supposed to be scaled
1822+
* @param height the height to which the image is supposed to be scaled
18241823
*
18251824
* @noreference This method is not intended to be referenced by clients.
18261825
*/
1827-
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
1826+
public static void drawAtSize(GC gc, ImageData imageData, int width, int height) {
18281827
StrictChecks.runWithStrictChecksDisabled(() -> {
18291828
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
1830-
gc.drawImage(imageToDraw, 0, 0, CocoaDPIUtil.pixelToPoint(width), CocoaDPIUtil.pixelToPoint(height),
1829+
gc.drawImage(imageToDraw, 0, 0, CocoaDPIUtil.pixelToPoint(imageData.width), CocoaDPIUtil.pixelToPoint(imageData.height),
18311830
/*
18321831
* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but
18331832
* avoiding rounding errors. Nevertheless, we still have some rounding errors
18341833
* due to the point-based API GC#drawImage(..).
18351834
*/
1836-
0, 0, Math.round(CocoaDPIUtil.pixelToPoint(width * scaleFactor)),
1837-
Math.round(CocoaDPIUtil.pixelToPoint(height * scaleFactor)));
1835+
0, 0, width, height);
18381836
imageToDraw.dispose();
18391837
});
18401838
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
* Provides an API that is invoked by SWT when an image needs to be drawn at a
17+
* specified width and height.
18+
* <p>
19+
* Client code must implement this interface to supply {@link ImageData} on demand.
20+
* </p>
21+
*
22+
* @since 3.132
23+
*/
24+
public interface ImageDataAtSizeProvider extends ImageDataProvider {
25+
26+
/**
27+
* Returns the {@link ImageData} for the given width and height.
28+
*
29+
* <p>
30+
* <b>Implementation notes:</b>
31+
* </p>
32+
* <ul>
33+
* <li>Returning <code>null</code> is not permitted. If <code>null</code> is
34+
* returned, SWT will throw an exception when loading the image.</li>
35+
* <li>The returned {@link ImageData} must match the requested width and height
36+
* exactly. Implementations should ensure proper resizing and scaling of the
37+
* image based on the height and width requested</li>
38+
* </ul>
39+
*
40+
* @param width the desired width of the {@link ImageData} to be returned
41+
* @param height the desired height of the {@link ImageData} to be returned
42+
* @return the {@link ImageData} that exactly matches the requested width and
43+
* height
44+
* @since 3.132
45+
*/
46+
ImageData getImageData(int width, int height);
47+
48+
}

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::drawAtSize, scaledWidth, scaledHeight);
150+
}
151+
152+
@FunctionalInterface
153+
private interface ImageDrawFunction {
154+
void draw(GC gc, ImageData imageData, int width, int height);
155+
}
156+
157+
private static ImageData scaleImage(Device device, final ImageData imageData, ImageDrawFunction drawFunction,
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, 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::drawAtSize, 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: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,22 +1579,21 @@ public String toString () {
15791579
*
15801580
* @param gc the GC to draw on the resulting image
15811581
* @param imageData the imageData which is used to draw the scaled Image
1582-
* @param width the width of the original image
1583-
* @param height the height of the original image
1584-
* @param scaleFactor the factor with which the image is supposed to be scaled
1582+
* @param width the width to which the image is supposed to be scaled
1583+
* @param height the height to which the image is supposed to be scaled
15851584
*
15861585
* @noreference This method is not intended to be referenced by clients.
15871586
*/
1588-
public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) {
1587+
public static void drawAtSize(GC gc, ImageData imageData, int width, int height) {
15891588
StrictChecks.runWithStrictChecksDisabled(() -> {
15901589
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
1591-
gc.drawImage(imageToDraw, 0, 0, width, height,
1590+
gc.drawImage(imageToDraw, 0, 0, imageData.width, imageData.height,
15921591
/*
15931592
* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but
15941593
* avoiding rounding errors. Nevertheless, we still have some rounding errors
15951594
* due to the point-based API GC#drawImage(..).
15961595
*/
1597-
0, 0, Math.round(width * scaleFactor), Math.round(height * scaleFactor));
1596+
0, 0, width, height);
15981597
imageToDraw.dispose();
15991598
});
16001599
}

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

@@ -1157,8 +1158,7 @@ public void drawImage(Image image, int destX, int destY, int destWidth, int dest
11571158
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
11581159
}
11591160

1160-
storeAndApplyOperationForExistingHandle(new DrawScalingImageToImageOperation(image, new Rectangle(0, 0, 0, 0),
1161-
new Rectangle(destX, destY, destWidth, destHeight)));
1161+
storeAndApplyOperationForExistingHandle(new DrawScaledImageOperation(image, new Rectangle(destX, destY, destWidth, destHeight)));
11621162
}
11631163

11641164
void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) {
@@ -1216,6 +1216,29 @@ private int calculateZoomForImage(int gcZoom, int srcWidth, int srcHeight, int d
12161216
}
12171217
}
12181218

1219+
private class DrawScaledImageOperation extends ImageOperation {
1220+
private final Rectangle destination;
1221+
1222+
DrawScaledImageOperation(Image image, Rectangle destination) {
1223+
super(image);
1224+
this.destination = destination;
1225+
}
1226+
1227+
@Override
1228+
void apply() {
1229+
int gcZoom = getZoom();
1230+
drawImage(getImage(), destination.x, destination.y, destination.width, destination.height, gcZoom);
1231+
}
1232+
}
1233+
1234+
private void drawImage(Image image, int destX, int destY, int destWidth, int destHeight, int imageZoom) {
1235+
Rectangle destPixels = Win32DPIUtils.pointToPixel(drawable, new Rectangle(destX, destY, destWidth, destHeight), imageZoom);
1236+
image.executeOnImageHandleAtSizeWithZoomFallback((tempHandle, handleSize) -> {
1237+
drawImage(image, 0, 0, handleSize.x, handleSize.y, destPixels.x, destPixels.y, destPixels.width,
1238+
destPixels.height, false, tempHandle);
1239+
}, destPixels.width, destPixels.height);
1240+
}
1241+
12191242
private void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY,
12201243
int destWidth, int destHeight, int imageZoom, int scaledImageZoom) {
12211244
Rectangle src = Win32DPIUtils.pointToPixel(drawable, new Rectangle(srcX, srcY, srcWidth, srcHeight), scaledImageZoom);
@@ -1237,7 +1260,7 @@ private void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHei
12371260
}
12381261
}
12391262
}
1240-
drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height, false, scaledImageZoom);
1263+
drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height, false, image.getHandle(scaledImageZoom, data.nativeZoom));
12411264
}
12421265

12431266
private class DrawImageToImageOperation extends ImageOperation {
@@ -1254,17 +1277,19 @@ private class DrawImageToImageOperation extends ImageOperation {
12541277

12551278
@Override
12561279
void apply() {
1257-
drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, getZoom());
1280+
long handle = Image.win32_getHandle(getImage(), getZoom());
1281+
drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, handle);
12581282
}
12591283
}
12601284

1261-
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) {
1285+
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) {
12621286
if (data.gdipGraphics != 0) {
12631287
//TODO - cache bitmap
1264-
long [] gdipImage = srcImage.createGdipImage(imageZoom);
1288+
long [] gdipImage = srcImage.createGdipImageFromHandle(tempImageHandle);
12651289
long img = gdipImage[0];
12661290
int imgWidth = Gdip.Image_GetWidth(img);
12671291
int imgHeight = Gdip.Image_GetHeight(img);
1292+
12681293
if (srcWidth == 0 && srcHeight == 0) {
12691294
srcWidth = imgWidth;
12701295
srcHeight = imgHeight;
@@ -1319,13 +1344,13 @@ private void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int src
13191344
}
13201345
return;
13211346
}
1322-
long imageHandle = srcImage.getHandle(imageZoom, data.nativeZoom);
13231347
switch (srcImage.type) {
13241348
case SWT.BITMAP:
1325-
drawBitmap(srcImage, imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
1349+
drawBitmap(srcImage, tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight,
1350+
simple);
13261351
break;
13271352
case SWT.ICON:
1328-
drawIcon(imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
1353+
drawIcon(tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
13291354
break;
13301355
}
13311356
}

0 commit comments

Comments
 (0)