Java Font Rendering - java

I have been attempting to enhance my GUI system written in Java to use subpixel antialiasing and have been successful, except for two remaining anomalies. This is a follow on to my other question from a few weeks ago.
The first 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.
The second problem is that I can find no way to query the underlying O/S setting for AA, so I have to do a rather kludgey workaround, which is to create a Swing JLabel, get it's FontMetrics, get it's FontRenderContext and then get the AA hint from that. Apart from involving Swing in a program that otherwise makes absolutely no use of Swing, it will not work on a device running any J2ME JVM. Can anyone suggest a better way to do this? It's OK if it requires J5 or J6, since the current kludge already requires J6 (but needing only J4 would be best). I have already tried every default setting and using an AWT component instead of JLabel.
Test Program
This program verifies that for subpixel AA to work, the general AA setting must first be disabled. (PS: I write to a back-buffer because my underlying GUI does, and I wanted to test in an equivalent context).
import java.awt.*;
import java.awt.event.*;
public class AwtTestFrame1b extends Panel {
private final Font font=new Font(Font.SANS_SERIF, Font.PLAIN, 16);
private final int line=25;
AwtTestFrame1b() {
setBackground(SystemColor.control);
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
int py=0;
py=paintText(g2d,py,null ,false);
py=paintText(g2d,py,null ,true );
py+=line;
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF ,false);
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT ,false);
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_ON ,false);
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_GASP ,false);
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,false);
py+=line;
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF ,true );
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT ,true );
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_ON ,true );
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_GASP ,true );
py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,true );
py+=line;
}
private int paintText(Graphics2D g2d, int py, Object val, boolean aa) {
Graphics2D dgc=g2d;
char[] txt=("The quick brown fox jumped over the lazy dog ("+val+", General AA: "+aa+")").toCharArray();
Image img=null;
GraphicsConfiguration cfg=getGraphicsConfiguration();
img=cfg.createCompatibleImage(getWidth(),line);
dgc=(Graphics2D)img.getGraphics();
dgc.setColor(getBackground());
dgc.fillRect(0,0,getWidth(),line);
dgc.setColor(g2d.getColor());
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.drawChars(txt,0,txt.length,10,line-5);
g2d.drawImage(img, 0,py, null);
dgc.dispose();
img.flush();
return (py+line);
}
public static 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 AwtTestFrame1b());
wnd.setSize(new Dimension(1000, 600));
wnd.setVisible(true);
}
}

Are the AWT Desktop Properties of any help? In particular, "awt.font.desktophints" - these contain the AA hints that the native components use, but can be applied to any Graphics2D you want.
Just a shot in the dark, having recently read through the AA section in Filthy Rich Clients.
Use would look something like this:
String str = "A quick brown fox jumps over the lazy dog";
Toolkit tk = Toolkit.getDefaultToolkit();
Map desktopHints = (Map)(tk.getDesktopProperty("awt.font.desktophints"));
Graphics2D g2d = (Graphics2D)g;
if(desktopHints != null) {
g2d.addRenderingHints(desktopHints);
}
g2d.drawString(str, someX, someY);
I was able to get the same results (using your example class and drawChars and drawImage, just typed drawString for simplicity) as the LCD HRGB mode using these hints and no other calls on my machine.
I'm not sure what release of Java this requires, if it's what you're looking for...

Don't forget: "Implementations are free to ignore the hints completely." For reference, here's what I see at 24 points:

Related

Java: How to avoid code duplication between display() and write(pdf)

I have two functions within a class
void display()
and
void write(PGraphics pdf)
I use display() to display elements to the screen. Is there a way of invoking the code within display() so that I do not have to write out each of the functions again within write(pdf)? e.g.
line(0,0,100,100) to pdf.line(0,0,100,100) etc
You could do this by drawing to a PGraphics image instead of drawing directly to the screen, then draw that image to the screen. That way you could swap it out for the PDF PGraphics without changing any code. Something like this:
PGraphics pg;
PGraphics pdf;
boolean usePdf = false;
void setup() {
size(100, 100);
pg = createGraphics(width, height);
pdf = //whatever
}
void draw(){
if(usePdf){
display(pdf);
}
else{
display(pg);
image(pg, 0, 0);
}
}
void display(PGraphics g) {
g.beginDraw();
g.background(100);
g.stroke(255);
g.line(20, 20, mouseX, mouseY);
g.endDraw();
}
If you want to use all of the code jsut call it inside (but I guess you don't want to do that)
You should refractor the code and put the stuff you want to use in both methods into it's own method like:
private void hopefullyIGetABetterName(...) {...}
You can call this method inside of display as well as in write
I hope this helps, otherwise feel free to ask :-)
extract a common interface between drawing to screen and to the PDF (lets call it Drawable)
Implement Drawable for drawing to the screen.
Implement Drawable for drawing to the PDF
Create a single draw method that takes that interface.
Change display() to call draw(screenDrawable)
Change write(...) to call draw(pdfDrawable)

Why isn't anything being drawn to my JPanel?

Since I'm new I can't post more than two links, but this is an x-post from reddit.com/r/learnprogramming, just for full disclosure.
I'll basically just be pasting what I said there to here. Thanks for your help, if you can help.
I'm writing somewhat of a graphing application. I currently only have it able to graph sin(x), but that's not the point of this question. I am not able to draw to my main panel. Here is what it currently looks like.
I had an overridden paint function in my Window.java class, which drew the sin(x) function and the axes, but when I made an inner class which extended JPanel(), it would no longer draw.
I then tried to make a separate file, but that didn't draw anything either.
What could be preventing it from drawing?
Here are all my files in question.
edit: code in question:
GraphDraw.java:
//import stuff
Public class GraphDraw extends JPanel {
SinX sinx = new SinX();
GraphPanel p = new GraphPanel();
#Override
public void paintComponent(Graphics gc) {
super.paintComponent(gc);
Graphics2D g = gc;
p.paintComponent(g);
sinx.paint(g);
}
}
And in Window.java, I initialize GraphDraw and add it to my main panel, which is underneath the buttons in the picture and above the x/y min/max labels.
GraphDraw drawer = new GraphDraw();
/*
GUI code
*/
mainPanel.add(drawer);
SinX.java
//import stuff
public class SinX extends Component {
public void paint(Graphics g) {
g.setColor(Color.red);
for(double x=-400;x<=400;x=x+0.5) {
double y = 50 * sin(x*((Math.PI)/180));
int Y = (int)y;
int X = (int)x;
g.drawLine(400+X,300-Y,400+X,300-Y);
}
}
}
First, before anything else, do the following:
Change you object from Component to JComponent
Do not ever, ever call paintComponent() or paint() on a graphics object from swing or awt, use object.repaint(); (For reasons I won't go into here, because it's long and complicated)
From there I would try calling setVisible(true); on all your objects. If you are getting this code from a tutorial, then stop and use a different tutorial. You need to learn how swing and the AWT library work before you can start making user interfaces. Nobody uses AWT anymore because Swing is much better. For reasons why, look at the following page. If you are too lazy to do that, its because it's more optimized and more powerful.
What is the difference between Swing and AWT?

Embed processing 3 into swing

I'm trying to integrate Processing 3 into a swing application, but because PApplet doesn't extend Applet anymore I can't just add it as a component right away.
Is there anyway of embeding a Processing 3 sketch into Swing, it would be enough if I could just open the sketch in a seperate window without the PDE.
You can run a sketch from Java by extending PApplet and then using the runSketch() function to run that PApplet. It'll look something like this:
String[] args = {"MyPapplet "};
MyPapplet mp = new MyPapplet ();
PApplet.runSketch(args, mp);
public class MyPapplet extends PApplet {
public void settings() {
size(200, 100);
}
public void draw() {
background(255);
fill(0);
ellipse(100, 50, 10, 10);
}
}
Then if you want to get at the underlying component, you have to write code that depends on which renderer you're using. Here's how you'd do it with the standard renderer:
PSurfaceAWT awtSurface = (PSurfaceAWT)mp.surface;
PSurfaceAWT.SmoothCanvas smoothCanvas = (PSurfaceAWT.SmoothCanvas)awtSurface.getNative();
Once you have the SmoothCanvas, you can remove it from its frame and add it to yours.

Update a BufferedImage in a JFrame

I have a BufferedImage displayed in a JFrame through my own class. I opted to display the BufferedImage using my own class so I can scale it. My paintComponent and update
public class MyBuffIm{
public void paintComponent(Graphics canvas) {
if (bi == null) {
} else {
//bi, maxWidth, and maxHeight were passed to constructor
canvas.drawImage(bi, 0, 0, maxWidth, maxHeight, null);
}
}
public void update(Graphics canvas) {
super.update(canvas);
if(bi != null){
//Got this from some tutorial in the net.
//Done out of desperation :|
paintComponent(bi.getGraphics());
}
}
}
I overrode update since the docs are saying something like "If this component is not a lightweight component, the AWT calls the update method in response to a call to repaint". I'm not exactly sure if my component is lightweight or not.
In any case, I have the following code in my Runnable (does not work as I expect it to):
BufferedImage p = SomeMyBuffIm.getBuffIm();
Vector<Point> randomPixels = getRandomPixels(500);
int limit = randomPixels.size()
for (i = 0; i < limit; i++) {
Point rp = randomPixels.get(i)
p.setRGB(rp.x, rp.y, Color.red.getRGB());
}
SomeMyBuffIm.repaint();
mainFrame.repaint(); //JFrame call to repaint
I'd like to think that, since I'm scaling my image, I just can't discern the difference between the new and old images. But I've tried the largest values for getRandomPixels still to no effect. My test image, by the way, is just a white sheet so red pixels should stand out in it.
Anything wrong I'm doing?
I overrode update since the docs are saying something like "If this component is not a lightweight component, the AWT calls the update method in response to a call to repaint". I'm not exactly sure if my component is lightweight or not.
No you should NOT override update(). You would do that with AWT but not with Swing.
If you update the BufferedImage then all you need to do is invoke repaint() on your instance of the MyBuffin class.
If you need more help than post your SSCCE that demonstrates the problem.

Scrollable JDesktopPane?

I'd like to add scrolling capability to a javax.swing.JDesktopPane. But wrapping in a javax.swing.JScrollPane does not produce the desired behavior.
Searching the web shows that this has been an issue for quite some time. There are some solutions out there, but they seem to be pretty old, and I'm not not completely satisfied with them.
What actively maintained solutions do you know?
I've used JavaWorld's solution by creating my own JScrollableDesktopPane.
Javaworld's JScrollableDesktopPane is no longer available on their website. I managed to scrounge up some copies of it but none of them work.
A simple solution I've derived can be achieved doing something like the following. It's not the prettiest but it certainly works better than the default behavior.
public class Window extends Frame {
JScrollPane scrollContainer = new JScrollPane();
JDesktopPane mainWorkingPane = new JDesktopPane();
public Window() {
scrollContainer.setViewportView(mainWorkingPane);
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent evt) {
revalidateDesktopPane();
}
});
}
private void revalidateDesktopPane() {
Dimension dim = new Dimension(0,0);
Component[] com = mainWorkingPane.getComponents();
for (int i=0 ; i<com.length ; i++) {
int w = (int) dim.getWidth()+com[i].getWidth();
int h = (int) dim.getHeight()+com[i].getHeight();
dim.setSize(new Dimension(w,h));
}
mainWorkingPane.setPreferredSize(dim);
mainWorkingPane.revalidate();
revalidate();
repaint();
}
}
The idea being to wrap JDesktopPane in a JScrollPane, add a resize listener on the main Frame and then evaluate the contents of the JDesktopPane on resize (or adding new elements).
Hope this helps someone out there.
I've found this : http://www.javaworld.com/javaworld/jw-11-2001/jw-1130-jscroll.html?page=1
It's a nice tutorial with lots of explanations and infos on Swing & so, which permits to create a JscrollableDesktopPane with lots of stuff.
You will need to modify a bit some parts of code to fulfill your requirements.
Enjoy !

Categories