Skip to content

Commit 96bf079

Browse files
[FIXUP] Improve consideration of file- and target/device-zoom
Co-authored-by: Heiko Klare <[email protected]>
1 parent 4a612fb commit 96bf079

File tree

12 files changed

+178
-94
lines changed

12 files changed

+178
-94
lines changed

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2020 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -19,6 +19,7 @@
1919

2020
import org.eclipse.swt.*;
2121
import org.eclipse.swt.internal.*;
22+
import org.eclipse.swt.internal.DPIUtil.*;
2223
import org.eclipse.swt.internal.cocoa.*;
2324
import org.eclipse.swt.internal.graphics.*;
2425

@@ -692,6 +693,7 @@ public Image(Device device, InputStream stream) {
692693
NSAutoreleasePool pool = null;
693694
if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
694695
try {
696+
// TODO: scale SVGs here too?
695697
init(new ImageData(stream));
696698
init();
697699
} finally {
@@ -738,6 +740,7 @@ public Image(Device device, String filename) {
738740
try {
739741
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
740742
initNative(filename);
743+
// TODO: scale SVGs here too?
741744
if (this.handle == null) init(new ImageData(filename));
742745
init();
743746
} finally {
@@ -778,6 +781,7 @@ public Image(Device device, ImageFileNameProvider imageFileNameProvider) {
778781
super(device);
779782
if (imageFileNameProvider == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
780783
this.imageFileNameProvider = imageFileNameProvider;
784+
//TODO: implement fine-grained zoom handling here as well?
781785
String filename = imageFileNameProvider.getImagePath(100);
782786
if (filename == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
783787
NSAutoreleasePool pool = null;
@@ -792,6 +796,14 @@ public Image(Device device, ImageFileNameProvider imageFileNameProvider) {
792796
id id = NSImageRep.imageRepWithContentsOfFile(NSString.stringWith(filename2x));
793797
NSImageRep rep = new NSImageRep(id);
794798
handle.addRepresentation(rep);
799+
} else {
800+
// Try to natively scale up the image (e.g. possible if it's an SVG)
801+
ElementAtZoom<ImageData> imageData2x = ImageDataLoader.load(filename, 100, 200);
802+
if (imageData2x.zoom() == 200) {
803+
Image image2x = new Image(device, imageData2x.element());
804+
NSImageRep rep = ImageUtil.createImageRep(image2x, image2x.width, image2x.height);
805+
handle.addRepresentation(rep);
806+
}
795807
}
796808
} finally {
797809
if (pool != null) pool.release();
@@ -880,7 +892,7 @@ public Image(Device device, ImageGcDrawer imageGcDrawer, int width, int height)
880892
if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
881893
try {
882894
init (data);
883-
init ();
895+
init ();
884896
} finally {
885897
if (pool != null) pool.release();
886898
}
@@ -1193,19 +1205,18 @@ NSBitmapImageRep getRepresentation (int scaleFactor) {
11931205
NSArray reps = handle.representations();
11941206
NSSize size = handle.size();
11951207
long count = reps.count();
1196-
NSSize targetSize = new NSSize();
1197-
targetSize.width = (int)size.width * scaleFactor / 100;
1198-
targetSize.height = (int)size.height * scaleFactor / 100;
1208+
int width = (int) size.width * scaleFactor / 100;
1209+
int height = (int) size.height * scaleFactor / 100;
11991210
NSBitmapImageRep rep;
12001211
for (int i = 0; i < count; i++) {
12011212
rep = new NSBitmapImageRep(reps.objectAtIndex(i));
1202-
if ((targetSize.width == rep.pixelsWide() && targetSize.height == rep.pixelsHigh())) {
1213+
if (width == rep.pixelsWide() && height == rep.pixelsHigh()) {
12031214
if (rep.isKindOfClass(OS.class_NSBitmapImageRep)) {
12041215
return rep;
12051216
}
12061217
}
12071218
}
1208-
NSBitmapImageRep newRep = createImageRep(targetSize);
1219+
NSBitmapImageRep newRep = createImageRep(width, height);
12091220
for (int i = 0; i < count; i++) {
12101221
handle.removeRepresentation(new NSImageRep(handle.representations().objectAtIndex(0)));
12111222
}
@@ -1397,8 +1408,8 @@ NSBitmapImageRep getRepresentation () {
13971408
return getRepresentation (DPIUtil.getDeviceZoom ());
13981409
}
13991410

1400-
NSBitmapImageRep createImageRep(NSSize targetSize) {
1401-
return ImageUtil.createImageRep(this, targetSize);
1411+
NSBitmapImageRep createImageRep(int targetWidth, int targetHeight) {
1412+
return ImageUtil.createImageRep(this, targetWidth, targetHeight);
14021413
}
14031414

14041415
/**

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2016 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -18,6 +18,7 @@
1818
import java.util.*;
1919

2020
import org.eclipse.swt.*;
21+
import org.eclipse.swt.internal.DPIUtil.*;
2122
import org.eclipse.swt.internal.image.*;
2223

2324
/**
@@ -149,10 +150,16 @@ void reset() {
149150
* </ul>
150151
*/
151152
public ImageData[] load(InputStream stream) {
153+
load(stream, FileFormat.DEFAULT_ZOOM, FileFormat.DEFAULT_ZOOM);
154+
return data;
155+
}
156+
157+
List<ElementAtZoom<ImageData>> load(InputStream stream, int fileZoom, int targetZoom) {
152158
if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
153159
reset();
154-
data = FileFormat.load(stream, this, FileFormat.DEFAULT_ZOOM);
155-
return data;
160+
List<ElementAtZoom<ImageData>> images = FileFormat.load(stream, this, fileZoom, targetZoom);
161+
data = images.stream().map(ElementAtZoom::element).toArray(ImageData[]::new);
162+
return images;
156163
}
157164

158165
/**
@@ -174,9 +181,14 @@ public ImageData[] load(InputStream stream) {
174181
* </ul>
175182
*/
176183
public ImageData[] load(String filename) {
184+
load(filename, FileFormat.DEFAULT_ZOOM, FileFormat.DEFAULT_ZOOM);
185+
return data;
186+
}
187+
188+
List<ElementAtZoom<ImageData>> load(String filename, int fileZoom, int targetZoom) {
177189
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
178190
try (InputStream stream = new FileInputStream(filename)) {
179-
return load(stream);
191+
return load(stream, fileZoom, targetZoom);
180192
} catch (IOException e) {
181193
SWT.error(SWT.ERROR_IO, e);
182194
}

bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/graphics/ImageUtil.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,25 @@ public class ImageUtil {
2828
* Creates new image representation based on the source image.
2929
*
3030
* @param image Source image object
31-
* @param targetSize the size at which image representation needs to be created
31+
* @param targetWidth the width at which image representation needs to be created
32+
* @param targetHeight the height at which image representation needs to be created
3233
*
3334
* @return image representation
3435
*
3536
* @since 3.110
3637
*/
37-
public static NSBitmapImageRep createImageRep(Image image, NSSize targetSize) {
38-
NSBitmapImageRep rep;
38+
public static NSBitmapImageRep createImageRep(Image image, int targetWidth, int targetHeight) {
3939
NSImage imgHandle= image.handle;
40-
rep = (NSBitmapImageRep)new NSBitmapImageRep().alloc();
41-
rep = rep.initWithBitmapDataPlanes(0, (int) targetSize.width, (int) targetSize.height, 8, 4, true, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat, (int) targetSize.width * 4, 32);
42-
C.memset(rep.bitmapData(), 0xFF, (int) targetSize.width * (int)targetSize.height * 4);
40+
NSBitmapImageRep rep = (NSBitmapImageRep) new NSBitmapImageRep().alloc();
41+
rep = rep.initWithBitmapDataPlanes(0, targetWidth, targetHeight, 8, 4, true, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat, targetWidth * 4, 32);
42+
C.memset(rep.bitmapData(), 0xFF, targetWidth * targetHeight * 4);
4343
NSGraphicsContext context = NSGraphicsContext.graphicsContextWithBitmapImageRep(rep);
4444
NSGraphicsContext.static_saveGraphicsState();
4545
context.setImageInterpolation(OS.NSImageInterpolationHigh);
4646
NSGraphicsContext.setCurrentContext(context);
4747
NSRect target = new NSRect();
48-
target.width = targetSize.width;
49-
target.height = targetSize.height;
48+
target.width = targetWidth;
49+
target.height = targetHeight;
5050
NSRect sourceRect = new NSRect();
5151
sourceRect.width = 0;
5252
sourceRect.height = 0;

bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Canvas.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ void drawWidget (long id, NSGraphicsContext context, NSRect rect) {
183183
if (image != null) {
184184
NSImage imageHandle = image.handle;
185185
NSSize size = imageHandle.size();
186-
NSImageRep imageRep = ImageUtil.createImageRep(image, size);
186+
NSImageRep imageRep = ImageUtil.createImageRep(image, (int) size.width, (int) size.height);
187187
if (!imageRep.isKindOfClass(OS.class_NSBitmapImageRep)) return;
188188
NSBitmapImageRep rep = new NSBitmapImageRep(imageRep);
189189
CGRect destRect = new CGRect ();

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -331,9 +331,7 @@ scanlinePad, checkData(data), 0, null,
331331
* @see ImageLoader#load(InputStream)
332332
*/
333333
public ImageData(InputStream stream) {
334-
ImageData[] data = ImageDataLoader.load(stream);
335-
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
336-
ImageData i = data[0];
334+
ImageData i = ImageDataLoader.load(stream);
337335
setAllFields(
338336
i.width,
339337
i.height,
@@ -377,9 +375,7 @@ public ImageData(InputStream stream) {
377375
* </ul>
378376
*/
379377
public ImageData(String filename) {
380-
ImageData[] data = ImageDataLoader.load(filename);
381-
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
382-
ImageData i = data[0];
378+
ImageData i = ImageDataLoader.load(filename);
383379
setAllFields(
384380
i.width,
385381
i.height,

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2006 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -14,19 +14,39 @@
1414
package org.eclipse.swt.graphics;
1515

1616
import java.io.*;
17+
import java.util.*;
18+
19+
import org.eclipse.swt.*;
20+
import org.eclipse.swt.internal.DPIUtil.*;
1721

1822
/**
1923
* Internal class that separates ImageData from ImageLoader
2024
* to allow removal of ImageLoader from the toolkit.
2125
*/
2226
class ImageDataLoader {
2327

24-
public static ImageData[] load(InputStream stream) {
25-
return new ImageLoader().load(stream);
28+
public static ImageData load(InputStream stream) {
29+
ImageData[] data = new ImageLoader().load(stream);
30+
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
31+
return data[0];
32+
}
33+
34+
public static ImageData load(String filename) {
35+
ImageData[] data = new ImageLoader().load(filename);
36+
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
37+
return data[0];
38+
}
39+
40+
public static ElementAtZoom<ImageData> load(InputStream stream, int fileZoom, int targetZoom) {
41+
List<ElementAtZoom<ImageData>> data = new ImageLoader().load(stream, fileZoom, targetZoom);
42+
if (data.isEmpty()) SWT.error(SWT.ERROR_INVALID_IMAGE);
43+
return data.get(0);
2644
}
2745

28-
public static ImageData[] load(String filename) {
29-
return new ImageLoader().load(filename);
46+
public static ElementAtZoom<ImageData> load(String filename, int fileZoom, int targetZoom) {
47+
List<ElementAtZoom<ImageData>> data = new ImageLoader().load(filename, fileZoom, targetZoom);
48+
if (data.isEmpty()) SWT.error(SWT.ERROR_INVALID_IMAGE);
49+
return data.get(0);
3050
}
3151

3252
}

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

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import org.eclipse.swt.*;
2222
import org.eclipse.swt.graphics.*;
23-
import org.eclipse.swt.internal.*;
23+
import org.eclipse.swt.internal.DPIUtil.*;
2424

2525
/**
2626
* Abstract factory class for loading/unloading images from files or streams
@@ -57,20 +57,30 @@ public abstract class FileFormat {
5757

5858
public static final int DEFAULT_ZOOM = 100;
5959

60+
private static Optional<FileFormat> determineFileFormat(LEDataInputStream stream) {
61+
return FORMAT_FACTORIES.stream().skip(1).map(Supplier::get).filter(f -> {
62+
try {
63+
return f.isFileFormat(stream);
64+
} catch (IOException e) {
65+
return false;
66+
}
67+
}).findFirst();
68+
}
69+
70+
private static final int MAX_SIGNATURE_BYTES = 18 + 2; // e.g. Win-BMP or OS2-BMP plus a safety-margin
71+
72+
public static boolean isDynamicallySizableFormat(InputStream is) {
73+
Optional<FileFormat> format = determineFileFormat(new LEDataInputStream(is, MAX_SIGNATURE_BYTES));
74+
return format.isPresent() && !(format.get() instanceof StaticImageFileFormat);
75+
}
76+
6077
static abstract class StaticImageFileFormat extends FileFormat {
6178

6279
abstract ImageData[] loadFromByteStream();
6380

6481
@Override
65-
ImageData[] loadFromByteStream(int zoom) {
66-
if (zoom == DEFAULT_ZOOM) {
67-
return loadFromByteStream();
68-
}
69-
return Arrays.stream(loadFromByteStream()).map(data -> {
70-
int width = DPIUtil.scaleUp(data.width, zoom);
71-
int height = DPIUtil.scaleUp(data.height, zoom);
72-
return data.scaledTo(width, height);
73-
}).toArray(ImageData[]::new);
82+
List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
83+
return Arrays.stream(loadFromByteStream()).map(d -> new ElementAtZoom<>(d, fileZoom)).toList();
7484
}
7585
}
7686

@@ -85,16 +95,20 @@ ImageData[] loadFromByteStream(int zoom) {
8595
*/
8696
abstract boolean isFileFormat(LEDataInputStream stream) throws IOException;
8797

88-
abstract ImageData[] loadFromByteStream(int zoom);
98+
/**
99+
* Format that do not implement {@link StaticImageFileFormat} MUST return
100+
* {@link ImageData} with the specified {@code targetZoom}.
101+
*/
102+
abstract List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom);
89103

90104
/**
91105
* Read the specified input stream, and return the
92106
* device independent image array represented by the stream.
93107
*/
94-
public ImageData[] loadFromStream(LEDataInputStream stream, int zoom) {
108+
public List<ElementAtZoom<ImageData>> loadFromStream(LEDataInputStream stream, int fileZoom, int targetZoom) {
95109
try {
96110
inputStream = stream;
97-
return loadFromByteStream(zoom);
111+
return loadFromByteStream(fileZoom, targetZoom);
98112
} catch (Exception e) {
99113
if (e instanceof IOException) {
100114
SWT.error(SWT.ERROR_IO, e);
@@ -109,20 +123,14 @@ public ImageData[] loadFromStream(LEDataInputStream stream, int zoom) {
109123
* Read the specified input stream using the specified loader, and
110124
* return the device independent image array represented by the stream.
111125
*/
112-
public static ImageData[] load(InputStream is, ImageLoader loader, int zoom) {
126+
public static List<ElementAtZoom<ImageData>> load(InputStream is, ImageLoader loader, int fileZoom, int targetZoom) {
113127
LEDataInputStream stream = new LEDataInputStream(is);
114-
FileFormat fileFormat = FORMAT_FACTORIES.stream().skip(1) //
115-
.map(Supplier::get).filter(f -> {
116-
try {
117-
return f.isFileFormat(stream);
118-
} catch (IOException e) {
119-
return false;
120-
}
121-
}) //
122-
.findFirst().orElse(null);
123-
if (fileFormat == null) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
128+
FileFormat fileFormat = determineFileFormat(stream).orElseGet(() -> {
129+
SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
130+
return null;
131+
});
124132
fileFormat.loader = loader;
125-
return fileFormat.loadFromStream(stream, zoom);
133+
return fileFormat.loadFromStream(stream, fileZoom, targetZoom);
126134
}
127135

128136
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ ImageData loadIcon(int[] iconHeader) {
133133
StaticImageFileFormat png = new PNGFileFormat();
134134
if (png.isFileFormat(inputStream)) {
135135
png.loader = this.loader;
136-
return png.loadFromStream(inputStream, DEFAULT_ZOOM)[0];
136+
return png.loadFromStream(inputStream, DEFAULT_ZOOM, DEFAULT_ZOOM).get(0).element();
137137
}
138138
} catch (Exception e) {
139139
}

0 commit comments

Comments
 (0)