Skip to content

Conversation

@ShahzaibIbrahim
Copy link
Contributor

@ShahzaibIbrahim ShahzaibIbrahim commented Oct 10, 2025

Description

Move Transparent Pixel Handling from Image to ImageHandle

Previously, the transparency pixel was managed at the Image level, which caused issues when an Image had multiple handles at different zoom levels or color models (e.g., indexed vs. direct/ARGB). For example, a GIF image at 100% zoom may use a transparency pixel, but when scaled, SWT converts it to a direct image with alpha data, making the transparency pixel concept invalid for the scaled handle.

This change moves the transparency pixel field from the Image class to the ImageHandle class. Now, each handle manages its own transparency information, allowing a single Image to have multiple handles, each with the correct transparency type (pixel or alpha) according to its color model. This ensures correct transparency handling for all handles, regardless of how they were created or scaled.

How to test

  1. Run the following snippet on 100% zoom monitor
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;


public class ButtonWithGifImage {
    public static void main(String[] args) {
    	System.setProperty("swt.autoScale.updateOnRuntime", "true");
    	System.setProperty("swt.autoScale", "quarter");
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("SWT Button with GIF Image");
        shell.setSize(300, 200);

        Button button = new Button(shell, SWT.PUSH);
        button.setBounds(80, 60, 140, 60);
        button.setText("Click Me");

        Image image = new Image(display,
            ButtonWithGifImage.class.getResourceAsStream("sample.gif"));
        button.setImage(image);

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }

        image.dispose();
        display.dispose();
    }
}

  1. Move the window to a 150% DPI monitor (causing scaling) — verify the image still renders correctly (no jagged/incorrect background).
  2. Move back to 100% — verify the image still renders correctly and no gray background appears.

GIF Image:
sample

Result

Here's the preview how it looked before and after this PR change. 100 -> 150 -> 100

Before:
image

After
image

Fixes: #2494

@merks
Copy link
Contributor

merks commented Oct 10, 2025

Yeah! 🥳

@github-actions
Copy link
Contributor

github-actions bot commented Oct 10, 2025

Test Results

  115 files  + 7    115 suites  +7   11m 34s ⏱️ -17s
4 562 tests +56  4 546 ✅ +54  16 💤 +3  0 ❌  - 1 
  311 runs  +56    308 ✅ +53   3 💤 +3  0 ❌ ±0 

Results for commit b36999d. ± Comparison against base commit b78efbf.

♻️ This comment has been updated with latest results.

@ShahzaibIbrahim ShahzaibIbrahim marked this pull request as draft October 10, 2025 15:01
@ShahzaibIbrahim ShahzaibIbrahim force-pushed the master-442 branch 2 times, most recently from 4340fa2 to 08fa953 Compare October 15, 2025 13:51
@ShahzaibIbrahim ShahzaibIbrahim marked this pull request as ready for review October 15, 2025 14:07
Copy link
Contributor

@HeikoKlare HeikoKlare left a comment

Choose a reason for hiding this comment

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

As we already discussed, moving transparentPixel/transparentColor to the individual handle makes sense. However, there now seem to be some flaws on how to retrieve the transparentPixel at Image level, where arbitrary handles or image data are used for that. Would it maybe make sense to additionally store whether the image was (originally) based on a transparent pixel to have this information available at image level, no matter whether some rescaling operation produced other handles that do not use the transparent pixel anymore? Or maybe store a reference to the according image handle, such that getBackgroundColor() can use the correct handle that is based on the transparent color?

Copy link
Contributor

@HeikoKlare HeikoKlare left a comment

Choose a reason for hiding this comment

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

I think that we agree that the transparentPixelbelongs to the handle and not to the image, so this is generally the right thing to do. @ShahzaibIbrahim can you please extract the preparatory refactoring to change the return type ofgetHandle()and use that one instead ofwin32_getHandle()` into a separate preparatory PR, such that this one becomes easier to review?
I left some additional comments regarding the functional changes in this PR.

if (transparentPixel == -1) return;
transparentColor = -1;
backgroundColor = color.getRGB();
zoomLevelToImageHandle.values().forEach(imageHandle -> imageHandle.setBackground(backgroundColor));
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 completely remove this? In case there are already existing handles, shouldn't we ...

  • apply the color if possible?
  • print a warning that it cannot be applied if the existing handles are scaled versions (maybe encapsulated in a strict check to avoid performance impacts)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I get the first part, we should apply color if possible. But why print a warning at all? If they are scaled version the setBackground method will do the early exit anyway after checking for transparentPixel != -1. Isn't that so?

Copy link
Contributor

Choose a reason for hiding this comment

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

But why print a warning at all? If they are scaled version the setBackground method will do the early exit anyway after checking for transparentPixel != -1. Isn't that so?

Yes, and that's potentially unexpected behavior. My comment was bit imprecise. The scenario I refer to is the following: you have an image whose handle at 100% is created with a transparent pixel. So if you create that image, set the background and then request handles (also for scaled version), they will all have the background applied. If you create such an image, then request multiple handles (including scaled version) and then set the background, that will only affect the 100% handle but not the others (because they have no transparent pixel that). That's unexpected.

But maybe we even have another here, because the image at 100% would need to be drawn to apply all the login in GC#drawBitmap() before creating a handle for a scaled version?

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair point, but as we currently even get it to properly work on a correct 100% image we need to revise the Image#setBackground logic overall anyway, if we wanna do something about it. So I would consider this a different topic, so I discussed with @ShahzaibIbrahim to raise a separate issue about this and write all his findings down to be able to decide if and how to proceed with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have added a follow-up ticket for the following issues, see: vi-eclipse/Eclipse-Platform#495

@ShahzaibIbrahim ShahzaibIbrahim force-pushed the master-442 branch 3 times, most recently from a72c61e to e384db6 Compare October 20, 2025 09:51
@ShahzaibIbrahim
Copy link
Contributor Author

@ShahzaibIbrahim can you please extract the preparatory refactoring to change the return type ofgetHandle()and use that one instead ofwin32_getHandle()

#2651

Copy link
Contributor

@HeikoKlare HeikoKlare left a comment

Choose a reason for hiding this comment

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

Just to not forget about it, here a comment from the extracted refactoring PR (#2651 (review)) that we should address in this PR:

As a follow up in #2615, we can get rid of the Image parameters in some GC methods, as they were only passed for the transparentPixel/transparentColor fields, which are then properly part of the ImageHandle.

@ShahzaibIbrahim
Copy link
Contributor Author

Just to not forget about it, here a comment from the extracted refactoring PR (#2651 (review)) that we should address in this PR:

As a follow up in #2615, we can get rid of the Image parameters in some GC methods, as they were only passed for the transparentPixel/transparentColor fields, which are then properly part of the ImageHandle.

There was only one method that was passing Image solely for transparentpixel/color. I have refactored it.

@ShahzaibIbrahim ShahzaibIbrahim marked this pull request as ready for review October 21, 2025 09:02
Copy link
Contributor

@HeikoKlare HeikoKlare left a comment

Choose a reason for hiding this comment

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

There was only one method that was passing Image solely for transparentpixel/color. I have refactored it.

The drawBitmap method also takes an Image in addition to an ImageHandle, now only for the sake of accessing its memGC. I don't even think the logic accessing the memGC is correctly placed in that method but should probably be moved to the caller. However, that's a follow-up task.

I have one comment on an introduced handle leak, which is why I place a change request.

if (transparentPixel == -1) return;
transparentColor = -1;
backgroundColor = color.getRGB();
zoomLevelToImageHandle.values().forEach(imageHandle -> imageHandle.setBackground(backgroundColor));
Copy link
Contributor

Choose a reason for hiding this comment

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

But why print a warning at all? If they are scaled version the setBackground method will do the early exit anyway after checking for transparentPixel != -1. Isn't that so?

Yes, and that's potentially unexpected behavior. My comment was bit imprecise. The scenario I refer to is the following: you have an image whose handle at 100% is created with a transparent pixel. So if you create that image, set the background and then request handles (also for scaled version), they will all have the background applied. If you create such an image, then request multiple handles (including scaled version) and then set the background, that will only affect the 100% handle but not the others (because they have no transparent pixel that). That's unexpected.

But maybe we even have another here, because the image at 100% would need to be drawn to apply all the login in GC#drawBitmap() before creating a handle for a scaled version?

@ShahzaibIbrahim ShahzaibIbrahim force-pushed the master-442 branch 2 times, most recently from 6cbe087 to 863413d Compare October 21, 2025 14:58
@HeikoKlare HeikoKlare dismissed their stale review October 21, 2025 15:32

I with draw my request for changes, as the problematic code was adapted. I have not re-reviewed yet and will not be able to do so in the next days. Please note that my latest comment is still unanswered and should be considered before merging this: #2615 (comment)

@akoch-yatta akoch-yatta force-pushed the master-442 branch 3 times, most recently from 47da655 to b039823 Compare October 23, 2025 08:23
Copy link
Contributor

@akoch-yatta akoch-yatta left a comment

Choose a reason for hiding this comment

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

This code changes look solid to me now. I did a small adaption by adding a new constructor to ImageHandle with transparentPixel to better differentiate the use cases
I tested with the Snippet and the IDE and did not find any regression

Previously, the transparency pixel was managed at the Image level, which
caused issues when an Image had multiple handles at different zoom
levels or color models (e.g., indexed vs. direct/ARGB). For example, a
GIF image at 100% zoom may use a transparency pixel, but when scaled,
SWT converts it to a direct image with alpha data, making the
transparency pixel concept invalid for the scaled handle.

This change moves the transparency pixel field from the Image class to
the ImageHandle class. Now, each handle manages its own transparency
information, allowing a single Image to have multiple handles, each with
the correct transparency type (pixel or alpha) according to its color
model. This ensures correct transparency handling for all handles,
regardless of how they were created or scaled.

Contributes to eclipse-platform#2494
@ShahzaibIbrahim
Copy link
Contributor Author

I did a small adaption by adding a new constructor to ImageHandle with transparentPixel to better differentiate the use cases.

I did the same before I got this comment from heiko, #2615 (comment)

@akoch-yatta akoch-yatta merged commit 6779ddc into eclipse-platform:master Oct 23, 2025
17 checks passed
@akoch-yatta akoch-yatta deleted the master-442 branch October 23, 2025 10:49
@akoch-yatta
Copy link
Contributor

I did a small adaption by adding a new constructor to ImageHandle with transparentPixel to better differentiate the use cases.

I did the same before I got this comment from heiko, #2615 (comment)

Ah okay, I didn't notice that. We need to adapt this in a separate PR now

@ShahzaibIbrahim
Copy link
Contributor Author

Ah okay, I didn't notice that. We need to adapt this in a separate PR now

Separate PR -> #2663

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.

Eclipse 2025-06: UI rendering issues on Windows with monitors using different scale factors Gif images sometimes have black backgrounds

4 participants