I have found that while rendering opaque text in Java (latest version 6u23) uses sub-pixel AA just fine, rendering translucent text does not.
Sub-pixel AA:
The same text for which only the color has been changed from 0xFFFFFFFF to 0xBFFFFFFF:
As you can see, the translucent text is clearly standard AA and rather than a clean translucent rendering, it has that awful '90s "spidery" appearance.
Is this due to a technical limitation for sub-pixel AA in general, or a bug in Java, or just because Java doesn't even try for translucent text, or have I missed something?
Graphics Initialization
dbGraphics=(Graphics2D)dbImage.getGraphics();
if(dctRoot.properties.getBoolean("Antialias",true)) {
try {
Map hnts=(Map)(dctRoot.awtComponent.getToolkit().getDesktopProperty("awt.font.desktophints"));
// SET AA ON OVERALL (NOTE: GENERAL AA MUST BE OFF FOR SUBPIXEL AA TO BE HONORED - TEXT WIDGETS MUST DO THIS THEMSELVES)
dbGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
if(hnts!=null) {
// SET FONT RENDERING HINTS FROM DESKTOP
dbGraphics.addRenderingHints(hnts);
}
else {
try {
// SET TEXT AA TO FONT-SPECIFIED GASP AA (JAVA 6+)
dbGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_GASP").get(null));
}
catch(Throwable thr3) {
// SET TEXT AA TO DEFAULT
dbGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
}
}
catch(Throwable thr) {
dctRoot.log.println("Antialiasing not supported on this JVM ("+thr+").");
dctRoot.setProperty("Antialias","False"); // turn off AA for subsequent painting
}
}
else {
try {
dbGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
dbGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
}
catch(Throwable thr) {;} // ignore exception
}
Text Rendering
Object oaa=disableGeneralAA(gc);
...
gc.drawString(tl,xx,(ty+(xa*met.getHeight())));
restoreGeneralAA(gc,oaa);
...
static private volatile boolean hasRenderingHints=true;
// *****************************************************************************
// STATIC INIT & MAIN
// *****************************************************************************
// *****************************************************************************
// STATIC METHODS
// *****************************************************************************
/**
* Disable the general anti-aliasing rendering hint, returning whether the old value was RenderingHints.VALUE_ANTIALIAS_ON.
* <p>
* This method is needed for text rendering due to a bug in AWT; as of Java 6_20 when general AA is on text is not rendered using subpixel
* AA, so general AA has to be turned off before rendering text and turned back on when done. This method abstracts that work and deals
* with the possibility that the JVM does not support rendering hints, such as is the case with JME JVMs.
*/
static public Object disableGeneralAA(Graphics2D gc) {
Object old=null;
if(hasRenderingHints) {
try {
old=gc.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
}
catch(NoClassDefFoundError thr) { hasRenderingHints=false; }
catch(NoSuchFieldError thr) { hasRenderingHints=false; }
catch(NoSuchMethodError thr) { hasRenderingHints=false; }
}
return old;
}
/**
* Disable the general anti-aliasing rendering hint, returning whether the old value was RenderingHints.VALUE_ANTIALIAS_ON.
* <p>
* This method is needed for text rendering due to a bug in AWT; as of Java 6_20 when general AA is on text is not rendered using subpixel
* AA, so general AA has to be turned off before rendering text and turned back on when done. This method abstracts that work and deals
* with the possibility that the JVM does not support rendering hints, such as is the case with JME JVMs.
*/
static public void restoreGeneralAA(Graphics2D gc, Object val) {
Object old=null;
if(hasRenderingHints && val!=null) {
try { gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,val); }
catch(NoClassDefFoundError thr) { hasRenderingHints=false; }
catch(NoSuchFieldError thr) { hasRenderingHints=false; }
catch(NoSuchMethodError thr) { hasRenderingHints=false; }
}
}
It seems rendering text to a transparant background is not (fully) supported, as can be seen from these bugreports:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6728834
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6749060
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6749069
I think its because your using GASP which takes the points from the font style. have you tried using VALUE_TEXT_ANTIALIAS_DEFAULT and VALUE_ALPHA_INTERPOLATION_DEFAULT? it's worth a shot.
http://download.oracle.com/javase/6/docs/api/java/awt/RenderingHints.html
What Java version are you using? You don´t say. But apparently this has been fixed as of either Java 6 update 12 (J6u12) or JDK7 b43
See here:
http://bugs.sun.com/view_bug.do?bug_id=6749060
If your test again with Java = or higher than J6u12 and still see the bug, there´s an RFE where you can comment on at Sun´s bug database.
The way to get things fixed in the Java platform, is to either:
Vote on the bug report at Sun´s BugParade, to increase its priority, and wait until the Sun/Oracle programmers get to it
or
Now that Java is open source, fix it yourself. (join the mailing list at ho.io/jkp5 :-)
The Bugparade report you want to vote or comment in is here (in case you test with j6u12 an it´s still present) is
url: ho.io/jkp2
If you want to implement a known work-around so the text looks nice even in older JREs a work-around is provided here
url: ho.io/jkpy
"Looks like the solution that i'm using to emulate the translucency by mixing the required foreground color with the background color of the component is still the way to go to make sure that the native rasterizer is used."
Good luck!
Related
all! Asking for your help.
I'm pretty new to SWT - so please do not judge me strictly for questions.
I have a big/old RCP app. It has many views that use embedded browser.
Recently made 2 major updates:
Updated Eclipse platform from 3.7.2 to 4.21;
switch from IE to Edge browser;
The package that I'm using has version:
org.eclipse.swt.win32.win32.x86_64
Bundle-Version: 3.117.0.v20210906-0842
OS: Windows 10
Problem:
After clicking the button on embedded browser page, JavaScript calls the Java MyFunction extends BrowserFunction. Which prepares the data to refresh the page. And before refresh it calls browser.evaluate() to get some number from JS side:
Double numberFromJS = (Double) browser.evaluate("return 2+2; //this expression just for example")
The main thread stuck in the while loop, because pstr[0] == null always true.
package org.eclipse.swt.browser;
class Edge extends WebBrowser {
...
static int callAndWait(String[] pstr, ToIntFunction<IUnknown> callable) {
int[] phr = new int[1];
IUnknown completion = newCallback((result, pszJson) -> {
phr[0] = (int)result;
if ((int)result == COM.S_OK) {
pstr[0] = wstrToString(pszJson, false);
}
return COM.S_OK;
});
pstr[0] = null;
phr[0] = callable.applyAsInt(completion);
completion.Release();
Display display = Display.getCurrent();
while (phr[0] == COM.S_OK && pstr[0] == null) { // <-------------- here
if (!display.readAndDispatch()) display.sleep();
}
return phr[0];
}
}
investigation:
Here https://www.eclipse.org/swt/faq.php I found a limitation about Edge and evaluate, but there is no Exception throwed;
Tried to debug the display.readAndDispatch(), but there is lack of java docs and it jumps into native methods - not clear for me what should exactly happen.
As I understood in general it should handle message from OS and pass it into completion callback from above and set some value into pstr[0];
interesting observation - the evaluate() method for this browser is successfully passed just on it creation. Looks like it became broken after something.
Questions:
Any suggestion to pay attention/check some places?
Is there known issues for that method and workarounds for it?
Is it possible that the messages from system are intercepted from some other place?
Thanks in advance!
I would like to use baked AO on an obj-model in Aframe. Don Mccurdy explains
that AO needs a 2nd UV channel and how to solve this in Java here:
https://github.com/aframevr/aframe/issues/2721
I tried it but I don`t get it to work!
var geometry = mesh.geometry;
geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
How do I point this js-lines to my obj-model in Aframe?
Thanks a lot for help, appreciate! Best, can
Ideally, I'd suggest opening the OBJ file in Blender, adding the Ambient Occlusion texture as described in the Blender docs, then exporting to glTF. The rest will be handled automatically with A-Frame's gltf-model component, and will load more quickly.
If converting to another format isn't an option, you'll need to write a custom component that listens for the model to load, then traverses every mesh in the model (there might be more than one!) and creates an extra UV set:
AFRAME.registerComponent('copy-uvs', {
init: function () {
this.el.addEventListener('model-loaded', function (e) {
e.detail.model.traverse(function(object) {
if (object.isMesh && object.geometry.attributes.uv) {
var geometry = object.geometry;
geometry.setAttribute('uv2', geometry.attributes.uv);
console.log('copied UVs!');
}
});
});
}
});
This copy-uvs component would need to be attached to the same element as your OBJ model.
How would I play whichever sound the user has set for exclamation when I display JOptionPane.WARNING_MESSAGE or the error sound when I display JOptionPane.ERROR_MESSAGE, for example?
My assumption - nothing special required to do, JOptionPane just does it - was based on skimming BasicOptionPaneUI code and checking if the optionPane's audioActionMap is installed.
The place where the audio is played is in the ui's propertyChangeListener on a change to its ancestor property:
if ("ancestor" == e.getPropertyName()) {
JOptionPane op = (JOptionPane)e.getSource();
boolean isComingUp;
// if the old value is null, then the JOptionPane is being
// created since it didn't previously have an ancestor.
if (e.getOldValue() == null) {
isComingUp = true;
} else {
isComingUp = false;
}
// figure out what to do based on the message type
switch (op.getMessageType()) {
case JOptionPane.PLAIN_MESSAGE:
if (isComingUp) {
BasicLookAndFeel.playSound(optionPane,
"OptionPane.informationSound");
}
break;
// all other message types handled as well
}
the shared actionMap is installed (lazyly, so an optionPane must have been visible once)
assertTrue(UIManager.get("AuditoryCues.actionMap") instanceof ActionMap);
ActionMap map = (ActionMap) UIManager.get("AuditoryCues.actionMap");
assertNotNull(map.get("OptionPane.errorSound"));
sounds enabled on OS (win 7) level and sound on hardware turned on (just for testing) ... WTF: but nothing happens (and assumption proven to be wrong ;-)
Debug session (I hate it ... but occasionally ...) turns out that performing the audioAction doesn't happen, here are the methods involved :
static void playSound(JComponent c, Object actionKey) {
LookAndFeel laf = UIManager.getLookAndFeel();
if (laf instanceof BasicLookAndFeel) {
ActionMap map = c.getActionMap();
if (map != null) {
Action audioAction = map.get(actionKey);
if (audioAction != null) {
// pass off firing the Action to a utility method
// JW: we have an audioAction, so on to the next method
((BasicLookAndFeel)laf).playSound(audioAction);
}
}
}
}
protected void playSound(Action audioAction) {
if (audioAction != null) {
Object[] audioStrings = (Object[])
UIManager.get("AuditoryCues.playList");
if (audioStrings != null) {
// JW: here the action is performed ... except we don't reach this
....
}
}
That's rather astonishing, isn't it? After all, the action were created, so if there is no playlist, why would they have been created?
And here comes the catch: the list used for creating the actions is a different list
// in BasicLookAndFeel
protected ActionMap getAudioActionMap() {
ActionMap audioActionMap = (ActionMap)UIManager.get(
"AuditoryCues.actionMap");
if (audioActionMap == null) {
// here it's named cueList
Object[] acList = (Object[])UIManager.get("AuditoryCues.cueList");
}
and the reason that's a different list is ... to allow LAFs to customize the sounds that actually are to be played
// BasicLookAndFeel
// *** Auditory Feedback
"AuditoryCues.cueList", allAuditoryCues,
// this key defines which of the various cues to render.
// L&Fs that want auditory feedback NEED to override playList.
"AuditoryCues.playList", null,
Ooookaaayy .. so let's see what a concrete LAF is doing, f.i. Win:
// *** Auditory Feedback
// this key defines which of the various cues to render
// Overridden from BasicL&F. This L&F should play all sounds
// all the time. The infrastructure decides what to play.
// This is disabled until sound bugs can be resolved.
"AuditoryCues.playList", null, // table.get("AuditoryCues.cueList"),
EOL.
Not quite :-) This comment hints to what is doable:
Object[] cueList = (Object[]) UIManager.get("AuditoryCues.cueList");
UIManager.put("AuditoryCues.playList", cueList);
Which in fact does work for WindowsLAF (even respecting the OS sound schema and - most importantly - not playing if disabled), but not for any of the other core LAFs.
Firstly, I agree with Andrew
However, take a look here then here
Ps I've not tested this myself
Using the links MadProgrammer provided (reposted at the end) as a starting point, here's what I figured out:
import java.awt.*;
import javax.swing.JOptionPane;
//retrieve the default sound from windows system sounds
//for another sound replace "default" accordingly
final Runnable SOUND = (Runnable)Toolkit.getDefaultToolkit().getDesktopProperty
("win.sound.default");
and then just before displaying the JOptionPane:
if(SOUND != null)SOUND.run();
NB Some sound events like Program Error cannot be accessed this way. A list of accessible sound events is available under the audio-feedback heading on the Windows Desktop Property Support page from Oracle
While this will not work at all on a non-windows o/s, it will not, according to the blog, cause the program to crash on another o/s. I don't have a JDK for my Linux partition yet, ergo I am currently unable to test this.
(source: google.com)
Recently, I realize the Chinese Character displayed are rather ugly in my application.
I thought I should make them to "anti-alias". But, how can I do that in Java?
FYI, I didn't explicitly choose the font I want to use in my GUI application. I solely let the system decide their own during startup. I however, do explicitly set the locale, before show up the GUI.
Locale.setDefault(locale);
The system will always choose
javax.swing.plaf.FontUIResource[family=Tahoma,name=Tahoma,style=plain,size=11]
no matter I am in English or Chinese locale.
Anti-aliasing considered harmful: http://www.joelonsoftware.com/articles/fog0000000041.html
The point is, that beauty of characters is not necessarily the user interface goal. It is not everything. What you should look for, is readability of text. When your Chinese characters look not smooth, it may be exactly what helps human eye's control loop to know that it is in focus and stop blaming the eye muscules for blurriness. Really, don't fall in this pitfal.
Here's a method to read a truetype font from the classpath and register it with the graphics environment:
private static Font readFont(String name) {
InputStream in = Fonts.class.getResourceAsStream(name + ".ttf");
if (in == null) {
throw new IllegalArgumentException(name);
}
try {
Font retval = Font.createFont(Font.TRUETYPE_FONT, in);
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(retval);
return retval;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
You can then use this font object to derive characters of different sizes, or you could try applying this font using Swing CSS. In this case, the value you would put in the "font-family" attribute is the value returned by Font.getName().
For example:
static {
Font font = readFont("VeraMono");
if (font != null) {
font = font.deriveFont(14f);
} else {
throw new IllegalStateException();
}
MONOSPACED_TEXT_FONT = font;
MONOSPACED_TEXT_FONT_STYLE = "font-family: " + font.getName() + "; font-size: 14pt; font-weight: normal;";
}
I have been attempting to enhance my GUI system written in Java to use subpixel antialiasing and have been successful, except for one remaining anomaly. This is a follow on to my other question from yesterday.
The remaining problem is that setting rendering hints KEY_ANTIALIASING to VALUE_ANTIALIAS_ON causes KEY_TEXT_ANTIALIASING to be ignored when it is set to an LCD (subpixel) AA value. Can anyone shed some light on this? Currently I am forced to VALUE_ANTIALIAS_OFF before rendering text and turn it back on after rendering text (so that other painting, like circles, etc, is AA'd).
This problem is proven by the self-contained test program below. As you can see if you run it, the circle is painted with AA when the font isn't, and vice versa. It would be nice to have AA preconfigured to work for all painting.
Self Contained Test Program
import java.awt.*;
import java.awt.event.*;
public class AwtTestFrame1c extends Panel {
AwtTestFrame1c() {
setBackground(SystemColor.control);
}
public void paint(Graphics gc) {
Graphics2D g2d = (Graphics2D)gc;
int py=0;
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,true );
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,false);
}
private int paintText(Graphics2D dgc, int py, Object val, boolean aa) {
char[] txt=("The quick brown fox jumped over the lazy dog ("+val+", General AA: "+aa+")").toCharArray();
if(aa ) { dgc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON ); }
else { dgc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF); }
if(val !=null) { dgc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,val); }
dgc.setFont(font);
dgc.drawOval(5,py+5,15,15);
dgc.drawChars(txt,0,txt.length,30,py+line-5);
return (py+line);
}
static private final Font font=new Font("SansSerif",Font.PLAIN,16);
static private final int line=25;
static public void main(String[] args) {
Frame wnd=new Frame("AWT Antialiased Text Sample");
wnd.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
wnd.add(new AwtTestFrame1c());
wnd.setSize(new Dimension(1000, 300));
wnd.setVisible(true);
}
}
I was updating VirtualBox, so I took pictures. I may just be seeing the host's rendering, but I suspect it is also implementation dependent.
Ubuntu 9.10
Mac OS X 10.5
Windows 7
A bit of poking around found this: bug 6263951.
Apparently the bustage is fixed in b17? I'm not exactly sure how to interpret the bug report.
Definitely broken here on 1.6.0_17-b04 (MacOS X 10.5).