-
Bug
-
Resolution: Duplicate
-
P4
-
None
-
1.2.2
-
x86
-
windows_98
Name: sl110371 Date: 06/28/2000
java version "1.2.2"
Classic VM (build JDK-1.2.2-001, native threads, symcjit)
--- AND ALSO ---
java version "1.3.0rc2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0rc2-Y)
Java HotSpot(TM) Client VM (build 1.3.0rc2-Y, mixed mode)
I am a highly experienced software engineer (30 years) and I have developed
portions of shipping products in Java for the past three years. The recent
availability of Netscape 6 Preview Release 1 motivated me to re-test my Java
applets under Java 2. I encountered a severe problem, so I downloaded the "pure
Sun" Java 2 SDK v1.3 (RC 2) and also Java 2 SDK v1.2.2 (production). I was able
to reproduce the problem consistently under "appletviewer" in BOTH 1.2.2 and 1.3
(as well as Netscape 6 PR1, which ships with the pre-release 1.3 JRE).
This problem does NOT occur under JDK v1.1 (I have been using 1.1.5 for several
years). It also does NOT occur under Netscape Communicator (through version
4.7) or Microsoft Internet Explorer (through version 5.01).
The problem is that whenever I redraw a character cell in the applet's "update"
method, I get "random" results -- most of the time, it draws okay, but about 10%
of the time I get a "blank cell". The typical code sequence is:
g.setColor(Color.white);
g.fillRect(x, y, cell_width, cell_height);
g.setColor(Color.black);
g.drawChars(array, 0, 1, x, y + cell_base);
What appears to be happening is that the "fillRect" method is being executed
asynchronously, under a separate thread -- which may or may not finish before
the "drawChars" is executed! The result is that about 10% of the time, on
random character cells, the "fillRect" is performed AFTER the "drawChars" and
"wipes out" the character that was just drawn. (This also happens if
"clearRect" is used instead of "fillRect" and/or if "drawString" is used instead
of "drawChars".)
I was able to confirm this theory by commenting out the call to the "fillRect"
method. In that case, it works correctly 100% of the time under Java 2.
Unfortunately, that is not a workable solution because susequent updates to the
same character cell are then written on top of the earlier character, creating a
big mess!
It is also possible to "work around" the problem by eliminating the use of the
"update" method and doing all of the text drawing in the applet's "paint"
method, without using any calls to "fillRect" (relying instead on the fact that
the "paint" method always clears the entire background). That trick works okay
for the simple test case I am including here, but it will not work for the
shipping applets that I produced over the last few years -- because many of them
need to fill the background of the cell with colors that are DIFFERENT from the
applet's normal background color. (And that trick has negative performance
implications as well.)
The test case is a simple applet that draws all 256 pages of a Unicode font, one
page at a time. The keys to control this applet are not shown "on screen".
They are as follows:
right arrow = next Unicode page
left arrow = previous Unicode page
up arrow = larger font
down arrow = smaller font
scroll lock = toggles between "Courier" and "Monospaced" fonts
Following are the HTML file and the Java source code necessary to reproduce this
problem:
<HTML>
<HEAD>
<TITLE>Font Test</TITLE>
</HEAD>
<BODY>
<CENTER>
<H3>Font Test</H3>
</CENTER>
<CENTER>
<APPLET CODE="fonts.class" WIDTH=728 HEIGHT=466>
</APPLET>
</CENTER>
</BODY>
</HTML>
import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.*;
public class fonts extends Applet implements FocusListener, KeyListener
{
Font screen_font;
int width, height;
int cell_width, cell_height, cell_base;
int left_margin, top_margin;
int x, y;
int font_test_page, font_test_size = 16; boolean font_test_type;
boolean have_focus, want_focus;
boolean focus_when_stopped = true;
public void init()
{
//
javax.swing.RepaintManager.currentManager(this).setDoubleBufferingEnabled(false)
;
setBackground(Color.white);
width = getSize().width;
height = getSize().height;
cell_width = width/32;
left_margin = (width - 32*cell_width)/2;
cell_height = height/18;
top_margin = (height - 18*cell_height)/2;
this.addFocusListener(this);
this.addKeyListener(this);
}
public void start()
{
// System.out.println("Applet started");
want_focus = focus_when_stopped;
}
public void stop()
{
// System.out.println("Applet stopped");
focus_when_stopped = have_focus;
}
public void paint(Graphics g)
{
if (screen_font == null)
{
int sf_cell_width = width/48;
int fs = 13;
screen_font = new Font("Courier", Font.PLAIN, 13);
int cw = g.getFontMetrics(screen_font).charWidth('0');
if (cw < sf_cell_width)
{
for (fs = 14; fs <= 96; fs++)
{
screen_font = new Font("Courier", Font.PLAIN, fs);
cw = g.getFontMetrics(screen_font).charWidth('0');
if (cw == sf_cell_width)
break;
else if (cw > sf_cell_width)
{
screen_font = new Font("Courier", Font.PLAIN, fs - 1);
cw = g.getFontMetrics(screen_font).charWidth('0');
break;
}
}
}
else if (cw > sf_cell_width)
{
for (fs = 12; fs >= 1; fs--)
{
screen_font = new Font("Courier", Font.PLAIN, fs);
cw = g.getFontMetrics(screen_font).charWidth('0');
if (cw <= sf_cell_width)
break;
}
}
int ds = g.getFontMetrics(screen_font).getDescent();
cell_base = cell_height - 1 - ds;
// System.out.println("width: " + width + "; height: " + height
// + "; sf_cell_width: " + sf_cell_width + "; cell_height: " +
cell_height
// + "; font size: " + fs + "; actual cell width: " + cw
// + "; descent: " + ds);
// System.out.println("screen_font: " + screen_font);
}
if (want_focus)
{
want_focus = false;
requestFocus();
}
font_test(g);
}
public void update(Graphics g)
{
font_test(g);
}
void font_test(Graphics g)
{
//
javax.swing.RepaintManager.currentManager(this).setDoubleBufferingEnabled(false)
;
g.setFont(screen_font);
y = top_margin;
g.setColor(Color.white);
g.fillRect(left_margin, y, 32*cell_width, 2*cell_height);
g.setColor(Color.black);
g.drawString(" Unicode Page " + font_test_page
+ (font_test_type ? " [Monospaced" : " [Courier")
+ ", size " + font_test_size + "]",
left_margin, y + cell_base + 8);
y += 2*cell_height;
// Font test_font = new Font("ZapfDingbats", Font.PLAIN, font_test_size);
Font test_font = new Font(font_test_type ? "Monospaced" : "Courier",
Font.PLAIN, font_test_size);
if (test_font == null)
{
System.out.println("Test font is null!");
System.exit(0);
}
else
{
// int fw = g.getFontMetrics(test_font).charWidth('0');
// System.out.println("test_font: " + test_font + "; cell width: " + fw);
g.setFont(test_font);
char[] array = new char[1];
for (int i = 0; i < 16; i++)
{
x = left_margin;
for (int j = 0; j < 16; j++)
{
g.setColor(Color.white);
g.fillRect(x, y, 2*cell_width, cell_height);
g.setColor(Color.black);
array[0] = (char)(256*font_test_page + 16*j + i);
g.drawChars(array, 0, 1, x + cell_width/2, y + cell_base + 2);
x += 2*cell_width;
}
y += cell_height;
}
}
}
public void focusGained(FocusEvent e)
{
// System.out.println("Focus gained: " + e);
have_focus = true;
}
public void focusLost(FocusEvent e)
{
// System.out.println("Focus lost: " + e);
have_focus = false;
}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
switch (key)
{
case KeyEvent.VK_SHIFT:
case KeyEvent.VK_CONTROL:
case KeyEvent.VK_ALT:
return;
}
int modifiers = e.getModifiers();
switch (key)
{
case KeyEvent.VK_UP:
if (++font_test_size == 49)
font_test_size = 1;
repaint();
break;
case KeyEvent.VK_DOWN:
if (--font_test_size == 0)
font_test_size = 48;
repaint();
break;
case KeyEvent.VK_LEFT:
if (--font_test_page == -1)
font_test_page = 255;
repaint();
break;
case KeyEvent.VK_RIGHT:
if (++font_test_page == 256)
font_test_page = 0;
repaint();
break;
case KeyEvent.VK_SCROLL_LOCK:
font_test_type ^= true;
repaint();
break;
default:
break;
}
}
public void keyTyped(KeyEvent e)
{
}
public void keyReleased(KeyEvent e)
{
}
}
(Review ID: 103420)
======================================================================
- duplicates
-
JDK-4337823 fillRect has severe synchronization bug in Java 2
-
- Resolved
-