Skip to content

Conversation

arunjose696
Copy link
Contributor

@arunjose696 arunjose696 commented Sep 19, 2025

Implementations of DynamicallySizableImageDataProvider can provide image data at a requested targetHeight and targetWidth. The behavior of GC#drawImage() is updated: when drawing a dynamically sizeable image, it requests a handle at the target size. If the image can be loaded at that size, it is used directly; otherwise, the image is loaded as before and scaled using zoom.

In the Windows implementation, if the source or destination width or height is set to zero, the image will be drawn fully, avoiding the need to call getBounds() to determine the image size.
With this changes we need to test the following

  1. The svgs are rendered at custom sizes without blurry scaling (this can be compared with current commit vs master)
  2. Drawing images with targetHeight and targetWidth with ImageDataAtSizeProvider (comparison can be made against ImageDataProvider vs ImageDataAtSizeProvider both at this commit)
    Steps to reproduce:
snippet 1 to check drawing svgs at custom sizes (click to show snippet) **Step 1**: Copy the eclipse.svg to same path where the snippets are and run the below snippet. **Step 2**: Run the snippet

package org.eclipse.swt.snippets;

package org.eclipse.swt.snippets;

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();

        Image[] images = new Image[] {

            new Image(display, createImageFileNameProvider("src/org/eclipse/swt/snippets/eclipse.svg")),

        };

        String[] descriptions = new String[] {

            "ImageFileNameProvider with SVGs scaled by SVG rasterization"
        };

        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)
        };

        createShellWithImages(display, images, descriptions, slices, "Snippet 386 - Flipped Layout");
    }

    private static ImageFileNameProvider createImageFileNameProvider(String fileName) {
        return zoom -> {
        	if(zoom==100)
        	return fileName;
        	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 = 1500, boxH = 1500, gap = 20;
        int titleHeight = 20, descHeight = 40, sliceHeaderHeight = 30;

        int rows = images.length;
        int cols = slices.length;

        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 -> {
            // Column headers
            for (int col = 0; col < cols; col++) {
                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();

            }

            // Rows
            for (int row = 0; row < rows; row++) {
                Image image = images[row];
                Rectangle rect = image.getBounds();
                int y = row * (boxH + titleHeight + gap) + descHeight + sliceHeaderHeight;

                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);
                    int srcY = (int) (rect.height * s.yFrac);
                    int srcW = (int) (rect.width * s.wFrac);
                    int srcH = (int) (rect.height * s.hFrac);

                    int boxTop = y + titleHeight;
                    e.gc.drawRectangle(x, boxTop, boxW, boxH);


                    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();
    }
}

before

after
**snippet 2** Drawing images with ImageDataAtSizeProvider

package org.eclipse.swt.snippets;

package org.eclipse.swt.snippets;

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();

        Image[] images = new Image[] {
            new Image(display, createImageDataProvider()),
            new Image(display, createImageDataAtSizeProvider())
        };

        String[] descriptions = new String[] {

            "ImageDataProvider with fixed font size for a given zoom",
            "ImageDataAtSizeProvider which scales font size based on target height and width"
        };

        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 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;
        }
    }

    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 = 500, boxH = 500, gap = 20;
        int titleHeight = 20, descHeight = 40, sliceHeaderHeight = 30;

        int rows = images.length;
        int cols = slices.length;

        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 -> {
            // Column headers
            for (int col = 0; col < cols; col++) {
                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();

            }

            // Rows
            for (int row = 0; row < rows; row++) {
                Image image = images[row];
                Rectangle rect = image.getBounds();
                int y = row * (boxH + titleHeight + gap) + descHeight + sliceHeaderHeight;

                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);
                    int srcY = (int) (rect.height * s.yFrac);
                    int srcW = (int) (rect.width * s.wFrac);
                    int srcH = (int) (rect.height * s.hFrac);

                    int boxTop = y + titleHeight;
                    e.gc.drawRectangle(x, boxTop, boxW, boxH);


                    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();
    }
}
image

This depends on #2514 and #2509, Only last commit is relevant to this PR

Copy link
Contributor

github-actions bot commented Sep 19, 2025

Test Results

  118 files  ±0    118 suites  ±0   11m 12s ⏱️ + 1m 6s
4 432 tests ±0  4 409 ✅  - 2  17 💤 ±0  6 ❌ +2 
  298 runs  ±0    288 ✅  - 2   4 💤 ±0  6 ❌ +2 

For more details on these failures, see this check.

Results for commit 063d70b. ± Comparison against base commit 35119f0.

♻️ This comment has been updated with latest results.

… and width

Adding internal API to SVGRasterizer and simple tests to see if the svgs
are loaded at right sizes by the rasterizer
@arunjose696 arunjose696 force-pushed the arunjose696/326/imageAtSizeProvider branch from fa4a774 to 770c6c2 Compare September 22, 2025 16:25
…d width

Extend ImageLoader to able to load images at a given target height and
width in addition to the current behaviour of only being able to load
images at a target zoom. This is required to load images in svg format
at custom height and width.
@arunjose696 arunjose696 force-pushed the arunjose696/326/imageAtSizeProvider branch from 770c6c2 to 8cf1bfb Compare September 22, 2025 16:56
Implementations of DynamicallySizableImageDataProvider can provide image
data at a requested targetHeight and targetWidth. The behavior of
GC#drawImage() is updated: when drawing a dynamically sizeable image, it
requests a handle at the target size. If the image can be loaded at that
size, it is used directly; otherwise, the image is loaded as before and
scaled using zoom.

In the Windows implementation, if the source or destination width/height
is set to zero, the image will be drawn fully, avoiding the need to call
getBounds() to determine the image size.
@arunjose696 arunjose696 force-pushed the arunjose696/326/imageAtSizeProvider branch from 8cf1bfb to 063d70b Compare September 23, 2025 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant