Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1818,23 +1818,21 @@ public String toString () {
*
* @param gc the GC to draw on the resulting image
* @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 width the width to which the image is supposed to be scaled
* @param height 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 drawAtSize(GC gc, ImageData imageData, int width, int height) {
StrictChecks.runWithStrictChecksDisabled(() -> {
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
gc.drawImage(imageToDraw, 0, 0, CocoaDPIUtil.pixelToPoint(width), CocoaDPIUtil.pixelToPoint(height),
gc.drawImage(imageToDraw, 0, 0, CocoaDPIUtil.pixelToPoint(imageData.width), CocoaDPIUtil.pixelToPoint(imageData.height),
/*
* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but
* 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, Math.round(CocoaDPIUtil.pixelToPoint(width)), Math.round(CocoaDPIUtil.pixelToPoint(height)));
imageToDraw.dispose();
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*******************************************************************************
* 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;

/**
* Provides an API that is invoked by SWT when an image needs to be drawn at a
* specified width and height.
* <p>
* Client code must implement this interface to supply {@link ImageData} on demand.
* </p>
*
* @since 3.132
* @noreference This class is still experimental API and might be subject to change.
*/
public interface ImageDataAtSizeProvider extends ImageDataProvider {

/**
* Returns the {@link ImageData} for the given width and height.
*
* <p>
* <b>Implementation notes:</b>
* </p>
* <ul>
* <li>Returning <code>null</code> is not permitted. If <code>null</code> is
* returned, SWT will throw an exception when loading the image.</li>
* <li>The returned {@link ImageData} must match the requested width and height
* exactly. Implementations should ensure proper resizing and scaling of the
* image based on the height and width requested</li>
* </ul>
*
* @param width the desired width of the {@link ImageData} to be returned
* @param height the desired height of the {@link ImageData} to be returned
* @return the {@link ImageData} that exactly matches the requested width and
* height
* @since 3.132
*/
ImageData getImageData(int width, int height);

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public static boolean canLoadAtZoom(String filename, int fileZoom, int targetZoo
return ImageLoader.canLoadAtZoom(filename, fileZoom, targetZoom);
}

static boolean isDynamicallySizable(String filename) {
return ImageLoader.isDynamicallySizable(filename);
}

static boolean isDynamicallySizable(InputStream stream) {
return ImageLoader.isDynamicallySizable(stream);
}

public static ElementAtZoom<ImageData> loadByZoom(InputStream stream, int fileZoom, int targetZoom) {
List<ElementAtZoom<ImageData>> data = new ImageLoader().loadByZoom(stream, fileZoom, targetZoom);
if (data.isEmpty()) SWT.error(SWT.ERROR_INVALID_IMAGE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,19 @@ 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;
}

static boolean isDynamicallySizable(InputStream stream) {
return FileFormat.isDynamicallySizableFormat(stream);
}

/**
* Saves the image data in this ImageLoader to the specified stream.
* The format parameter can have one of the following values:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,24 @@ 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::drawAtSize, scaledWidth, scaledHeight);
}

@FunctionalInterface
private interface ImageDrawFunction {
void draw(GC gc, ImageData imageData, int width, int height);
}

private static ImageData scaleImage(Device device, final ImageData imageData, ImageDrawFunction drawFunction,
int scaledWidth, int scaledHeight) {
int defaultZoomLevel = 100;
boolean useSmoothScaling = isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
if (useSmoothScaling) {
ImageGcDrawer drawer = new ImageGcDrawer() {
@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, imageWidth, imageHeight);
};

@Override
Expand All @@ -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::drawAtSize, targetWidth, targetHeight);
}

public static boolean isSmoothScalingEnabled() {
return autoScaleMethod == AutoScaleMethod.SMOOTH;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1579,22 +1579,21 @@ public String toString () {
*
* @param gc the GC to draw on the resulting image
* @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 width the width to which the image is supposed to be scaled
* @param height 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 drawAtSize(GC gc, ImageData imageData, int width, int height) {
StrictChecks.runWithStrictChecksDisabled(() -> {
Image imageToDraw = new Image(gc.device, (ImageDataProvider) zoom -> imageData);
gc.drawImage(imageToDraw, 0, 0, width, height,
gc.drawImage(imageToDraw, 0, 0, imageData.width, imageData.height,
/*
* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but
* 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, width, height);
imageToDraw.dispose();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,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);
}
}

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

storeAndApplyOperationForExistingHandle(new DrawScalingImageToImageOperation(image, new Rectangle(0, 0, 0, 0),
new Rectangle(destX, destY, destWidth, destHeight)));
storeAndApplyOperationForExistingHandle(new DrawScaledImageOperation(image, new Rectangle(destX, destY, destWidth, destHeight)));
}

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

private class DrawScaledImageOperation extends ImageOperation {
private final Rectangle destination;

DrawScaledImageOperation(Image image, Rectangle destination) {
super(image);
this.destination = destination;
}

@Override
void apply() {
int gcZoom = getZoom();
drawImage(getImage(), destination.x, destination.y, destination.width, destination.height, gcZoom);
}
}

private void drawImage(Image image, int destX, int destY, int destWidth, int destHeight, int imageZoom) {
Rectangle destPixels = Win32DPIUtils.pointToPixel(drawable, new Rectangle(destX, destY, destWidth, destHeight), imageZoom);
image.executeOnImageHandleAtSize((tempHandle, handleSize) -> {
drawImage(image, 0, 0, handleSize.x, handleSize.y, destPixels.x, destPixels.y, destPixels.width,
destPixels.height, false, tempHandle);
}, destPixels.width, destPixels.height);
}

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);
Expand All @@ -1237,7 +1260,7 @@ private void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHei
}
}
}
drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height, false, scaledImageZoom);
drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height, false, image.getHandle(scaledImageZoom, data.nativeZoom));
}

private class DrawImageToImageOperation extends ImageOperation {
Expand All @@ -1254,17 +1277,19 @@ 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);

if (srcWidth == 0 && srcHeight == 0) {
srcWidth = imgWidth;
srcHeight = imgHeight;
Expand Down Expand Up @@ -1319,13 +1344,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;
}
}
Expand Down
Loading
Loading