mgarin / weblaf

WebLaF is a fully open-source Look & Feel and component library written in pure Java for cross-platform desktop Swing applications.
http://weblookandfeel.com
GNU General Public License v3.0
1.15k stars 235 forks source link

Text rendering issues on non-opaque destination #130

Open FisherYu-CN opened 10 years ago

FisherYu-CN commented 10 years ago

Hi mgarin,

I do enjoy weblaf, it's really amazing. But I found some problems when I try to use it together with JDK8 in Windows 7. It seems the menu items render differently when they are in different menus. Please look at the screen shot below.

untitled

This problem becomes more obvious when I change the default font.

untitled

I haven't done anything particular to initialize those menu items.

menuBar = new JMenuBar();
this.setJMenuBar(menuBar);

settingsMenu = new JMenu("Menu1");
menuBar.add(settingsMenu);

sourceSettingMenuItem = new JMenuItem("MenuItem1");
settingsMenu.add(sourceSettingMenuItem);

basicSettingMenuItem = new JMenuItem("MenuItem2");
settingsMenu.add(basicSettingMenuItem);

helpMenu = new JMenu("Menu2");
menuBar.add(helpMenu);

aboutHuntersMenuItem = new JMenuItem("MenuItem1");
helpMenu.add(aboutHuntersMenuItem);

Is it a bug or there's something else I need to do? Thanks.

mgarin commented 10 years ago

Not sure whether this is painting a bug or just some font-related rendering issue. In any case that looks pretty weird, I will investigate it.

Thanks for reporting :)

mgarin commented 10 years ago

I got an idea of what causes this bug - most probably when 1st menu is displayed - it gets outside of the frame bounds and, obviously, rendered on a separate window. Menu styling forces its window to become background-transparent and render all the text in the frame using Java base AA which causes the effect you see in the first menu.

The second menu still in the window bounds and rendered without AA which makes font better in most cases and more system-like.

Unfortunately i cannot fix this issue anyhow since this is a JDK-related bug (or behavior) which is not going to be fixed anytime soon as it seems (it is there for a loooong time).

FisherYu-CN commented 10 years ago

Yes, I guess it makes sense. Thanks for your effort. Good job, best laf I have ever seen :)

mgarin commented 10 years ago

Here is a similar older bug with more explanations and examples, by the way: https://github.com/mgarin/weblaf/issues/84

I'll close this issue as well. If some day there will be a way to fix this rendering issue i will surely do it.

mgarin commented 10 years ago

By the way, as a workaround - menu shadows can be reduced so that popup menu fits into window bounds and rendered normally. But that will help only while menu stays in frame bounds, otherwise you will see font rendering issue coming back.

bobbylight commented 10 years ago

@mgarin, Is there a way to disable drop shadows in the LAF globally? I know it's not nearly as cool without them, but with window decorations enabled (#84 like you referenced), all text rendering in the entire UI is done with Java AA, since the main window has a drop shadow. Since I think your window decorations are cool, a fair trade-off for me would be to have no drop shadow for windows or menus, and square window edges (if necessary), in exchange for sharper text rendering and cooler-looking window chrome.

On Windows at least, where the current UI is typically flat, a lack of shadows isn't as noticeable. :)

Wasn't sure if this should be a new ticket or just a question for this one; please move it if necessary. I can also look into creating a pull request for this feature if you like the idea but don't have time to look into it.

mgarin commented 10 years ago

You can disable shades and use squared shapes, but windows will still be transparent.

Disabling transparent windows might be a good option overall considering some bugs (including font rendering) it causes. Though it will require some additional modifications. It is better to move it into a separate issue.

mgarin commented 8 years ago

I'll keep this issue opened as the oldest one for reference. If something changes regarding subpixel rendering on non-opaque windows I will make sure to post an update here.

weisJ commented 4 years ago

I have experience the same issue in Darklaf. To mitigate it one can paint the text to an opaque off-screen image and then paint the image to the component graphics. This way subpixel AA is enabled for the text rendering.

See PaintUtil#drawStringImpl.

mgarin commented 4 years ago

I can't really do that when the destination of the text is semi-transparent (which is the case with menus for instance), so it's not a solution I can use unfortunately. It might be fine for cases when text is clearly drawn on top of an opaque element, but unfortunately due to the way all parts of each component are drawn in WebLaF - I have no reasonable way to find out whether text is drawn on an opaque or non-opaque destination.

weisJ commented 4 years ago

I see. If you are willing to use reflection then the following approach will work even with transparent surfaces. It’s not a 100% perfect as the area under the text is still a bit more opaque than the rest, but it’s barely noticeable.

https://gist.github.com/weisJ/4cf624c149819f42a6c5ad75407901b5

e.g. here is a tooltip with a gradient paint and transparency: tooltip_aa

mgarin commented 4 years ago

Thats... an interesting solution. Thanks for the code example, I'll look into that!

My main and only concern with it is performance, especially when a lot of text is drawn on a window (let's say you're using a custom window decoration for the main app window) - that will require some testing and benchmarking.

And reflection is not an issue, WebLaF uses lots of reflection for accessing various Swing stuff and not only that, so that wouldn't really change anything.

weisJ commented 4 years ago

Upon further testing I found that with a dark background the area under the text is still quite visible. The background color might need to be manually adjusted to mitigate the impact.

weisJ commented 4 years ago

I have updated the gist with an approach that doesn't preserve the background. When drawing the off-screen image to the graphics object we ignore all pixels that have the background color. For all other pixels we calculate the channel alpha values to reconstruct the anti-aliased color values now with the current graphics image as the destination surface.

Performance wise this could probably be a bit improved by converting the FilterImageSource to a BuffereImageOp.

It might still be feasible to calculate a background based on the foreground color to ensure it has maximal contrast. tooltip_aatooltip_aa_dark

Note If the the graphics object has SurfaceData of type CGLSurfaceData then the resulting text will be gray scaled anti-aliased. (This could be circumvented by first painting the graphics offscreen image to a BufferedImage so it matches the expected type).

mgarin commented 4 years ago

Thanks for the update!

It might still be feasible to calculate a background based on the foreground color to ensure it has maximal contrast.

It will only work in case background color retrieval can be automated and performed within the text painting code, otherwise it will be very hard to determine exact background code in many cases, so this is something I'll have to investigate once I get to it.

Also one more thing I just realized - a separate solution will be required for the JTextComponents as this only covers plain/html text rendering. Theoretically the actual painting part can be provided separately into the utility, but I haven't really fully looked into it yet, so not sure about that.

weisJ commented 4 years ago

Thanks for the update!

It might still be feasible to calculate a background based on the foreground color to ensure it has maximal contrast.

It will only work in case background color retrieval can be automated and performed within the text painting code, otherwise it will be very hard to determine exact background code in many cases, so this is something I'll have to investigate once I get to it.

I have already incorporated background color generation in the gist.

Also one more thing I just realized - a separate solution will be required for the JTextComponents as this only covers plain/html text rendering. Theoretically the actual painting part can be provided separately into the utility, but I haven't really fully looked into it yet, so not sure about that.

I haven't checked yet whether it would work for JTextComponent. I'll try it out later.

mgarin commented 4 years ago

I have already incorporated background color generation in the gist.

Yep, noticed it now, was mostly just glancing over the code :)

I haven't checked yet whether it would work for JTextComponent. I'll try it out later.

It should work, unless there are some issues with custom non-text elements - although I doubt it since it works with HTML view that has a lot of custom stuff.

mgarin commented 4 years ago

I'll probably try it out next week on the simple text implementation (in AbstractTextContent.java) to see how it performs on demo app and maybe one of our large proprietary apps and will post results here.

weisJ commented 4 years ago

I have made some corrections to accommodate components that are part of a more complex hierarchy (previously the text rectangle was assumed to be relative to the parent window).