Skip to content

Move ImageData Scaling Impl to Image for all platforms #1933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
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 @@ -1408,7 +1408,39 @@ public ImageData getImageData(int zoom) {
} finally {
if (pool != null) pool.release();
}
return DPIUtil.scaleImageData (device, getImageData(100), zoom, 100);
ImageData imageData = getImageData(100);
return scaledTo (imageData, zoom, 100, DPIUtil.getScalingType(imageData));
}

private ImageData scaledTo(ImageData imageData, int targetZoom, int currentZoom, int scaleType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private ImageData scaledTo(ImageData imageData, int targetZoom, int currentZoom, int scaleType) {
private Image scaledTo(int targetZoom, int scaleType) {

so the caller can operate on the Image (dispose it or use it or ...) also current zoom seems always 100, the image data can also better be fetched by the method itself

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This always being 100 is a specifics of the MacOS implementation. Independent from that making a scaledTo method return an Image would not really make sense, as there is no image being scaled (from which you could retrieve the data scaled according to different zooms again), but the data itself is scaled.

if (imageData == null || currentZoom == targetZoom || !device.isAutoScalable()) {
return imageData;
}
float scaleFactor = (float) targetZoom / (float) currentZoom;
int scaledWidth = Math.round (imageData.width * scaleFactor);
int scaledHeight = Math.round (imageData.height * scaleFactor);
switch (scaleType) {
case SWT.SMOOTH:
return scaleUsingSmoothScaling(imageData, scaledWidth, scaledHeight);
default:
return imageData.scaledTo(scaledWidth, scaledHeight);
}
}

private ImageData scaleUsingSmoothScaling(ImageData imageData, int width, int height) {
Image original = new Image (device, (ImageDataProvider) zoom -> imageData);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we create a new image here and not using this Image?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that create an endless loop? When you try to create image data at a specific zoom by painting the image into a GC, which requires requesting the image data at that specific zoom, this will likely produce a stack overflow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was that at the Image level we can access any internal API as required (including a painting the current ImageData instead of using ImageDataProvider).

/* Create a 24 bit image data with alpha channel */
final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
resultData.alphaData = new byte [width * height];
Image resultImage = new Image (device, (ImageDataProvider) zoom -> resultData);
GC gc = new GC (resultImage);
gc.setAntialias (SWT.ON);
gc.drawImage (original, 0, 0, imageData.width, imageData.height, 0, 0, width, height, false);
gc.dispose ();
original.dispose ();
ImageData result = resultImage.getImageData (DPIUtil.getDeviceZoom());
resultImage.dispose ();
return result;
}

/** Returns the best available representation. May be 100% or 200% iff there is an image provider. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,6 @@ private static enum AutoScaleMethod { AUTO, NEAREST, SMOOTH }
}
}

/**
* Auto-scale down ImageData
*/
public static ImageData autoScaleDown (Device device, final ImageData imageData) {
if (deviceZoom == 100 || imageData == null || (device != null && !device.isAutoScalable())) return imageData;
float scaleFactor = 1.0f / getScalingFactor (deviceZoom);
return autoScaleImageData(device, imageData, scaleFactor);
}

public static int[] autoScaleDown(int[] pointArray) {
if (deviceZoom == 100 || pointArray == null) return pointArray;
float scaleFactor = getScalingFactor (deviceZoom);
Expand Down Expand Up @@ -272,54 +263,18 @@ public static Rectangle scaleDown(Drawable drawable, Rectangle rect, int zoom) {
return scaleDown (rect, zoom);
}

/**
* Auto-scale image with ImageData
*/
public static ImageData scaleImageData (Device device, final ImageData imageData, int targetZoom, int currentZoom) {
if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
float scaleFactor = (float) targetZoom / (float) currentZoom;
return autoScaleImageData(device, imageData, scaleFactor);
}


public static ImageData scaleImageData (Device device, final ElementAtZoom<ImageData> elementAtZoom, int targetZoom) {
return scaleImageData(device, elementAtZoom.element(), targetZoom, elementAtZoom.zoom());
}

private static ImageData autoScaleImageData (Device device, final ImageData imageData, float scaleFactor) {
// Guards are already implemented in callers: if (deviceZoom == 100 || imageData == null || scaleFactor == 1.0f) return imageData;
int width = imageData.width;
int height = imageData.height;
int scaledWidth = Math.round (width * scaleFactor);
int scaledHeight = Math.round (height * scaleFactor);
boolean useSmoothScaling = isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
if (useSmoothScaling) {
Image original = new Image (device, (ImageDataProvider) zoom -> imageData);
/* Create a 24 bit image data with alpha channel */
final ImageData resultData = new ImageData (scaledWidth, scaledHeight, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
resultData.alphaData = new byte [scaledWidth * scaledHeight];
Image resultImage = new Image (device, (ImageDataProvider) zoom -> resultData);
GC gc = new GC (resultImage);
gc.setAntialias (SWT.ON);
gc.drawImage (original, 0, 0, autoScaleDown (width), autoScaleDown (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 (autoScaleDown (width * scaleFactor)), Math.round (autoScaleDown (height * scaleFactor)));
gc.dispose ();
original.dispose ();
ImageData result = resultImage.getImageData (getDeviceZoom ());
resultImage.dispose ();
return result;
} else {
return imageData.scaledTo (scaledWidth, scaledHeight);
public static int getScalingType(ImageData imageData) {
switch(autoScaleMethod) {
case SMOOTH:
if (imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK) {
return SWT.SMOOTH;
}
return SWT.DEFAULT;
default:
return SWT.DEFAULT;
}
}

public static boolean isSmoothScalingEnabled() {
return autoScaleMethod == AutoScaleMethod.SMOOTH;
}

/**
* Returns a new rectangle as per the scaleFactor.
*/
Expand All @@ -334,22 +289,6 @@ public static Rectangle scaleBounds (Rectangle rect, int targetZoom, int current
return returnRect;
}

/**
* Auto-scale ImageData to device zoom that are at given zoom factor.
*/
public static ImageData autoScaleImageData (Device device, final ImageData imageData, int imageDataZoomFactor) {
if (deviceZoom == imageDataZoomFactor || imageData == null || (device != null && !device.isAutoScalable())) return imageData;
float scaleFactor = (float) deviceZoom / imageDataZoomFactor;
return autoScaleImageData(device, imageData, scaleFactor);
}

/**
* Auto-scale up ImageData to device zoom that is at 100%.
*/
public static ImageData autoScaleUp (Device device, final ImageData imageData) {
return autoScaleImageData(device, imageData, 100);
}

public static int[] autoScaleUp(int[] pointArray) {
return scaleUp(pointArray, deviceZoom);
}
Expand Down Expand Up @@ -640,10 +579,6 @@ public static boolean useCairoAutoScale() {
}

public static int getZoomForAutoscaleProperty (int nativeDeviceZoom) {
return getZoomForAutoscaleProperty(nativeDeviceZoom, autoScaleValue);
}

private static int getZoomForAutoscaleProperty (int nativeDeviceZoom, String autoScaleValue) {
int zoom = 0;
if (autoScaleValue != null) {
if ("false".equalsIgnoreCase (autoScaleValue)) {
Expand Down Expand Up @@ -746,7 +681,11 @@ public AutoScaleImageDataProvider(Device device, ImageData data, int zoom){
}
@Override
public ImageData getImageData(int zoom) {
return DPIUtil.scaleImageData(device, imageData, zoom, currentZoom);
Image image = new Image(device, imageData);
int adjustedZoom = (int) ((float) getDeviceZoom() / (float) currentZoom) * zoom;
ImageData imageData = image.getImageData(adjustedZoom);
image.dispose();
return imageData;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ public Image(Device device, ImageData data) {
super(device);
if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
currentDeviceZoom = DPIUtil.getDeviceZoom();
data = DPIUtil.autoScaleUp (device, data);
data = scaledTo(data, DPIUtil.getDeviceZoom(), 100, DPIUtil.getScalingType(data));
init(data);
init();
}
Expand Down Expand Up @@ -466,8 +466,8 @@ public Image(Device device, ImageData source, ImageData mask) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
currentDeviceZoom = DPIUtil.getDeviceZoom();
source = DPIUtil.autoScaleUp (device, source);
mask = DPIUtil.autoScaleUp (device, mask);
source = scaledTo(source, currentDeviceZoom, 100, DPIUtil.getScalingType(source));
mask = scaledTo(mask, currentDeviceZoom, 100, DPIUtil.getScalingType(mask));
mask = ImageData.convertMask (mask);
ImageData image = new ImageData(source.width, source.height, source.depth, source.palette, source.scanlinePad, source.data);
image.maskPad = mask.scanlinePad;
Expand Down Expand Up @@ -533,7 +533,7 @@ public Image(Device device, InputStream stream) {
super(device);
currentDeviceZoom = DPIUtil.getDeviceZoom();
ElementAtZoom<ImageData> image = ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, currentDeviceZoom);
ImageData data = DPIUtil.scaleImageData(device, image, currentDeviceZoom);
ImageData data = scaledTo(image.element(), currentDeviceZoom, image.zoom(), DPIUtil.getScalingType(image.element()));
init(data);
init();
}
Expand Down Expand Up @@ -575,7 +575,7 @@ public Image(Device device, String filename) {
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
currentDeviceZoom = DPIUtil.getDeviceZoom();
ElementAtZoom<ImageData> image = ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, currentDeviceZoom);
ImageData data = DPIUtil.scaleImageData(device, image, currentDeviceZoom);
ImageData data = scaledTo(image.element(), currentDeviceZoom, image.zoom(), DPIUtil.getScalingType(image.element()));
init(data);
init();
}
Expand Down Expand Up @@ -744,7 +744,7 @@ boolean refreshImageForZoom () {
if (deviceZoomLevel != currentDeviceZoom) {
ImageData data = getImageDataAtCurrentZoom();
destroy ();
ImageData resizedData = DPIUtil.scaleImageData(device, data, deviceZoomLevel, currentDeviceZoom);
ImageData resizedData = scaledTo(data, deviceZoomLevel, currentDeviceZoom, DPIUtil.getScalingType(data));
init(resizedData);
init();
refreshed = true;
Expand Down Expand Up @@ -778,15 +778,15 @@ private void initFromFileNameProvider(int zoom) {
ElementAtZoom<ImageData> imageDataAtZoom = ImageDataLoader.load(fileForZoom.element(), fileForZoom.zoom(), zoom);
ImageData imageData = imageDataAtZoom.element();
if (imageDataAtZoom.zoom() != zoom) {
imageData = DPIUtil.scaleImageData(device, imageDataAtZoom, zoom);
imageData = scaledTo(imageDataAtZoom.element(), zoom, imageDataAtZoom.zoom(), DPIUtil.getScalingType(imageData));
}
init(imageData);
}
}

private void initFromImageDataProvider(int zoom) {
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (imageDataProvider, zoom);
ImageData resizedData = DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
ImageData resizedData = scaledTo(data.element(), zoom, data.zoom(), DPIUtil.getScalingType(data.element()));
init(resizedData);
}

Expand Down Expand Up @@ -1146,17 +1146,50 @@ public ImageData getImageData (int zoom) {
return getImageDataAtCurrentZoom();
} else if (imageDataProvider != null) {
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (imageDataProvider, zoom);
return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
return scaledTo(data.element(), zoom, data.zoom(), DPIUtil.getScalingType(data.element()));
} else if (imageFileNameProvider != null) {
ElementAtZoom<String> fileName = DPIUtil.validateAndGetImagePathAtZoom (imageFileNameProvider, zoom);
return DPIUtil.scaleImageData (device, new ImageData (fileName.element()), zoom, fileName.zoom());
ImageData imageData = new ImageData (fileName.element());
return scaledTo(imageData, zoom, fileName.zoom(), DPIUtil.getScalingType(imageData));
} else if (imageGcDrawer != null) {
return drawWithImageGcDrawer(width, height, zoom);
} else {
return DPIUtil.scaleImageData (device, getImageDataAtCurrentZoom (), zoom, currentDeviceZoom);
ImageData imageData = getImageDataAtCurrentZoom ();
return scaledTo(imageData, zoom, currentDeviceZoom, DPIUtil.getScalingType(imageData));
}
}

private ImageData scaledTo(ImageData imageData, int targetZoom, int currentZoom, int scaleType) {
if (imageData == null || currentZoom == targetZoom || !device.isAutoScalable()) {
return imageData;
}
float scaleFactor = (float) targetZoom / (float) currentZoom;
int scaledWidth = Math.round (imageData.width * scaleFactor);
int scaledHeight = Math.round (imageData.height * scaleFactor);
switch (scaleType) {
case SWT.SMOOTH:
return scaleUsingSmoothScaling(imageData, scaledWidth, scaledHeight);
default:
return imageData.scaledTo(scaledWidth, scaledHeight);
}
}

private ImageData scaleUsingSmoothScaling(ImageData imageData, int width, int height) {
Image original = new Image (device, (ImageDataProvider) zoom -> imageData);
/* Create a 24 bit image data with alpha channel */
final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
resultData.alphaData = new byte [width * height];
Image resultImage = new Image (device, (ImageDataProvider) zoom -> resultData);
GC gc = new GC (resultImage);
gc.setAntialias (SWT.ON);
gc.drawImage (original, 0, 0, imageData.width, imageData.height, 0, 0, width, height, false);
gc.dispose ();
original.dispose ();
ImageData result = resultImage.getImageData (DPIUtil.getDeviceZoom());
resultImage.dispose ();
return result;
}

private ImageData drawWithImageGcDrawer(int width, int height, int zoom) {
Image image = new Image(device, width, height);
GC gc = new GC(image);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,15 +372,21 @@ public static Long win32_getHandle (Cursor cursor, int zoom) {
if (cursor.source == null) {
cursor.setHandleForZoomLevel(cursor.handle, zoom);
} else {
ImageData source = DPIUtil.scaleImageData(cursor.device, cursor.source, zoom, DEFAULT_ZOOM);
Image image;
ImageData source, mask;
Cursor newCursor;
image = new Image(cursor.device, cursor.source);
source = image.getImageData(zoom);
image.dispose();
if (cursor.isIcon) {
Cursor newCursor = new Cursor(cursor.device, source, cursor.hotspotX, cursor.hotspotY);
cursor.setHandleForZoomLevel(newCursor.handle, zoom);
newCursor = new Cursor(cursor.device, source, cursor.hotspotX, cursor.hotspotY);
} else {
ImageData mask = DPIUtil.scaleImageData(cursor.device, cursor.mask, zoom, DEFAULT_ZOOM);
Cursor newCursor = new Cursor(cursor.device, source, mask, cursor.hotspotX, cursor.hotspotY);
cursor.setHandleForZoomLevel(newCursor.handle, zoom);
image = new Image(cursor.device, cursor.mask);
mask = image.getImageData(zoom);
image.dispose();
newCursor = new Cursor(cursor.device, source, mask, cursor.hotspotX, cursor.hotspotY);
}
cursor.setHandleForZoomLevel(newCursor.handle, zoom);
}
return cursor.zoomLevelToHandle.get(zoom);
}
Expand Down
Loading
Loading