Bug 97506 - [HiDPI] ImageData.scaledTo() should use a better interpolation method
Summary: [HiDPI] ImageData.scaledTo() should use a better interpolation method
Status: NEW
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 3.1   Edit
Hardware: All All
: P3 enhancement with 3 votes (vote)
Target Milestone: ---   Edit
Assignee: Platform-SWT-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 495417 4835 399786 488913
  Show dependency tree
 
Reported: 2005-05-31 10:56 EDT by Billy Biggs CLA
Modified: 2022-04-26 05:37 EDT (History)
17 users (show)

See Also:


Attachments
SnippetScaling.java (8.31 KB, text/plain)
2016-01-12 11:32 EST, Markus Keller CLA
no flags Details
The output of the SnippedScaling.java together with some AWT experiments (66.31 KB, image/png)
2018-03-03 08:33 EST, Conrad Groth CLA
no flags Details
The SnippetScaling enhanced by AWT scaling (11.86 KB, text/plain)
2018-03-03 08:46 EST, Conrad Groth CLA
no flags Details
Icon scaling demo (13.96 KB, application/java-archive)
2020-09-28 13:41 EDT, Stanimir Stamenkov CLA
no flags Details
Icons demo at 125% screen (25.91 KB, image/png)
2020-09-28 13:42 EDT, Stanimir Stamenkov CLA
no flags Details
Icons demo at 150% screen (32.58 KB, image/png)
2020-09-28 13:42 EDT, Stanimir Stamenkov CLA
no flags Details
Icons demo at 200% screen (39.45 KB, image/png)
2020-09-28 13:43 EDT, Stanimir Stamenkov CLA
no flags Details
Toolbar icons – Scaling factors and methods (323.79 KB, image/png)
2022-04-26 05:37 EDT, Stanimir Stamenkov CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Billy Biggs CLA 2005-05-31 10:56:29 EDT
Currently, ImageData.scaledTo() does not interpolate pixels, it instead uses
nearest-neighbor sampling.  This does not give good results when scaling common
images in user interfaces such as icons.

A better algorithm to use in this case would be linear interpolation, as it
provides a reasonable speed-quality tradeoff for user interface graphics.
Comment 1 Steve Cohen CLA 2006-06-30 17:35:49 EDT
I agree, my application used scaledTo to scale photos, and the results were atrocious. Drawing an image onto a smaller image produces results that are acceptable.

Why has nothing been done with this bug since 2005?
Comment 2 Markus Keller CLA 2016-01-12 11:22:30 EST
DPIUtil#autoScaleImageData(..) also uses ImageData#scaledTo(..), which e.g. makes toolbar buttons look bad when scaled to 1.5.
Comment 3 Markus Keller CLA 2016-01-12 11:32:01 EST
Created attachment 259133 [details]
SnippetScaling.java

Here's a snippet (paste to the org.eclipse.swt.snippets project) that tests various icons with different ways of scaling them to 0.5, 1, 1.25, 1.5, and 2x

I don't see a big difference between the different ways to set up the GC for GC#drawImage(..) with GC#setAntialias(..) and GC#setTransform(..). Any of those approaches can be used to improve ImageData#scaledTo(..).
Comment 4 Markus Keller CLA 2016-03-22 14:51:14 EDT
For integer scaling factors, we should give users a way to choose between pixel-doubling and anti-aliasing. At 200%, it's largely a matter of preference. Both strategies are suboptimal, depending on the concrete icons.

Abstract forms like Minimize, Maximize, Menu, Resume, Suspend, Terminate look better with pixel-doubling. Other icons may look better with a little blur.

E.g. enable anti-aliasing with: -Dorg.eclipse.swt.scaled.images=antialias
Comment 5 Eclipse Genie CLA 2016-04-11 15:44:00 EDT
New Gerrit change created: https://git.eclipse.org/r/70415
Comment 6 Sravan Kumar Lakkimsetti CLA 2016-04-21 04:15:59 EDT
May be we can use gdk_pixbuf_scale_simple api for scaling on gtk and stretchBlt api for windows. I used gdk_pixbuf_scale_simple with GDK_INTERP_BILENEAR. the image quality is pretty good.

For more information on GTK https://developer.gnome.org/gdk-pixbuf/unstable/gdk-pixbuf-Scaling.html#gdk-pixbuf-scale-simple

In case of cairo we can use cairo_scale to scale the image
Comment 7 Markus Keller CLA 2016-04-21 11:29:42 EDT
(In reply to Sravan Kumar Lakkimsetti from comment #6)
Sounds good, and it's effectively what the GC#drawImage(..) in my hack does.

How does your proposal work with transparency in the image? If the implementation needs to reuse large parts of the existing implementation, then please extract the reusable parts and avoid code duplication.

For non-integer scale factors, the current implementation of ImageData#scaledTo(..) is unusable, and I have no problem with replacing that with a better method. However, I think we need to keep at least an option to use the pixel-doubling method for integer multiples of the original size. Blurring images when scaling *2 or *3 could be considered a breaking change for existing API clients.
Comment 8 Sravan Kumar Lakkimsetti CLA 2016-04-22 02:04:00 EDT
(In reply to Markus Keller from comment #7)
> (In reply to Sravan Kumar Lakkimsetti from comment #6)
> Sounds good, and it's effectively what the GC#drawImage(..) in my hack does.
> 
> How does your proposal work with transparency in the image? If the
> implementation needs to reuse large parts of the existing implementation,
> then please extract the reusable parts and avoid code duplication.
> 
> For non-integer scale factors, the current implementation of
> ImageData#scaledTo(..) is unusable, and I have no problem with replacing
> that with a better method. However, I think we need to keep at least an
> option to use the pixel-doubling method for integer multiples of the
> original size. Blurring images when scaling *2 or *3 could be considered a
> breaking change for existing API clients.

I haven't tried using the transparency yet. I believe it will work. I will try it.
Comment 9 Sravan Kumar Lakkimsetti CLA 2016-04-22 04:47:22 EDT
Markus,

I have dynamic scaling bug to be fixed. I may not be able to focus on this in M7. I would like to work on it later. I am thinking of SR1. will that be ok?
Comment 10 Sravan Kumar Lakkimsetti CLA 2016-04-25 07:28:16 EDT
Targeting to 4.7 as it is difficult to complete in 4.6 and we will back port to 4.6 post neon release
Comment 11 Markus Keller CLA 2016-05-10 17:24:34 EDT
I don't think we should change ImageData.scaledTo(). The API has always implemented nearest-neighbor scaling, and it's quite possible that there are clients that rely on this behavior. We even have an invocation in SWT's JPEGFileFormat.

I've created https://git.eclipse.org/r/#/c/70415/5 , which implements smoother scaling for HiDPI in DPIUtil#autoScaleImageData(..).

The scaling method can be chosen explicitly with the command line parameter
-Dswt.autoScale.method=nearest
or
-Dswt.autoScale.method=smooth

On GTK, this works fine, and the smooth scaling that is used by default for fractional scale factors looks much better.

But on Windows, I ran into huge trouble, see the FIXME in DPIUtil.

Drawing of PNGs and GIFs seems to work differently, and both don't work as they should. I didn't find any solution to make GC#drawImage(..) properly draw onto a transparent base image.

=> We can't fix auto-scaling of images without a solution for this problem on Windows. Any help would be appreciated. I'm out of ideas.
Comment 12 Sravan Kumar Lakkimsetti CLA 2016-05-11 05:17:56 EDT
(In reply to Markus Keller from comment #11)
> I don't think we should change ImageData.scaledTo(). The API has always
> implemented nearest-neighbor scaling, and it's quite possible that there are
> clients that rely on this behavior. We even have an invocation in SWT's
> JPEGFileFormat.
> 
> I've created https://git.eclipse.org/r/#/c/70415/5 , which implements
> smoother scaling for HiDPI in DPIUtil#autoScaleImageData(..).
> 
> The scaling method can be chosen explicitly with the command line parameter
> -Dswt.autoScale.method=nearest
> or
> -Dswt.autoScale.method=smooth
> 
> On GTK, this works fine, and the smooth scaling that is used by default for
> fractional scale factors looks much better.
> 
> But on Windows, I ran into huge trouble, see the FIXME in DPIUtil.
> 
> Drawing of PNGs and GIFs seems to work differently, and both don't work as
> they should. I didn't find any solution to make GC#drawImage(..) properly
> draw onto a transparent base image.
> 
> => We can't fix auto-scaling of images without a solution for this problem
> on Windows. Any help would be appreciated. I'm out of ideas.

This looks much better on gtk. I haven't tested the windows yet.
Comment 13 Markus Keller CLA 2016-05-11 16:35:11 EDT
Back to inbox. The autoScale stuff has been moved to a new bug 493462.

Let's keep this bug for its original intended purpose:
Offer an API to scale images with a better interpolation method.

It should not be ImageData#scaledTo(..), but a new method.
Comment 14 Niraj Modi CLA 2018-02-09 03:59:59 EST
Hi Conrad,
Any idea on this issue ?
Comment 15 Niraj Modi CLA 2018-02-09 05:44:47 EST
(In reply to Markus Keller from comment #11)
> I don't think we should change ImageData.scaledTo(). The API has always
> implemented nearest-neighbor scaling, and it's quite possible that there are
> clients that rely on this behavior. We even have an invocation in SWT's
> JPEGFileFormat.
> 
> I've created https://git.eclipse.org/r/#/c/70415/5 , which implements
> smoother scaling for HiDPI in DPIUtil#autoScaleImageData(..).
> 
> The scaling method can be chosen explicitly with the command line parameter
> -Dswt.autoScale.method=nearest
> or
> -Dswt.autoScale.method=smooth
> 
> On GTK, this works fine, and the smooth scaling that is used by default for
> fractional scale factors looks much better.
> 
> But on Windows, I ran into huge trouble, see the FIXME in DPIUtil.
> 
> Drawing of PNGs and GIFs seems to work differently, and both don't work as
> they should. I didn't find any solution to make GC#drawImage(..) properly
> draw onto a transparent base image.
> 
> => We can't fix auto-scaling of images without a solution for this problem
> on Windows. Any help would be appreciated. I'm out of ideas.

Summarizing this issue Part1:
- Markus had already initiated the work for the SMOOTH autoScale algorithm and bulk of the changes are already in the master via bug 493462.
- SMOOTH autoScale algorithm doesn't give expected result on Windows and the specific limitations are mentioned in below gerrit patch, see the FIXME part:
https://git.eclipse.org/r/#/c/70415/5/bundles/org.eclipse.swt/Eclipse+SWT/common/org/eclipse/swt/internal/DPIUtil.java

Part2 of the issue, is regarding API change:
Once we have sorted the issues with SMOOTH autoScale algorithm.. we can take a call on how to expose it as an API in SWT.
[Note: ImageData.scaledTo() method is dedicated to nearest-neighbor scaling, so we will have to figure out a new API]
Comment 16 Lakshmi P Shanmugam CLA 2018-02-09 06:02:35 EST
(In reply to Niraj Modi from comment #15)
> Summarizing this issue Part1:
> - Markus had already initiated the work for the SMOOTH autoScale algorithm
> and bulk of the changes are already in the master via bug 493462.
> - SMOOTH autoScale algorithm doesn't give expected result on Windows and the
> specific limitations are mentioned in below gerrit patch, see the FIXME part:
> https://git.eclipse.org/r/#/c/70415/5/bundles/org.eclipse.swt/Eclipse+SWT/common/org/eclipse/swt/internal/DPIUtil.java
> 
Adding to Niraj's points: The SMOOTH scaling works on Linux/GTK. We need the SMOOTH scaling to work on Windows as well to provide a better scaling of Images at fractional scale factors such as 125% and 150%. Currently, SWT by default rounds-off all scale factors < 175% to 100%.
Comment 17 Conrad Groth CLA 2018-02-11 13:41:27 EST
I don't have a clue on this bug, but I investigated bug 493455, which is the cause for the SMOOTH case in the DPIUtil not working for Windows. I found two problems, but only a solution for one.
Comment 18 Lakshmi P Shanmugam CLA 2018-02-12 00:33:51 EST
(In reply to Conrad Groth from comment #17)
> I don't have a clue on this bug, but I investigated bug 493455, which is the
> cause for the SMOOTH case in the DPIUtil not working for Windows. I found
> two problems, but only a solution for one.

Thanks Conrad!
Comment 19 Conrad Groth CLA 2018-03-03 08:26:33 EST
(In reply to Sravan Kumar Lakkimsetti from comment #6)
> May be we can use gdk_pixbuf_scale_simple api for scaling on gtk and
> stretchBlt api for windows. I used gdk_pixbuf_scale_simple with
> GDK_INTERP_BILENEAR. the image quality is pretty good.
> ...

The windows StretchBlt function is already used in SWT for scaling. Unfortunately this function uses nearest-neighbor scaling.
Comment 20 Conrad Groth CLA 2018-03-03 08:33:52 EST
Created attachment 272986 [details]
The output of the SnippedScaling.java together with some AWT experiments
Comment 21 Conrad Groth CLA 2018-03-03 08:44:17 EST
I tried to scale the images by AWT, as AWT offers biliniar and bicubic scaling. The output can be see in the attachment 272986 [details] together with the output of the original SnippetScaling.java (attachment 259133 [details]). I used the conversion methods provided by Snippet156 to convert SWT images to AWT images and vice versa.
The first problem is that the transparency gets lost.
Also the scaled images look very similar to the antialiased ones from SWT.
I will not continue working on this issue. I just wanted to present my results.
Comment 22 Conrad Groth CLA 2018-03-03 08:46:08 EST
Created attachment 272988 [details]
The SnippetScaling enhanced by AWT scaling
Comment 23 Stanimir Stamenkov CLA 2020-09-28 13:41:02 EDT
Created attachment 284302 [details]
Icon scaling demo

    java -jar icons-demo.jar

IconsDemo.java source included in the JAR.
Comment 24 Stanimir Stamenkov CLA 2020-09-28 13:42:15 EDT
Created attachment 284303 [details]
Icons demo at 125% screen
Comment 25 Stanimir Stamenkov CLA 2020-09-28 13:42:41 EDT
Created attachment 284304 [details]
Icons demo at 150% screen
Comment 26 Stanimir Stamenkov CLA 2020-09-28 13:43:12 EDT
Created attachment 284305 [details]
Icons demo at 200% screen
Comment 27 Stanimir Stamenkov CLA 2020-09-28 13:49:02 EDT
I see two distinct issues to this:

1. Icon scaling strategy

Given there's a higher resolution variant available, it is always better to downscale from it to the target resolution.

Try icons-demo.jar (attachment #284302 [details]) and compare some results:

-   125% (attachment #284303 [details])
-   150% (attachment #284304 [details])
-   200% (attachment #284305 [details])

2. General image (up)scaling

Have you considered pixel-art scaling algorithms [1]?  These usually have the constraint of scaling only to an integral factor but then I think using a smooth downscaling algorithm with a sharp pixel-art upscaled image produces quite satisfying results.

So far I've found xBRZ [2] superior to others for its general results and for its alpha transparency handling.  I've made a Java port [3] of it but I don't know if its licensing is suitable for direct use in Eclipse.  Maybe if one could produce a formal specificaion of the algorithm it would alow for a clean-room implementations which might use different than GPL license.  Otherwise see some of its results:

1x: https://raw.githubusercontent.com/stanio/xbrz-java/master/src/test/resources/net/sourceforge/xbrz/test/gbamockup.png
6x: https://raw.githubusercontent.com/stanio/xbrz-java/master/src/test/resources/net/sourceforge/xbrz/test/gbamockup%406xbrz.png

1x: https://raw.githubusercontent.com/stanio/xbrz-java/master/src/test/resources/net/sourceforge/xbrz/test/open-folder.png
6x: https://raw.githubusercontent.com/stanio/xbrz-java/master/src/test/resources/net/sourceforge/xbrz/test/open-folder%406xbrz.png

[1] https://en.wikipedia.org/wiki/Pixel-art_scaling_algorithms
[2] https://sourceforge.net/projects/xbrz/
[3] https://github.com/stanio/xbrz-java
Comment 28 Stanimir Stamenkov CLA 2022-04-26 05:37:34 EDT
Created attachment 288472 [details]
Toolbar icons – Scaling factors and methods

> 1. Icon scaling strategy
> 
> Given there's a higher resolution variant available, it is always better to 
> downscale from it to the target resolution.

Now that Bug 493455 is available in Eclipse 4.24M1, here's a visualization of current results – Eclipse upscaling the low-res variants compared to downscaling the hi-res variants (200%).  As far as I can tell the latter produces better results even when using nearest-neighbor interpolation.