From 93c69d6e44ee87e55bcced5400ad9c8a1a48d531 Mon Sep 17 00:00:00 2001 From: arunjose696 Date: Fri, 5 Sep 2025 16:26:07 +0200 Subject: [PATCH 1/3] Version Bump --- .../org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF | 2 +- .../org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF | 2 +- binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF | 2 +- .../org.eclipse.swt.gtk.linux.loongarch64/META-INF/MANIFEST.MF | 2 +- binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF | 2 +- binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF | 2 +- binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF | 2 +- .../org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF | 2 +- .../org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF | 2 +- bundles/org.eclipse.swt/META-INF/MANIFEST.MF | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF index 26c863aecd8..126ecc19ba8 100644 --- a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.cocoa.macosx.aarch64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF index 37c4c27a312..799baef7c67 100644 --- a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.cocoa.macosx.x86_64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF index ab66886914a..e2a8a8d3661 100644 --- a/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.aarch64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.gtk.linux.loongarch64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.loongarch64/META-INF/MANIFEST.MF index 681fcdc9a62..db1efee8c1c 100644 --- a/binaries/org.eclipse.swt.gtk.linux.loongarch64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.loongarch64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.loongarch64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF index 043a0153a2d..f6015be594d 100644 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.ppc64le;singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF index 5c8fa217e53..76d0b4ea316 100644 --- a/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.riscv64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF index 688a94b7de3..5ce90b73997 100644 --- a/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.x86_64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF index 4804132b9d1..3fd6e010835 100644 --- a/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.win32.win32.aarch64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF index 7855c496bee..84f618f2b6a 100644 --- a/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.win32.win32.x86_64; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: fragment Export-Package: diff --git a/bundles/org.eclipse.swt/META-INF/MANIFEST.MF b/bundles/org.eclipse.swt/META-INF/MANIFEST.MF index cb862cd26f4..e00e43a3b4f 100644 --- a/bundles/org.eclipse.swt/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.swt/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt; singleton:=true -Bundle-Version: 3.131.0.qualifier +Bundle-Version: 3.132.0.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: plugin DynamicImport-Package: org.eclipse.swt.accessibility2 From f0bed5a1f6caa641178ea81a9f4401d887b7a814 Mon Sep 17 00:00:00 2001 From: arunjose696 Date: Fri, 5 Sep 2025 13:40:21 +0200 Subject: [PATCH 2/3] Support rendering images at custom sizes This commit introduces support for drawing images at arbitrary sizes. This enables flexible rendering in scenarios where image size is determined by UI requirements rather than screen zoom, such as: - Scalable images in diagrams (e.g., GEF-based tools) - Resizable UI components that embed images - Annotation rulers that scale with editor font size (#3044) --- .../org/eclipse/swt/svg/JSVGRasterizer.java | 46 ++-- .../cocoa/org/eclipse/swt/graphics/Image.java | 16 +- .../swt/internal/NativeImageLoader.java | 4 + .../swt/graphics/ImageDataAtSizeProvider.java | 22 ++ .../eclipse/swt/graphics/ImageDataLoader.java | 28 ++- .../org/eclipse/swt/graphics/ImageLoader.java | 37 ++- .../org/eclipse/swt/internal/DPIUtil.java | 16 +- .../swt/internal/image/FileFormat.java | 31 +++ .../swt/internal/image/SVGFileFormat.java | 20 ++ .../swt/internal/image/SVGRasterizer.java | 2 + .../gtk/org/eclipse/swt/graphics/Image.java | 13 +- .../swt/internal/NativeImageLoader.java | 4 + .../win32/org/eclipse/swt/graphics/GC.java | 55 ++--- .../win32/org/eclipse/swt/graphics/Image.java | 168 ++++++++++++- .../swt/internal/NativeImageLoader.java | 4 + .../resources/Snippet386/collapseall.png | Bin 0 -> 360 bytes .../resources/Snippet386/collapseall.svg | 223 ++++++++++++++++++ .../org/eclipse/swt/snippets/Snippet386.java | 159 +++++++++++++ .../swt/snippets/TextImageDataProvider.java | 109 +++++++++ 19 files changed, 872 insertions(+), 85 deletions(-) create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataAtSizeProvider.java create mode 100644 examples/org.eclipse.swt.snippets/resources/Snippet386/collapseall.png create mode 100644 examples/org.eclipse.swt.snippets/resources/Snippet386/collapseall.svg create mode 100644 examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java create mode 100644 examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java diff --git a/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java b/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java index cee41f23d9a..a2a5da280dc 100644 --- a/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java +++ b/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java @@ -72,34 +72,44 @@ public class JSVGRasterizer implements SVGRasterizer { @Override public ImageData rasterizeSVG(InputStream inputStream, int zoom) throws IOException { - SVGDocument svgDocument = loadSVG(inputStream); - if (svgDocument == null) { - SWT.error(SWT.ERROR_INVALID_IMAGE); - } + SVGDocument svgDocument = loadAndValidateSVG(inputStream); BufferedImage rasterizedImage = renderSVG(svgDocument, zoom); return convertToSWTImageData(rasterizedImage); } - - private SVGDocument loadSVG(InputStream inputStream) { - return SVG_LOADER.load(inputStream, null, LoaderContext.createDefault()); + + @Override + public ImageData rasterizeSVG(InputStream inputStream, int width, int height) throws IOException { + SVGDocument svgDocument = loadAndValidateSVG(inputStream); + BufferedImage rasterizedImage = renderSVG(svgDocument, width, height); + return convertToSWTImageData(rasterizedImage); + } + + private SVGDocument loadAndValidateSVG(InputStream inputStream) throws IOException { + SVGDocument svgDocument = SVG_LOADER.load(inputStream, null, LoaderContext.createDefault()); + if (svgDocument == null) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + return svgDocument; } private BufferedImage renderSVG(SVGDocument svgDocument, int zoom) { + FloatSize sourceImageSize = svgDocument.size(); float scalingFactor = zoom / 100.0f; - BufferedImage image = createImageBase(svgDocument, scalingFactor); - Graphics2D g = configureRenderingOptions(scalingFactor, image); + int targetImageWidth = calculateTargetWidth(scalingFactor, sourceImageSize); + int targetImageHeight = calculateTargetHeight(scalingFactor, sourceImageSize); + return renderSVG(svgDocument, targetImageWidth, targetImageHeight); + } + + private BufferedImage renderSVG(SVGDocument svgDocument, int width, int height) { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + float widthScalingFactor = width / svgDocument.size().width; + float heightScalingFactor = height / svgDocument.size().height; + Graphics2D g = configureRenderingOptions(widthScalingFactor, heightScalingFactor, image); svgDocument.render(null, g); g.dispose(); return image; } - private BufferedImage createImageBase(SVGDocument svgDocument, float scalingFactor) { - FloatSize sourceImageSize = svgDocument.size(); - int targetImageWidth = calculateTargetWidth(scalingFactor, sourceImageSize); - int targetImageHeight = calculateTargetHeight(scalingFactor, sourceImageSize); - return new BufferedImage(targetImageWidth, targetImageHeight, BufferedImage.TYPE_INT_ARGB); - } - private int calculateTargetWidth(float scalingFactor, FloatSize sourceImageSize) { double sourceImageWidth = sourceImageSize.getWidth(); return (int) Math.round(sourceImageWidth * scalingFactor); @@ -110,10 +120,10 @@ private int calculateTargetHeight(float scalingFactor, FloatSize sourceImageSize return (int) Math.round(sourceImageHeight * scalingFactor); } - private Graphics2D configureRenderingOptions(float scalingFactor, BufferedImage image) { + private Graphics2D configureRenderingOptions(float widthScalingFactor, float heightScalingFactor, BufferedImage image) { Graphics2D g = image.createGraphics(); g.setRenderingHints(RENDERING_HINTS); - g.scale(scalingFactor, scalingFactor); + g.scale(widthScalingFactor, heightScalingFactor); return g; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java index 0dc7f5e36d5..f310f316c34 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java @@ -692,7 +692,7 @@ public Image(Device device, InputStream stream) { try { byte[] input = stream.readAllBytes(); initWithSupplier(zoom -> ImageDataLoader.canLoadAtZoom(new ByteArrayInputStream(input), FileFormat.DEFAULT_ZOOM, zoom), - zoom -> ImageDataLoader.load(new ByteArrayInputStream(input), FileFormat.DEFAULT_ZOOM, zoom).element()); + zoom -> ImageDataLoader.loadByZoom(new ByteArrayInputStream(input), FileFormat.DEFAULT_ZOOM, zoom).element()); init(); } catch (IOException e) { SWT.error(SWT.ERROR_INVALID_ARGUMENT, e); @@ -742,7 +742,7 @@ public Image(Device device, String filename) { initNative(filename); if (this.handle == null) { initWithSupplier(zoom -> ImageDataLoader.canLoadAtZoom(filename, FileFormat.DEFAULT_ZOOM, zoom), - zoom -> ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, zoom).element()); + zoom -> ImageDataLoader.loadByZoom(filename, FileFormat.DEFAULT_ZOOM, zoom).element()); } init(); } finally { @@ -789,7 +789,7 @@ public Image(Device device, ImageFileNameProvider imageFileNameProvider) { if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); try { initNative(filename); - if (this.handle == null) init(ImageDataLoader.load(filename, 100, 100).element(), 100); + if (this.handle == null) init(ImageDataLoader.loadByZoom(filename, 100, 100).element(), 100); init(); String filename2x = imageFileNameProvider.getImagePath(200); if (filename2x != null) { @@ -799,7 +799,7 @@ public Image(Device device, ImageFileNameProvider imageFileNameProvider) { handle.addRepresentation(rep); } else if (ImageDataLoader.canLoadAtZoom(filename, 100, 200)) { // Try to natively scale up the image (e.g. possible if it's an SVG) - ImageData imageData2x = ImageDataLoader.load(filename, 100, 200).element(); + ImageData imageData2x = ImageDataLoader.loadByZoom(filename, 100, 200).element(); alphaInfo_200 = new AlphaInfo(); NSBitmapImageRep rep = createRepresentation (imageData2x, alphaInfo_200); handle.addRepresentation(rep); @@ -1820,11 +1820,12 @@ public String toString () { * @param imageData the imageData which is used to draw the scaled Image * @param width the width of the original image * @param height the height of the original image - * @param scaleFactor the factor with which the image is supposed to be scaled + * @param targetWidth the width to which the image is supposed to be scaled + * @param targetHeight the height to which the image is supposed to be scaled * * @noreference This method is not intended to be referenced by clients. */ -public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) { +public static void drawAtTargetSize(GC gc, ImageData imageData, int width, int height, int targetWidth, int targetHeight) { StrictChecks.runWithStrictChecksDisabled(() -> { Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData); 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, * avoiding rounding errors. Nevertheless, we still have some rounding errors * due to the point-based API GC#drawImage(..). */ - 0, 0, Math.round(CocoaDPIUtil.pixelToPoint(width * scaleFactor)), - Math.round(CocoaDPIUtil.pixelToPoint(height * scaleFactor))); + 0, 0, targetWidth, targetHeight); imageToDraw.dispose(); }); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/NativeImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/NativeImageLoader.java index 09b39591368..3d122179db7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/NativeImageLoader.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/NativeImageLoader.java @@ -26,6 +26,10 @@ public static List> load(ElementAtZoom str return FileFormat.load(streamAtZoom, imageLoader, targetZoom); } + public static ImageData load(InputStream streamAtZoom, ImageLoader imageLoader, int targetWidth, int targetHeight) { + return FileFormat.load(streamAtZoom, imageLoader, targetWidth, targetHeight); + } + public static void save(OutputStream stream, int format, ImageLoader imageLoader) { FileFormat.save(stream, format, imageLoader); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataAtSizeProvider.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataAtSizeProvider.java new file mode 100644 index 00000000000..80f6ed948bd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataAtSizeProvider.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2025 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Bangas (Vector Informatik GmbH) - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + +/** + * @since 3.132 + */ +public interface ImageDataAtSizeProvider extends ImageDataProvider { + + ImageData getImageData(int targetWidth, int targetHeight); + +} \ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java index 29f517b45c0..8b2d1dc36a7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java @@ -41,20 +41,32 @@ public static boolean canLoadAtZoom(InputStream stream, int fileZoom, int target return ImageLoader.canLoadAtZoom(stream, fileZoom, targetZoom); } - public static ElementAtZoom load(InputStream stream, int fileZoom, int targetZoom) { - List> data = new ImageLoader().load(stream, fileZoom, targetZoom); - if (data.isEmpty()) SWT.error(SWT.ERROR_INVALID_IMAGE); - return data.get(0); - } - public static boolean canLoadAtZoom(String filename, int fileZoom, int targetZoom) { return ImageLoader.canLoadAtZoom(filename, fileZoom, targetZoom); } - public static ElementAtZoom load(String filename, int fileZoom, int targetZoom) { - List> data = new ImageLoader().load(filename, fileZoom, targetZoom); + public static ElementAtZoom loadByZoom(InputStream stream, int fileZoom, int targetZoom) { + List> data = new ImageLoader().loadByZoom(stream, fileZoom, targetZoom); + if (data.isEmpty()) SWT.error(SWT.ERROR_INVALID_IMAGE); + return data.get(0); + } + + public static ElementAtZoom loadByZoom(String filename, int fileZoom, int targetZoom) { + List> data = new ImageLoader().loadByZoom(filename, fileZoom, targetZoom); if (data.isEmpty()) SWT.error(SWT.ERROR_INVALID_IMAGE); return data.get(0); } + public static ImageData loadByTargetSize(InputStream stream, int targetWidth, int targetHeight) { + ImageData data = new ImageLoader().loadByTargetSize(stream, targetWidth, targetHeight); + if (data == null) SWT.error(SWT.ERROR_INVALID_IMAGE); + return data; + } + + public static ImageData loadByTargetSize(String filename, int targetWidth, int targetHeight) { + ImageData data = new ImageLoader().loadByTargetSize(filename, targetWidth, targetHeight); + if (data == null) SWT.error(SWT.ERROR_INVALID_IMAGE); + return data; + } + } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java index 9973b8f06e3..dc7917dc349 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java @@ -151,11 +151,11 @@ void reset() { * */ public ImageData[] load(InputStream stream) { - load(stream, FileFormat.DEFAULT_ZOOM, FileFormat.DEFAULT_ZOOM); + loadByZoom(stream, FileFormat.DEFAULT_ZOOM, FileFormat.DEFAULT_ZOOM); return data; } -List> load(InputStream stream, int fileZoom, int targetZoom) { +List> loadByZoom(InputStream stream, int fileZoom, int targetZoom) { if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); reset(); List> images = NativeImageLoader.load(new ElementAtZoom<>(stream, fileZoom), this, targetZoom); @@ -163,6 +163,14 @@ List> load(InputStream stream, int fileZoom, int target return images; } +ImageData loadByTargetSize(InputStream stream, int targetWidth, int targetHeight) { + if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + reset(); + ImageData image = NativeImageLoader.load(stream, this, targetWidth, targetHeight); + data = new ImageData[] {image}; + return image; +} + static boolean canLoadAtZoom(InputStream stream, int fileZoom, int targetZoom) { if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); return FileFormat.canLoadAtZoom(new ElementAtZoom<>(stream, fileZoom), targetZoom); @@ -187,14 +195,24 @@ static boolean canLoadAtZoom(InputStream stream, int fileZoom, int targetZoom) { * */ public ImageData[] load(String filename) { - load(filename, FileFormat.DEFAULT_ZOOM, FileFormat.DEFAULT_ZOOM); + loadByZoom(filename, FileFormat.DEFAULT_ZOOM, FileFormat.DEFAULT_ZOOM); return data; } -List> load(String filename, int fileZoom, int targetZoom) { +List> loadByZoom(String filename, int fileZoom, int targetZoom) { if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); try (InputStream stream = new FileInputStream(filename)) { - return load(stream, fileZoom, targetZoom); + return loadByZoom(stream, fileZoom, targetZoom); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + return null; +} + +ImageData loadByTargetSize(String filename, int targetWidth, int targetHeight) { + if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + try (InputStream stream = new FileInputStream(filename)) { + return loadByTargetSize(stream, targetWidth, targetHeight); } catch (IOException e) { SWT.error(SWT.ERROR_IO, e); } @@ -211,6 +229,15 @@ static boolean canLoadAtZoom(String filename, int fileZoom, int targetZoom) { return false; } +static boolean isDynamicallySizable(String filename) { + try (InputStream stream = new FileInputStream(filename)) { + return FileFormat.isDynamicallySizableFormat(stream); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + return false; +} + /** * Saves the image data in this ImageLoader to the specified stream. * The format parameter can have one of the following values: diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java index 44d67e71408..bf4b04ca554 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java @@ -146,6 +146,16 @@ public static ImageData autoScaleImageData (Device device, final ImageData image int height = imageData.height; int scaledWidth = Math.round (width * scaleFactor); int scaledHeight = Math.round (height * scaleFactor); + return scaleImage(device, imageData, Image::drawAtTargetSize, width, height, scaledWidth, scaledHeight); +} + +@FunctionalInterface +private interface ImageDrawFunction { + void draw(GC gc, ImageData imageData, int originalWidth, int originalHeight, int targetWidth, int targetHeight); +} + +private static ImageData scaleImage(Device device, final ImageData imageData, ImageDrawFunction drawFunction, int width, int height, + int scaledWidth, int scaledHeight) { int defaultZoomLevel = 100; boolean useSmoothScaling = isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK; if (useSmoothScaling) { @@ -153,7 +163,7 @@ public static ImageData autoScaleImageData (Device device, final ImageData image @Override public void drawOn(GC gc, int imageWidth, int imageHeight) { gc.setAntialias (SWT.ON); - Image.drawScaled(gc, imageData, width, height, scaleFactor); + drawFunction.draw(gc, imageData, width, height, imageWidth, imageHeight); }; @Override @@ -170,6 +180,10 @@ public int getGcStyle() { } } +public static ImageData autoScaleImageData(Device device, final ImageData imageData, int targetWidth, int targetHeight) { + return scaleImage(device, imageData, Image::drawAtTargetSize, imageData.width, imageData.height, targetWidth, targetHeight); +} + public static boolean isSmoothScalingEnabled() { return autoScaleMethod == AutoScaleMethod.SMOOTH; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java index 8232d71023d..6e5da08b774 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java @@ -85,6 +85,11 @@ static abstract class StaticImageFileFormat extends FileFormat { List> loadFromByteStream(int fileZoom, int targetZoom) { return Arrays.stream(loadFromByteStream()).map(d -> new ElementAtZoom<>(d, fileZoom)).toList(); } + + @Override + ImageData loadFromByteStreamByTargetSize(int targetWidth, int targetHeight) { + return loadFromByteStream()[0]; + } } LEDataInputStream inputStream; @@ -104,6 +109,8 @@ List> loadFromByteStream(int fileZoom, int targetZoom) */ abstract List> loadFromByteStream(int fileZoom, int targetZoom); + abstract ImageData loadFromByteStreamByTargetSize(int targetWidth, int targetHeight); + /** * Read the specified input stream, and return the * device independent image array represented by the stream. @@ -122,6 +129,20 @@ public List> loadFromStream(LEDataInputStream stream, i } } +public ImageData loadFromStreamByTargetSize(LEDataInputStream stream, int targetWidth, int targetHeight) { + try { + inputStream = stream; + return loadFromByteStreamByTargetSize(targetWidth, targetHeight); + } catch (Exception e) { + if (e instanceof IOException) { + SWT.error(SWT.ERROR_IO, e); + } else { + SWT.error(SWT.ERROR_INVALID_IMAGE, e); + } + return null; + } +} + /** * Read the specified input stream using the specified loader, and * return the device independent image array represented by the stream. @@ -136,6 +157,16 @@ public static List> load(ElementAtZoom is, return fileFormat.loadFromStream(stream, is.zoom(), targetZoom); } +public static ImageData load(InputStream is, ImageLoader loader, int targetWidth, int targetHeight) { + LEDataInputStream stream = new LEDataInputStream(is); + FileFormat fileFormat = determineFileFormat(stream).orElseGet(() -> { + SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); + return null; + }); + fileFormat.loader = loader; + return fileFormat.loadFromStreamByTargetSize(stream, targetWidth, targetHeight); +} + public static boolean canLoadAtZoom(ElementAtZoom is, int targetZoom) { return is.zoom() == targetZoom || isDynamicallySizableFormat(is.element()); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java index 78d8abebcb6..777e7373ae4 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java @@ -60,6 +60,26 @@ List> loadFromByteStream(int fileZoom, int targetZoom) } } + @Override + ImageData loadFromByteStreamByTargetSize(int targetWidth, int targetHeight) { + if (RASTERIZER == null) { + SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT, null, " [No SVG rasterizer found]"); + } + if (targetWidth <= 0 || targetHeight <= 0) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, " [Cannot rasterize SVG for targetWidth or targetHeight <= 0]"); + } + if (targetHeight <= 0 || targetHeight <= 0) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, " [Cannot rasterize SVG for targetHeight or targetHeight <= 0]"); + } + try { + ImageData rasterizedImageData = RASTERIZER.rasterizeSVG(inputStream, targetWidth, targetHeight); + return rasterizedImageData; + } catch (IOException e) { + SWT.error(SWT.ERROR_INVALID_IMAGE, e); + return null; + } + } + @Override void unloadIntoByteStream(ImageLoader loader) { throw new UnsupportedOperationException(); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java index 9586abfb5c6..79eae39be1c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java @@ -32,4 +32,6 @@ public interface SVGRasterizer { * the input is not a valid SVG file or cannot be processed. */ public ImageData rasterizeSVG(InputStream stream, int zoom) throws IOException; + + public ImageData rasterizeSVG(InputStream stream, int width, int height) throws IOException; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java index f77c83f79e9..cdc6d5c38d9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java @@ -552,7 +552,7 @@ public Image(Device device, ImageData source, ImageData mask) { public Image(Device device, InputStream stream) { super(device); currentDeviceZoom = DPIUtil.getDeviceZoom(); - ElementAtZoom image = ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, currentDeviceZoom); + ElementAtZoom image = ImageDataLoader.loadByZoom(stream, FileFormat.DEFAULT_ZOOM, currentDeviceZoom); ImageData data = DPIUtil.scaleImageData(device, image, currentDeviceZoom); init(data); init(); @@ -594,7 +594,7 @@ public Image(Device device, String filename) { super(device); if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); currentDeviceZoom = DPIUtil.getDeviceZoom(); - ElementAtZoom image = ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, currentDeviceZoom); + ElementAtZoom image = ImageDataLoader.loadByZoom(filename, FileFormat.DEFAULT_ZOOM, currentDeviceZoom); ImageData data = DPIUtil.scaleImageData(device, image, currentDeviceZoom); init(data); init(); @@ -785,7 +785,7 @@ private void initFromFileNameProvider(int zoom) { initNative(fileForZoom.element()); } if (this.surface == 0) { - ElementAtZoom imageDataAtZoom = ImageDataLoader.load(fileForZoom.element(), fileForZoom.zoom(), zoom); + ElementAtZoom imageDataAtZoom = ImageDataLoader.loadByZoom(fileForZoom.element(), fileForZoom.zoom(), zoom); ImageData imageData = imageDataAtZoom.element(); if (imageDataAtZoom.zoom() != zoom) { imageData = DPIUtil.scaleImageData(device, imageDataAtZoom, zoom); @@ -1581,11 +1581,12 @@ public String toString () { * @param imageData the imageData which is used to draw the scaled Image * @param width the width of the original image * @param height the height of the original image - * @param scaleFactor the factor with which the image is supposed to be scaled + * @param targetWidth the width to which the image is supposed to be scaled + * @param targetHeight the height to which the image is supposed to be scaled * * @noreference This method is not intended to be referenced by clients. */ -public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) { +public static void drawAtTargetSize(GC gc, ImageData imageData, int width, int height, int targetWidth, int targetHeight) { StrictChecks.runWithStrictChecksDisabled(() -> { Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData); gc.drawImage(imageToDraw, 0, 0, width, height, @@ -1594,7 +1595,7 @@ public static void drawScaled(GC gc, ImageData imageData, int width, int height, * avoiding rounding errors. Nevertheless, we still have some rounding errors * due to the point-based API GC#drawImage(..). */ - 0, 0, Math.round(width * scaleFactor), Math.round(height * scaleFactor)); + 0, 0, targetWidth, targetHeight); imageToDraw.dispose(); }); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/NativeImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/NativeImageLoader.java index 838fd72cd36..91321b5244d 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/NativeImageLoader.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/NativeImageLoader.java @@ -142,6 +142,10 @@ public static List> load(ElementAtZoom str return Arrays.stream(imgDataArray).map(data -> new ElementAtZoom<>(data, streamAtZoom.zoom())).toList(); } + public static ImageData load(InputStream streamAtZoom, ImageLoader imageLoader, int targetWidth, int targetHeight) { + return FileFormat.load(streamAtZoom, imageLoader, targetWidth, targetHeight); + } + /** * Return true if the image is an interlaced PNG file. This is used to check * whether ImageLoaderEvent should be fired when loading images. diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java index e2d8c831cc1..b3dd16e0da1 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java @@ -1053,7 +1053,8 @@ void apply() { private void drawImageInPixels(Image image, Point location) { if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); - drawImage(image, 0, 0, -1, -1, location.x, location.y, -1, -1, true, getZoom()); + long handle = Image.win32_getHandle(image, getZoom()); + drawImage(image, 0, 0, -1, -1, location.x, location.y, -1, -1, true, handle); } } @@ -1155,26 +1156,25 @@ private int calculateZoomForImage(int gcZoom, int srcWidth, int srcHeight, int d private void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, int imageZoom, int scaledImageZoom) { - Rectangle src = Win32DPIUtils.pointToPixel(drawable, new Rectangle(srcX, srcY, srcWidth, srcHeight), scaledImageZoom); - Rectangle dest = Win32DPIUtils.pointToPixel(drawable, new Rectangle(destX, destY, destWidth, destHeight), imageZoom); - if (scaledImageZoom != 100) { - /* - * This is a HACK! Due to rounding errors at fractional scale factors, - * the coordinates may be slightly off. The workaround is to restrict - * coordinates to the allowed bounds. - */ - Rectangle b = image.getBounds(scaledImageZoom); - int errX = src.x + src.width - b.width; - int errY = src.y + src.height - b.height; - if (errX != 0 || errY != 0) { - if (errX <= scaledImageZoom / 100 && errY <= scaledImageZoom / 100) { - src.intersect(b); - } else { - SWT.error (SWT.ERROR_INVALID_ARGUMENT); - } - } - } - drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height, false, scaledImageZoom); + float widthScalingFactor = (float) destWidth / srcWidth; + float heightScalingFactor = (float) destHeight / srcHeight; + + Rectangle fullImageBounds = image.getBounds(); + int fullImageWidth = Math.round(fullImageBounds.width * widthScalingFactor); + int fullImageHeight = Math.round(fullImageBounds.height * heightScalingFactor); + int scaledImageWidthInPixels = Win32DPIUtils.pointToPixel(fullImageWidth, scaledImageZoom); + int scaledImageHeightInPixels = Win32DPIUtils.pointToPixel(fullImageHeight, scaledImageZoom); + + Rectangle scaledSrc = new Rectangle(Math.round(srcX * widthScalingFactor), Math.round(srcY * heightScalingFactor), + Math.round(srcWidth * widthScalingFactor), Math.round(srcHeight * heightScalingFactor)); + Rectangle srcPixels = Win32DPIUtils.pointToPixel(drawable, scaledSrc, scaledImageZoom); + Rectangle destPixels = Win32DPIUtils.pointToPixel(drawable, new Rectangle(destX, destY, destWidth, destHeight), + imageZoom); + + image.executeOnImageHandleAtSize( + tempHandle -> drawImage(image, srcPixels.x, srcPixels.y, srcPixels.width, srcPixels.height, destPixels.x, + destPixels.y, destPixels.width, destPixels.height, false, tempHandle), + scaledImageWidthInPixels, scaledImageHeightInPixels); } private class DrawImageToImageOperation extends ImageOperation { @@ -1191,14 +1191,15 @@ private class DrawImageToImageOperation extends ImageOperation { @Override void apply() { - drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, getZoom()); + long handle = Image.win32_getHandle(getImage(), getZoom()); + drawImage(getImage(), source.x, source.y, source.width, source.height, destination.x, destination.y, destination.width, destination.height, simple, handle); } } -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) { +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) { if (data.gdipGraphics != 0) { //TODO - cache bitmap - long [] gdipImage = srcImage.createGdipImage(imageZoom); + long [] gdipImage = srcImage.createGdipImageFromHandle(tempImageHandle); long img = gdipImage[0]; int imgWidth = Gdip.Image_GetWidth(img); int imgHeight = Gdip.Image_GetHeight(img); @@ -1253,13 +1254,13 @@ private void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int src } return; } - long imageHandle = srcImage.getHandle(imageZoom, data.nativeZoom); switch (srcImage.type) { case SWT.BITMAP: - drawBitmap(srcImage, imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); + drawBitmap(srcImage, tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, + simple); break; case SWT.ICON: - drawIcon(imageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); + drawIcon(tempImageHandle, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); break; } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java index 84147c25e0c..13545803ddc 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -18,8 +18,10 @@ import java.io.*; import java.util.*; +import java.util.List; import java.util.Map.*; import java.util.function.*; +import java.util.stream.*; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; @@ -27,6 +29,7 @@ import org.eclipse.swt.internal.gdip.*; import org.eclipse.swt.internal.image.*; import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; /** * Instances of this class are graphics which have been prepared @@ -526,7 +529,7 @@ public Image (Device device, InputStream stream) { public Image (Device device, String filename) { super(device); if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); - this.imageProvider = new ImageFileNameProviderWrapper(zoom -> { + this.imageProvider = createImageFileNameProviderWrapper(zoom -> { if (zoom == 100) { return filename; } @@ -567,7 +570,7 @@ public Image (Device device, String filename) { */ public Image(Device device, ImageFileNameProvider imageFileNameProvider) { super(device); - this.imageProvider = new ImageFileNameProviderWrapper(imageFileNameProvider); + this.imageProvider = createImageFileNameProviderWrapper(imageFileNameProvider); if (imageFileNameProvider.getImagePath(100) == null) { SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, ": ImageFileNameProvider [" + imageFileNameProvider + "] returns null fileName at 100% zoom."); @@ -607,7 +610,11 @@ public Image(Device device, ImageFileNameProvider imageFileNameProvider) { */ public Image(Device device, ImageDataProvider imageDataProvider) { super(device); - this.imageProvider = new ImageDataProviderWrapper(imageDataProvider); + if (imageDataProvider instanceof ImageDataAtSizeProvider imageDataAtSizeProvider) { + this.imageProvider = new ImageDataAtSizeProviderWrapper(imageDataAtSizeProvider); + } else { + this.imageProvider = new ImageDataProviderWrapper(imageDataProvider); + } if (imageDataProvider.getImageData(100) == null) { SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, ": ImageDataProvider [" + imageDataProvider + "] returns null ImageData at 100% zoom."); @@ -643,6 +650,18 @@ public Image(Device device, ImageGcDrawer imageGcDrawer, int width, int height) init(); } +private ImageFileNameProviderWrapper createImageFileNameProviderWrapper(ImageFileNameProvider imageFileNameProvider) { + String fileName = DPIUtil.validateAndGetImagePathAtZoom(imageFileNameProvider, 100).element(); + if(ImageLoader.isDynamicallySizable(fileName)) + { + return new dynamicallySizableImageFileNameProviderWrapper(imageFileNameProvider); + } + else { + return new ImageFileNameProviderWrapper(imageFileNameProvider); + } + +} + private ImageData adaptImageDataIfDisabledOrGray(ImageData data) { ImageData returnImageData = null; switch (this.styleFlag) { @@ -820,6 +839,19 @@ long getHandle (int targetZoom, int nativeZoom) { return getImageMetadata(zoomContext).handle; } +void executeOnImageHandleAtSize(Consumer drawFunction, int targetWidth, int targetHeight) { + ImageData imageData = this.imageProvider.newImageData(targetWidth, targetHeight); + HandleForImageDataContainer handleContainer = init(device, imageData); + long tempHandle = handleContainer.handles()[0]; + drawFunction.accept(tempHandle); + if (handleContainer.type == SWT.ICON) { + OS.DestroyIcon (tempHandle); + } else { + OS.DeleteObject (tempHandle); + } +} + + /** * IMPORTANT: This method is not part of the public * API for Image. It is marked public only so that it @@ -831,22 +863,29 @@ long getHandle (int targetZoom, int nativeZoom) { * @param imageData the imageData which is used to draw the scaled Image * @param width the width of the original image * @param height the height of the original image - * @param scaleFactor the factor with which the image is supposed to be scaled + * @param targetWidth the width to which the image is supposed to be scaled + * @param targetHeight the height to which the image is supposed to be scaled * * @noreference This method is not intended to be referenced by clients. */ -public static void drawScaled(GC gc, ImageData imageData, int width, int height, float scaleFactor) { +public static void drawAtTargetSize(GC gc, ImageData imageData, int width, int height, int targetWidth, int targetHeight) { StrictChecks.runWithStrictChecksDisabled(() -> { Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData); - gc.drawImage(imageToDraw, 0, 0, width, height, 0, 0, Math.round(width * scaleFactor), - Math.round(height * scaleFactor), false); + gc.drawImage(imageToDraw, 0, 0, width, height, 0, 0, targetHeight, + targetHeight, false); imageToDraw.dispose(); }); } + + long [] createGdipImage(Integer zoom) { long handle = Image.win32_getHandle(this, zoom); + return createGdipImageFromHandle(handle); +} + +long[] createGdipImageFromHandle(long handle) { switch (type) { case SWT.BITMAP: { BITMAP bm = new BITMAP(); @@ -1900,6 +1939,20 @@ public static Image win32_new(Device device, int type, long handle, int nativeZo return new Image(device, type, handle, nativeZoom); } +@FunctionalInterface +interface SizeProvider { + ImageData getForSize(int width, int height); +} + +private ImageData getElementAtTargetSize(SizeProvider elementForSizeProvider, int targetWidth, + int targetHeight) { + ImageData dataAtOriginalSize = elementForSizeProvider.getForSize(targetWidth, targetHeight); + if (dataAtOriginalSize!=null) { + return dataAtOriginalSize; + } + return null; +} + /** * ZoomContext holds information about zoom details used to create and cache the image * @@ -1933,6 +1986,40 @@ public Collection getPreservedZoomLevels() { abstract ImageData newImageData(ZoomContext zoomContext); + final ImageData newImageData(int targetWidth, int targetHeight) { + ImageData imageData = loadImageData(targetWidth, targetHeight); + imageData = adaptImageDataIfDisabledOrGray(imageData); + + return DPIUtil.autoScaleImageData(device, imageData, targetWidth, targetHeight); + }; + + protected ImageData loadImageData(int targetWidth, int targetHeight) { + ImageData imageData; + Rectangle bounds = getBounds(100); + int imageZoomForWidth = Math.round(100 * targetWidth / bounds.width); + int imageZoomForHeight = Math.round(100 * targetHeight / bounds.height); + int imageZoom = Math.max(imageZoomForWidth, imageZoomForHeight); + System.out.println(imageZoom); + if (getAllCurrentMonitorZooms().contains(imageZoom)) { + return getImageData(imageZoom); + } + if (imageZoom > 150) { + imageData = getImageData(200); + } else { + imageData = getImageData(100); + } + return imageData; + }; + + private Collection getAllCurrentMonitorZooms() { + if (device instanceof Display display) { + return Arrays.stream(display.getMonitors()) + .map(Monitor::getZoom) + .collect(Collectors.toSet()); + } + return Collections.emptySet(); + } + abstract AbstractImageProviderWrapper createCopy(Image image); ImageData getScaledImageData (int zoom) { @@ -2035,7 +2122,6 @@ private ImageHandle initializeHandleFromSource(ZoomContext zoomContext) { imageData = adaptImageDataIfDisabledOrGray(imageData); return newImageHandle(imageData, zoomContext); } - } private class PlainImageDataProviderWrapper extends ImageFromImageDataProviderWrapper { @@ -2119,7 +2205,7 @@ private ImageDataLoaderStreamProviderWrapper(byte[] inputStreamData) { @Override protected ElementAtZoom loadImageData(int zoom) { - return ImageDataLoader.load(new ByteArrayInputStream(inputStreamData), FileFormat.DEFAULT_ZOOM, zoom); + return ImageDataLoader.loadByZoom(new ByteArrayInputStream(inputStreamData), FileFormat.DEFAULT_ZOOM, zoom); } @Override @@ -2300,7 +2386,6 @@ ImageData newImageData(ZoomContext zoomContext) { return (ImageData) cachedImageData.computeIfAbsent(zoomContext.targetZoom(), imageDataRetrival).clone(); } - @Override protected ImageHandle newImageHandle(ZoomContext zoomContext) { int targetZoom = zoomContext.targetZoom(); @@ -2341,7 +2426,7 @@ protected ElementAtZoom loadImageData(int zoom) { // Load at appropriate zoom via loader if (fileForZoom.zoom() != zoom && ImageDataLoader.canLoadAtZoom(fileForZoom.element(), fileForZoom.zoom(), zoom)) { - ElementAtZoom imageDataAtZoom = ImageDataLoader.load(fileForZoom.element(), fileForZoom.zoom(), zoom); + ElementAtZoom imageDataAtZoom = ImageDataLoader.loadByZoom(fileForZoom.element(), fileForZoom.zoom(), zoom); return new ElementAtZoom<>(imageDataAtZoom.element(), zoom); } @@ -2354,7 +2439,7 @@ protected ElementAtZoom loadImageData(int zoom) { } ElementAtZoom imageDataAtZoom; if (nativeInitializedImage == null) { - imageDataAtZoom = ImageDataLoader.load(fileForZoom.element(), fileForZoom.zoom(), zoom); + imageDataAtZoom = ImageDataLoader.loadByZoom(fileForZoom.element(), fileForZoom.zoom(), zoom); } else { imageDataAtZoom = new ElementAtZoom<>(nativeInitializedImage.getImageData(), fileForZoom.zoom()); nativeInitializedImage.destroy(); @@ -2580,6 +2665,65 @@ protected ElementAtZoom loadImageData(int zoom) { ImageDataProviderWrapper createCopy(Image image) { return image.new ImageDataProviderWrapper(provider); } + +} + +private class dynamicallySizableImageFileNameProviderWrapper extends ImageFileNameProviderWrapper { + dynamicallySizableImageFileNameProviderWrapper(ImageFileNameProvider provider) { + super(provider); + } + + @Override + protected ImageData loadImageData(int targetWidth, int targetHeight) { + Optional fileForTargetSize = validateAndGetImagePathAtTargetSize(provider, targetWidth, targetHeight); + if (fileForTargetSize.isPresent()) { + ImageData imageDataAtZoom = ImageDataLoader.loadByTargetSize(fileForTargetSize.get(), targetWidth, + targetWidth); + return imageDataAtZoom; + } + return null; + } + + private Optional validateAndGetImagePathAtTargetSize(ImageFileNameProvider provider, int targetWidth, + int targetHeight) { + if (provider == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + String imagePath = DPIUtil.validateAndGetImagePathAtZoom(provider, 100).element(); + return Optional.of(imagePath); + } + +} + +private class ImageDataAtSizeProviderWrapper extends ImageDataProviderWrapper { + ImageDataAtSizeProviderWrapper(ImageDataAtSizeProvider provider) { + super(provider); + } + + @Override + protected ImageData loadImageData(int targetWidth, int targetHeight) { + ImageData imageData = validateAndGetImageDataAtTargetSize(targetWidth, targetHeight); + if (imageData == null) { + imageData = provider.getImageData(100); + } + return imageData; + } + + private ImageData validateAndGetImageDataAtTargetSize(int targetWidth, int targetHeight) { + if (provider == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + + return getElementAtTargetSize((x, z) -> ((ImageDataAtSizeProvider) provider).getImageData(x, z), targetWidth, + targetHeight); + + } + + @Override + ImageDataProviderWrapper createCopy(Image image) { + return image.new ImageDataProviderWrapper(provider); + } + } private class ImageGcDrawerWrapper extends DynamicImageProviderWrapper { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/NativeImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/NativeImageLoader.java index 09b39591368..3d122179db7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/NativeImageLoader.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/NativeImageLoader.java @@ -26,6 +26,10 @@ public static List> load(ElementAtZoom str return FileFormat.load(streamAtZoom, imageLoader, targetZoom); } + public static ImageData load(InputStream streamAtZoom, ImageLoader imageLoader, int targetWidth, int targetHeight) { + return FileFormat.load(streamAtZoom, imageLoader, targetWidth, targetHeight); + } + public static void save(OutputStream stream, int format, ImageLoader imageLoader) { FileFormat.save(stream, format, imageLoader); } diff --git a/examples/org.eclipse.swt.snippets/resources/Snippet386/collapseall.png b/examples/org.eclipse.swt.snippets/resources/Snippet386/collapseall.png new file mode 100644 index 0000000000000000000000000000000000000000..0ac25a9247ba9cf9b9a9916cbbcf782c21daaf26 GIT binary patch literal 360 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR44*VqH_sb~aQnMsrKE9!xl< zUGmWIkMvPf-NG~H>xr-yeP>%bYjNpozZ{%PH;+Lt1ta_bOBl$b=95aU-K1>T?or+TFrlrofD z48B?H=g!RjuHJg{`qwKw0*#-uPW|Ov9&J`vIVC{B;d0mAl=z0j?`&11RI zR9Yk(ZKtfYLV%g;&YzaEhxGUT4)?nH{twgoN{iFiID~D19%k@#^>bP0l+XkK#)yWF literal 0 HcmV?d00001 diff --git a/examples/org.eclipse.swt.snippets/resources/Snippet386/collapseall.svg b/examples/org.eclipse.swt.snippets/resources/Snippet386/collapseall.svg new file mode 100644 index 00000000000..587c3c3497e --- /dev/null +++ b/examples/org.eclipse.swt.snippets/resources/Snippet386/collapseall.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java new file mode 100644 index 00000000000..10d698fd19d --- /dev/null +++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java @@ -0,0 +1,159 @@ +package org.eclipse.swt.snippets; + +import java.io.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.custom.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +public class Snippet386 { + + public static void main(String[] args) { + Display display = new Display(); + + // Load all images + Image[] images = new Image[] { + new Image(display, createImageFileNameProvider("resources/Snippet386/collapseall.png")), + new Image(display, createImageDataProvider("resources/Snippet386/collapseall.svg")), + new Image(display, createImageFileNameProvider("resources/Snippet386/collapseall.svg")), + new Image(display, createImageDataProvider("resources/Snippet386/collapseall.png")) + }; + + // Descriptions for each image (rows) + String[] descriptions = new String[] { + "ImageFileNameProvider with SVGs scaled by SVG rasterization", + "ImageDataProvider with SVGs scaled by SVG rasterization", + "ImageFileNameProvider with PNGs scaled destructively", + "ImageDataProvider with PNGs scaled destructively" + }; + + // Slice names (columns) + Slice[] slices = { + new Slice("Full", 0.0, 0.0, 1.0, 1.0), + new Slice("Top Half", 0.0, 0.0, 1.0, 0.5), + new Slice("Bottom Half", 0.0, 0.5, 1.0, 0.5), + new Slice("Left Half", 0.0, 0.0, 0.5, 1.0), + new Slice("Right Half", 0.5, 0.0, 0.5, 1.0), + new Slice("Top-Left Quarter", 0.0, 0.0, 0.5, 0.5) + }; + + createShellWithImages(display, images, descriptions, slices, "Snippet 386 - Flipped Layout"); + } + + + + private static ImageFileNameProvider createImageFileNameProvider(String fileName) { + return new ImageFileNameProvider() { + + @Override + public String getImagePath(int zoom) { + return fileName; + } + }; + } + + private static ImageDataProvider createImageDataProvider(String fileName) { + return new ImageDataProvider() { + @Override + public ImageData getImageData(int zoom) { + try (InputStream stream = new FileInputStream(fileName)) { + if(zoom==100) + return new ImageData (fileName); + else + return new ImageData ("resources/Snippet384/images/civil-icon.png"); + //return NativeImageLoader.load(new ElementAtZoom<>(stream, 100), new ImageLoader(), 100).get(0).element(); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + return null; + } + }; + } + + static class Slice { + String name; + double xFrac, yFrac, wFrac, hFrac; + Slice(String name, double x, double y, double w, double h) { + this.name = name; + this.xFrac = x; this.yFrac = y; + this.wFrac = w; this.hFrac = h; + } + } + + private static void createShellWithImages(Display display, Image[] images, String[] descriptions, Slice[] slices, String title) { + Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.MAX | SWT.RESIZE); + shell.setText(title); + shell.setLayout(new FillLayout()); + + ScrolledComposite scrolledComposite = new ScrolledComposite(shell, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER); + Canvas canvas = new Canvas(scrolledComposite, SWT.DOUBLE_BUFFERED); + scrolledComposite.setContent(canvas); + scrolledComposite.setExpandHorizontal(true); + scrolledComposite.setExpandVertical(true); + + int boxW = 400; + int boxH = 500; + int gap = 20; + int titleHeight = 20; + int descHeight = 40; // description row height + int sliceHeaderHeight = 30; // column header height + + int rows = images.length; // one row per image + int cols = slices.length; // one column per slice + + int canvasWidth = (boxW + gap) * cols + gap; // extra gap for row headers + int canvasHeight = (boxH + titleHeight + gap) * rows + descHeight + sliceHeaderHeight; + canvas.setSize(canvasWidth, canvasHeight); + scrolledComposite.setMinSize(canvasWidth, canvasHeight); + + canvas.addListener(SWT.Paint, e -> { + + // Draw column headers (slice names) + for (int col = 0; col < cols; col++) { + int x = col * (boxW + gap) + gap; // leave gap for row header + e.gc.drawText(slices[col].name, x, 0, true); + } + + // Draw each row (image type) + for (int row = 0; row < rows; row++) { + Image image = images[row]; + Rectangle rect = image.getBounds(); + + int y = row * (boxH + titleHeight + gap) + descHeight + sliceHeaderHeight; + + // Draw row description + e.gc.drawText(descriptions[row], 0, y, true); + + for (int col = 0; col < cols; col++) { + Slice s = slices[col]; + + int x = col * (boxW + gap) + gap; + + int srcX = (int) (rect.width * s.xFrac); + int srcY = (int) (rect.height * s.yFrac); + int srcW = (int) (rect.width * s.wFrac); + int srcH = (int) (rect.height * s.hFrac); + + // Draw box + int boxTop = y + titleHeight; + e.gc.drawRectangle(x, boxTop, boxW, boxH); + + // Draw image slice + e.gc.drawImage(image, srcX, srcY, srcW, srcH, x, boxTop, boxW, boxH); + } + } + }); + + shell.setMaximized(true); + shell.open(); + + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) display.sleep(); + } + + for (Image img : images) img.dispose(); + display.dispose(); + } +} diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java new file mode 100644 index 00000000000..7c9b32681dc --- /dev/null +++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java @@ -0,0 +1,109 @@ +package org.eclipse.swt.snippets; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; + +public class TextImageDataProvider implements ImageDataAtSizeProvider { + private final String text; + + + + public TextImageDataProvider(String text, int baseFontSize, double zoom) { + this.text = text; + } + + @Override + public ImageData getImageData(int zoomLevel) { + int scaleFactor=zoomLevel/100; + int baseWidth =100; + int baseHeight=100; + return getImageData(baseWidth*scaleFactor,baseHeight*scaleFactor); + } + + @Override +public ImageData getImageData(int width, int height) { + Display display = Display.getDefault(); + + // Step 1: Determine font size based on height and zoom + int fontSize = (int) Math.round( height / 100.0); + if (fontSize <= 0) fontSize = 1; + Font font = new Font(display, "Arial", fontSize, SWT.NORMAL); + + // Step 2: Measure text size + Image tmp = new Image(display, 1, 1); + GC measureGC = new GC(tmp); + measureGC.setFont(font); + String text="abcd"; + Point textExtent = measureGC.textExtent(text); + measureGC.dispose(); + tmp.dispose(); + + // Step 3: Scale font to fit requested width/height + double scaleX = (double) width / textExtent.x; + double scaleY = (double) height / textExtent.y; + double scale = Math.min(scaleX, scaleY); + + fontSize = Math.max(1, (int) (fontSize * scale)); + font.dispose(); + font = new Font(display, "Arial", fontSize, SWT.NORMAL); + + // Step 4: Create image of requested size + Image image = new Image(display, width, height); + GC gc = new GC(image); + gc.setFont(font); + gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK)); + gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); + gc.fillRectangle(image.getBounds()); + + // Step 5: Draw line and text + gc.setLineWidth(Math.max(1, width / 20)); // thickness relative to image height + gc.drawLine(0, 0, width/2, height); // example diagonal line + Point newTextExtent = gc.textExtent(text); + int x = (width - newTextExtent.x) / 2; // center horizontally + int y = (height - newTextExtent.y) / 2; // center vertically + gc.drawText(text, x, y, true); + + gc.dispose(); + + ImageData data =image.getImageData(); + + image.dispose(); + font.dispose(); + + return data; +} + + + + // Demo: paint to a canvas + public static void main(String[] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setText("Text Image Provider Demo"); + shell.setSize(4000, 4000); + + TextImageDataProvider provider = new TextImageDataProvider("abcd", 12, 1); + + Canvas canvas = new Canvas(shell, SWT.NONE); + canvas.setBounds(0, 0, 16000, 16000); + + canvas.addPaintListener(e -> { + // create image from provider + Image image = new Image(display, provider); + System.out.println(image.getBounds().width+"ssssss"+image.getBounds().height); + e.gc.drawImage(image, 0, 0); + e.gc.drawImage(image, 100, 0); + e.gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0,200, 200, 200); + e.gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0,400, 100, 100); + e.gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 100,400, 500, 500); + image.dispose(); + }); + + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) display.sleep(); + } + display.dispose(); + } +} From 1fde41643888805c049024ecda83cf317abdc3b8 Mon Sep 17 00:00:00 2001 From: arunjose696 Date: Mon, 8 Sep 2025 17:28:21 +0200 Subject: [PATCH 3/3] Temp --- .../win32/org/eclipse/swt/graphics/Image.java | 3 +- .../org/eclipse/swt/snippets/Snippet386.java | 211 ++++++++++-------- .../swt/snippets/TextImageDataProvider.java | 109 --------- 3 files changed, 117 insertions(+), 206 deletions(-) delete mode 100644 examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java index 13545803ddc..8d7de2af62b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -872,7 +872,7 @@ public static void drawAtTargetSize(GC gc, ImageData imageData, int width, int h StrictChecks.runWithStrictChecksDisabled(() -> { Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData); - gc.drawImage(imageToDraw, 0, 0, width, height, 0, 0, targetHeight, + gc.drawImage(imageToDraw, 0, 0, width, height, 0, 0, targetWidth, targetHeight, false); imageToDraw.dispose(); }); @@ -1999,7 +1999,6 @@ protected ImageData loadImageData(int targetWidth, int targetHeight) { int imageZoomForWidth = Math.round(100 * targetWidth / bounds.width); int imageZoomForHeight = Math.round(100 * targetHeight / bounds.height); int imageZoom = Math.max(imageZoomForWidth, imageZoomForHeight); - System.out.println(imageZoom); if (getAllCurrentMonitorZooms().contains(imageZoom)) { return getImageData(imageZoom); } diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java index ade6c846212..01b55599fe4 100644 --- a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java +++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet386.java @@ -1,8 +1,12 @@ package org.eclipse.swt.snippets; +import java.io.*; + import org.eclipse.swt.*; import org.eclipse.swt.custom.*; import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.DPIUtil.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; @@ -11,23 +15,22 @@ public class Snippet386 { public static void main(String[] args) { Display display = new Display(); - // Load all images Image[] images = new Image[] { new Image(display, createImageFileNameProvider("resources/Snippet386/collapseall.png")), - new Image(display, createImageDataProvider("resources/Snippet386/collapseall.svg")), new Image(display, createImageFileNameProvider("resources/Snippet386/collapseall.svg")), - new Image(display, createImageDataProvider("resources/Snippet386/collapseall.png")) + new Image(display, createImageDataProviderWithImageLoader()), + new Image(display, createImageDataProvider()), + new Image(display, createImageDataAtSizeProvider()) }; - // Descriptions for each image (rows) String[] descriptions = new String[] { - "ImageFileNameProvider with SVGs scaled by SVG rasterization", - "ImageDataProvider with SVGs scaled by SVG rasterization", "ImageFileNameProvider with PNGs scaled destructively", - "ImageDataProvider with PNGs scaled destructively" + "ImageFileNameProvider with SVGs scaled by SVG rasterization", + "ImageDataAtSizeProvider loading svgs with NativeImageLoader", + "ImageDataProvider with fixed font size for a given zoom", + "ImageDataAtSizeProvider which scales font size based on target height and width" }; - // Slice names (columns) Slice[] slices = { new Slice("Full", 0.0, 0.0, 1.0, 1.0), new Slice("Top Half", 0.0, 0.0, 1.0, 0.5), @@ -40,91 +43,110 @@ public static void main(String[] args) { createShellWithImages(display, images, descriptions, slices, "Snippet 386 - Flipped Layout"); } - - private static ImageFileNameProvider createImageFileNameProvider(String fileName) { - return new ImageFileNameProvider() { - - @Override - public String getImagePath(int zoom) { - return fileName; - } - }; + return zoom -> fileName; } - private static ImageDataProvider createImageDataProvider(String fileName) { + private static ImageDataProvider createImageDataProviderWithImageLoader() { return new ImageDataAtSizeProvider() { + @SuppressWarnings("restriction") @Override - public ImageData getImageData(int zoomLevel) { - int scaleFactor = zoomLevel / 100; - int baseWidth = 100; - int baseHeight = 100; - return getImageData(baseWidth * scaleFactor, baseHeight * scaleFactor); + public ImageData getImageData(int targetWidth, int targetHeight) { + try (InputStream stream = new FileInputStream("resources/Snippet386/collapseall.svg")) { + return NativeImageLoader.load(stream, new ImageLoader(), targetWidth, targetHeight); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + return null; } + @SuppressWarnings("restriction") @Override - public ImageData getImageData(int width, int height) { - Display display = Display.getDefault(); - - // Step 1: Determine font size based on height and zoom - int fontSize = (int) Math.round(height / 100.0); - if (fontSize <= 0) - fontSize = 1; - Font font = new Font(display, "Arial", fontSize, SWT.NORMAL); - - // Step 2: Measure text size - Image tmp = new Image(display, 1, 1); - GC measureGC = new GC(tmp); - measureGC.setFont(font); - String text = "abcd"; - Point textExtent = measureGC.textExtent(text); - measureGC.dispose(); - tmp.dispose(); - - // Step 3: Scale font to fit requested width/height - double scaleX = (double) width / textExtent.x; - double scaleY = (double) height / textExtent.y; - double scale = Math.min(scaleX, scaleY); - - fontSize = Math.max(1, (int) (fontSize * scale)); - font.dispose(); - font = new Font(display, "Arial", fontSize, SWT.NORMAL); - - // Step 4: Create image of requested size - Image image = new Image(display, width, height); - GC gc = new GC(image); - gc.setFont(font); - gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK)); - gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); - gc.fillRectangle(image.getBounds()); - - // Step 5: Draw line and text - gc.setLineWidth(Math.max(1, width / 20)); // thickness relative to image height - gc.drawLine(0, 0, width / 2, height); // example diagonal line - Point newTextExtent = gc.textExtent(text); - int x = (width - newTextExtent.x) / 2; // center horizontally - int y = (height - newTextExtent.y) / 2; // center vertically - gc.drawText(text, x, y, true); - - gc.dispose(); - - ImageData data = image.getImageData(); - - image.dispose(); - font.dispose(); - - return data; + public ImageData getImageData(int zoom) { + try (InputStream stream = new FileInputStream("resources/Snippet386/collapseall.svg")) { + return NativeImageLoader.load(new ElementAtZoom<>(stream, 100), new ImageLoader(), zoom).get(0).element(); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + return null; } }; } + private static ImageDataProvider createImageDataProvider() { + return new ImageDataProvider() { + @Override + public ImageData getImageData(int zoomLevel) { + int scaleFactor = zoomLevel / 100; + return createScaledTextImageData(100 * scaleFactor, 100 * scaleFactor); + } + }; + } + + private static ImageDataProvider createImageDataAtSizeProvider() { + return new ImageDataAtSizeProvider() { + @Override + public ImageData getImageData(int zoomLevel) { + int scaleFactor = zoomLevel / 100; + return createScaledTextImageData(100 * scaleFactor, 100 * scaleFactor); + } + + @Override + public ImageData getImageData(int width, int height) { + return createScaledTextImageData(width, height); + } + }; + } + + private static ImageData createScaledTextImageData(int width, int height) { + Display display = Display.getDefault(); + String text = "abcd"; + + int fontSize = Math.max(1, height / 100); + Font font = new Font(display, "Arial", fontSize, SWT.NORMAL); + + Image tmp = new Image(display, 1, 1); + GC measureGC = new GC(tmp); + measureGC.setFont(font); + Point textExtent = measureGC.textExtent(text); + measureGC.dispose(); + tmp.dispose(); + + double scale = Math.min((double) width / textExtent.x, (double) height / textExtent.y); + font.dispose(); + font = new Font(display, "Arial", Math.max(1, (int) (fontSize * scale)), SWT.NORMAL); + + Image image = new Image(display, width, height); + GC gc = new GC(image); + gc.setFont(font); + gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK)); + gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); + gc.fillRectangle(image.getBounds()); + + gc.setLineWidth(Math.max(1, width / 20)); + gc.drawLine(0, 0, width / 2, height); + + Point newTextExtent = gc.textExtent(text); + gc.drawText(text, (width - newTextExtent.x) / 2, (height - newTextExtent.y) / 2, true); + + gc.dispose(); + ImageData data = image.getImageData(); + + image.dispose(); + font.dispose(); + + return data; + } + static class Slice { String name; double xFrac, yFrac, wFrac, hFrac; Slice(String name, double x, double y, double w, double h) { this.name = name; - this.xFrac = x; this.yFrac = y; - this.wFrac = w; this.hFrac = h; + this.xFrac = x; + this.yFrac = y; + this.wFrac = w; + this.hFrac = h; } } @@ -139,42 +161,41 @@ private static void createShellWithImages(Display display, Image[] images, Strin scrolledComposite.setExpandHorizontal(true); scrolledComposite.setExpandVertical(true); - int boxW = 400; - int boxH = 400; - int gap = 20; - int titleHeight = 20; - int descHeight = 40; // description row height - int sliceHeaderHeight = 30; // column header height + int boxW = 200, boxH = 200, gap = 20; + int titleHeight = 20, descHeight = 40, sliceHeaderHeight = 30; - int rows = images.length; // one row per image - int cols = slices.length; // one column per slice + int rows = images.length; + int cols = slices.length; - int canvasWidth = (boxW + gap) * cols + gap; // extra gap for row headers + int canvasWidth = (boxW + gap) * cols + gap; int canvasHeight = (boxH + titleHeight + gap) * rows + descHeight + sliceHeaderHeight; canvas.setSize(canvasWidth, canvasHeight); scrolledComposite.setMinSize(canvasWidth, canvasHeight); canvas.addListener(SWT.Paint, e -> { - - // Draw column headers (slice names) + // Column headers for (int col = 0; col < cols; col++) { - int x = col * (boxW + gap) + gap; // leave gap for row header + int x = col * (boxW + gap) + gap; + Font font = new Font(display, "Arial", 18, SWT.NORMAL); + e.gc.setFont(font); e.gc.drawText(slices[col].name, x, 0, true); + font.dispose(); + } - // Draw each row (image type) + // Rows for (int row = 0; row < rows; row++) { Image image = images[row]; Rectangle rect = image.getBounds(); - int y = row * (boxH + titleHeight + gap) + descHeight + sliceHeaderHeight; - // Draw row description - e.gc.drawText(descriptions[row], 0, y, true); + Font font = new Font(display, "Arial", 18, SWT.NORMAL); + e.gc.setFont(font); + e.gc.drawText(descriptions[row], 0, y - 10, true); + font.dispose(); for (int col = 0; col < cols; col++) { Slice s = slices[col]; - int x = col * (boxW + gap) + gap; int srcX = (int) (rect.width * s.xFrac); @@ -182,12 +203,12 @@ private static void createShellWithImages(Display display, Image[] images, Strin int srcW = (int) (rect.width * s.wFrac); int srcH = (int) (rect.height * s.hFrac); - // Draw box int boxTop = y + titleHeight; e.gc.drawRectangle(x, boxTop, boxW, boxH); - // Draw image slice + e.gc.drawImage(image, srcX, srcY, srcW, srcH, x, boxTop, boxW, boxH); + } } }); diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java deleted file mode 100644 index 7b167843ce7..00000000000 --- a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/TextImageDataProvider.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.eclipse.swt.snippets; - -import org.eclipse.swt.*; -import org.eclipse.swt.graphics.*; -import org.eclipse.swt.widgets.*; - -public class TextImageDataProvider implements ImageDataAtSizeProvider { - private final String text; - - - - public TextImageDataProvider(String text, int baseFontSize, double zoom) { - this.text = text; - } - - @Override - public ImageData getImageData(int zoomLevel) { - int scaleFactor=zoomLevel/100; - int baseWidth =100; - int baseHeight=100; - return getImageData(baseWidth*scaleFactor,baseHeight*scaleFactor); - } - - @Override -public ImageData getImageData(int width, int height) { - Display display = Display.getDefault(); - - // Step 1: Determine font size based on height and zoom - int fontSize = (int) Math.round( height / 100.0); - if (fontSize <= 0) fontSize = 1; - Font font = new Font(display, "Arial", fontSize, SWT.NORMAL); - - // Step 2: Measure text size - Image tmp = new Image(display, 1, 1); - GC measureGC = new GC(tmp); - measureGC.setFont(font); - String text="abcd"; - Point textExtent = measureGC.textExtent(text); - measureGC.dispose(); - tmp.dispose(); - - // Step 3: Scale font to fit requested width/height - double scaleX = (double) width / textExtent.x; - double scaleY = (double) height / textExtent.y; - double scale = Math.min(scaleX, scaleY); - - fontSize = Math.max(1, (int) (fontSize * scale)); - font.dispose(); - font = new Font(display, "Arial", fontSize, SWT.NORMAL); - - // Step 4: Create image of requested size - Image image = new Image(display, width, height); - GC gc = new GC(image); - gc.setFont(font); - gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK)); - gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); - gc.fillRectangle(image.getBounds()); - - // Step 5: Draw line and text - gc.setLineWidth(Math.max(1, width / 20)); // thickness relative to image height - gc.drawLine(0, 0, width/2, height); // example diagonal line - Point newTextExtent = gc.textExtent(text); - int x = (width - newTextExtent.x) / 2; // center horizontally - int y = (height - newTextExtent.y) / 2; // center vertically - gc.drawText(text, x, y, true); - - gc.dispose(); - - ImageData data =image.getImageData(); - - image.dispose(); - font.dispose(); - - return data; -} - - - - // Demo: paint to a canvas - public static void main(String[] args) { - Display display = new Display(); - Shell shell = new Shell(display); - shell.setText("Text Image Provider Demo"); - shell.setSize(4000, 4000); - - TextImageDataProvider provider = new TextImageDataProvider("abcd", 12, 1); - - Canvas canvas = new Canvas(shell, SWT.NONE); - canvas.setBounds(0, 0, 16000, 16000); - - canvas.addPaintListener(e -> { - // create image from provider - Image image = new Image(display, provider); - System.out.println(image.getBounds().width+"ssssss"+image.getBounds().height); - e.gc.drawImage(image, 0, 0); -// e.gc.drawImage(image, 100, 0); -// e.gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0,200, 200, 200); - e.gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0,400, 100, 200); - e.gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height,0,600, 200, 100); - image.dispose(); - }); - - shell.open(); - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) display.sleep(); - } - display.dispose(); - } -}