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

Lightweight transparent popups over Canvas painting issues #340

Open mgarin opened 9 years ago

mgarin commented 9 years ago

It is highly possible that this happens on any Canvas and Canvas-like elements. Here is an example screenshot of the issue: Issue example

parkjohnston commented 9 years ago

happens on a mac as well, but with a white border

On Nov 7, 2015, at 6:13 PM, Mikle notifications@github.com wrote:

It is highly possible that this happens on any Canvas and Canvas-like elements. Here is an example screenshot of the issue: https://camo.githubusercontent.com/4c9d80cd5833b249525e9f0bfe6c3bf0acafb485/687474703a2f2f692e696d6775722e636f6d2f7377704953766b2e706e67 — Reply to this email directly or view it on GitHub https://github.com/mgarin/weblaf/issues/340.

CotterPin-Source commented 9 years ago

happens on a mac as well, but with a white border

This is my screen shot. The pink colour is just the background of the Panel that the canvas is housed in.

mgarin commented 9 years ago

happens on a mac as well, but with a white border

That is probably a different issue with menu itself which shouldn't already occur on pre-release build. This specific issue is about Canvas-like elements painting behind a semi-transparent components.

CotterPin-Source commented 9 years ago

Just noticed the font changes in the screen shots too. It only looks nice and crips in the center picture and then blocky and bold in the two outside shots.

mgarin commented 9 years ago

The font change was already explained multiple times in other issues - generally its a JDK bug with translucent windows. When some window (popup window in this case) is translucent - font rendering seems to be done by completely different set of tools for unknown reason. That seem to be hidden somewhere deep in the Graphics implementation and there is no way to affect it.

RobertBColton commented 9 years ago

@mgarin Perhaps that may have something to do with antialiasing? Could try SwingUtilities2.paintString

mgarin commented 9 years ago

@RobertBColton Obviously it is a AA problem as every painted string is using it, without you will see ugly pixeled stuff. The problem is that usually you see native AA in action, but on translucent windows Graphics decides not to use it for some weird reason which is hidden deep within its implementation.

RobertBColton commented 9 years ago

@mgarin Yeah it looks like you are right, and it's not just with windows. It seems to also be a problem with drawing to transparent bitmaps. From the following Stack Overflow it looks as though there is a fix in the JDK 8 pool for JavaFX applications. http://stackoverflow.com/questions/7992281/java2d-swing-rendering-a-component-with-text-anti-aliasing-to-a-bufferedimage

Top posters suggestion is a possible work around, you could render/cache the text to the menu item's to a buffered image and then draw that instead. That's likely to end up pretty convoluted unless maybe you create string painting utilities (if you don't already have a replacement for SwingUtilites2) where you can just make it abstract. Just a possibility.

mgarin commented 9 years ago

Top posters suggestion is a possible work around, you could render/cache the text to the menu item's to a buffered image and then draw that instead. That's likely to end up pretty convoluted unless maybe you create string painting utilities (if you don't already have a replacement for SwingUtilites2) where you can just make it abstract. Just a possibility.

Damn, this is actually a really good idea! I will have a single painting pipe for all the stuff soon, so having an image as a middle-man is not an issue, will just take slightly more memory. And this is certainly not an issue if it will fix this old text rendering bug.

Thanks for linking this SO post :)

RobertBColton commented 9 years ago

Oh that's great then, no problem, I am used to working around these kind of limitations. Glad I could help!

Sciss commented 9 years ago

I don't know if having an image in the middle is really no issue. It will interfere for example if you export a window that uses a PDF-graphics2D (iTextPDF for instance) because now you will paste a bitmap image and don't get pristine vector graphics. Also you will want to be careful to allocate a larger image size when a high-density screen (such as Retina) is used.

mgarin commented 9 years ago

@Sciss

I don't know if having an image in the middle is really no issue. It will interfere for example if you export a window that uses a PDF-graphics2D (iTextPDF for instance) because now you will paste a bitmap image and don't get pristine vector graphics.

I'm not really sure how that would be working, but I am pretty sure this case (with "a window that uses a PDF-graphics2D") can simply be excluded from the buffered painting logic. Based on the provided graphics instance for example.

Also you will want to be careful to allocate a larger image size when a high-density screen (such as Retina) is used.

I didn't really say how this tricky thing is going to work ;) I can ensure you that application won't suddenly eat another 100-200 MB out of nowhere. It is more like 1-2 MB of actual RAM will be used once for the small buffer image and only if it is actually needed (translucent window case).

RobertBColton commented 9 years ago

I'd be a fan of doing it this way.

Instead of caching each individual piece of text, just create a single buffer image that is the largest needed. So like the size of the longest string. Then when it comes time to paint the text, clear the part of the buffer image needed and then paint it with clipping. Just keep reusing that same buffer image.

This avoids the large memory consumption of caching each piece of text's/control's buffer image. And it also avoids having to recreate the graphics devices multiple times if you didn't save at least one of the buffer images. And it still solves the antialasing problem. And like @mgarin said, the biggest this buffer image would ever be is the size of your display resolution (AT A MAXIMUM) so it would never be more than a 1-2 MB RAM take. In fact, I just saved a 1080p 32bit BMP image from PDN on my computer, and it was 2.04 MB's of disk space.

mgarin commented 9 years ago

Instead of caching each individual piece of text, just create a single buffer image that is the largest needed. So like the size of the longest string. Then when it comes time to paint the text, clear the part of the buffer image needed and then paint it with clipping. Just keep reusing that same buffer image.

This is exactly how it should be done.

the biggest this buffer image would ever be is the size of your display resolution (AT A MAXIMUM) so it would never be more than a 1-2 MB RAM take. In fact, I just saved a 1080p 32bit BMP image from PDN on my computer, and it was 2.04 MB's of disk space.

The actual size of the image in memory might be completely different from the size of that image on hard drive as it is converted into a different data format which is optimal performance-wise but not optimal memory-wise. I would probably never use 1080p-size image as it will actually eat a lot of RAM, but I don't really need image of that size either.


I have checked topics on the problem once more and there might still be no way to fix it as the issue is generally related to the text painting on translucent images. So I can't really paint text with transparent background either way. Here is, by the way, the JDK issue with "Won't Fix" resolution on this: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6749069

Though I can provide some fixes depending on actual element transparency. That is what I will do based on the painters for specific components.

RobertBColton commented 9 years ago

Yeah I see what you mean, and exactly I meant as a maximum, that's the biggest any control would ever be.

You could still do it that way and paint the text on a filled background with a polar opposite color. Then remove the color, but clearly not going to do that, really suboptimal. Would work if it was done with a shader though, but that's not possible and not really feasible even if you could. http://stackoverflow.com/questions/7405955/making-a-certain-color-on-a-bufferedimage-become-transparent

mgarin commented 5 years ago

It's been a while since we discussed this issue, but I actually have been testing painting text on an intermediate image and... it doesn't really work the way I imagined. There seem to be quite a few technical limitations in place I wasn't aware of.

First of all painting AA-d text onto the image doesn't seem to consistently work across different JDKs & OS versions. Also image buffer approach might create some unwanted visual artifacts depending on the background color as it is kind of a hack. And generally it is still not really possible to paint AA-d text onto transparent background properly.

So I'm afraid we will have to wait for some JDK solution and simply make sure that all popup menus land inside the frame to have consistent text rendering for now.