[REPOST] java.awt.Image problem

M

MaSTeR

The problem is quite straighforward:
I am double buffering an image which represents an horizontal table, as you
might expect this could get really wide. If the table changes (row deleted
or added) I redraw the buffer otherwise I only draw the image that's in the
buffer.

I do my development in J# using visual studio 2003 because I like the IDE
and the debugger is just the best. Then (since I'm not using .Net api) I
just recompile my code with the SUN sdk. So far so good. The problem is that
the image is drawn perfectly using the microsoft VM but when I run it with
SUN's the image is created correctly (on row deletion, insertion) but when
just redrawing the buffer it gets screwed up.

Here's an example : http://217.158.134.37/MobileJava/ticker.jpg
At the top the right one.

Ah the 'thing' is sitting inside a class deriving from Java.awt.Panel and I
just use awt. It works fine if I use it inside an applet in a browser that
run microsoft VM 1.1. It goes funny anyway with sun VM ( 1.4.2 ).

Thanks for any help.
 
B

bugbear

MaSTeR said:
The problem is quite straighforward:
I am double buffering an image which represents an horizontal table, as you
might expect this could get really wide. If the table changes (row deleted
or added) I redraw the buffer otherwise I only draw the image that's in the
buffer.


Looks like you've got some kind of race condition between
altering your buffer, and copying from your buffer
to the user's view of stuff.

If it *is* a race, wether you get your desired (as opposed
to "correct") behaviour is just a fluke of timing, and
could depend on ... virtually anything.

I'd check your events and threads carefully to be sure
you're using all the API's correctly.

BugBear
 
M

MaSTeR

bugbear said:
Looks like you've got some kind of race condition between
altering your buffer, and copying from your buffer
to the user's view of stuff.

If it *is* a race, wether you get your desired (as opposed
to "correct") behaviour is just a fluke of timing, and
could depend on ... virtually anything.

I'd check your events and threads carefully to be sure
you're using all the API's correctly.

BugBear

Than's what I thought at first, but I ensure you there are no threading
issues. Nevertheless it works perfectly on any given VM execept SUN's.
Plus, it goes funny only when the number of element is greater than a
threshold and hence the image gets wider. If I set it up to show like 10
elements the problem doesn't appear. I will triple check my code for racing
conditions, but I've kinda lost the hope and will start thinking about a
kludge (like an array of smaller images).

Thanks for your reply BugBear.
 
B

bugbear

MaSTeR said:
Than's what I thought at first, but I ensure you there are no threading
issues.
^^^^^^^
assure?

In Swing, I was under the impression that pretty much everything
is multi-threaded. In particular, the paint() method (and its friends)
are called by the event dispatcher thread, which isn't your "main" thread.

If your "main" thread is messing with data while your "paint()" is accessing
the data, you're dead meat!

BugBear
BugBear
 
M

MaSTeR

assure?
Aren't ensure and assure synonymous ?
In Swing, I was under the impression that pretty much everything
is multi-threaded. In particular, the paint() method (and its friends)
are called by the event dispatcher thread, which isn't your "main" thread.
You are correct. repaint() generates another thread, you could use
paint(this.getGraphics()) if you want the current thread to repaint.
If your "main" thread is messing with data while your "paint()" is accessing
the data, you're dead meat!

BugBear

I am so confident because, I repaint everything just when an updates comes
in, otherwise the paint() method terminates just after redrawing the buffer.
Now when I push in a new update the Ticker paints correctly (!) but when I
move the double buffer back and forth I get my problem.
 
B

Babu Kalakrishnan

MaSTeR said:
Aren't ensure and assure synonymous ?



You are correct. repaint() generates another thread, you could use
paint(this.getGraphics()) if you want the current thread to repaint.




I am so confident because, I repaint everything just when an updates comes
in, otherwise the paint() method terminates just after redrawing the buffer.
Now when I push in a new update the Ticker paints correctly (!) but when I
move the double buffer back and forth I get my problem.

The best way to get someone to spot your error (or for you yourselves to
realize what you're doing wrong) is to post a short code sample that
exhibits the problem. Redisplaying from a double buffer does work - even
on the Sun VM - and if it isn't, it is most certainly something wrong
that you're doing in the code. (To take a wild guess - it could be that
your drawImage call passes a null as the ImageObserver ?).

BK
 
M

MaSTeR

The best way to get someone to spot your error (or for you yourselves to
realize what you're doing wrong) is to post a short code sample that
exhibits the problem. Redisplaying from a double buffer does work - even
on the Sun VM - and if it isn't, it is most certainly something wrong
that you're doing in the code. (To take a wild guess - it could be that
your drawImage call passes a null as the ImageObserver ?).

BK

Right, unfortunately the code is almost 3 thousands line, I'll try to
summarize it.
/*
* Forces repaint of the ticker
*/
public void paint(Graphics g)
{
if (offScreenImage == null)
offScreenImage = createImage(this.getBounds().width, this.getBounds().height
+ rowHeight);
// I need a template to paint something
if (template == null)
return;
slideLeft(g); // THIS JUST REDRAW THE BUFFER
}

private void slideLeft(Graphics g)
{
int drawAtPosition = drawHorizontalAt+distance+scrollingOffset;
g.drawImage(offScreenImage, drawAtPosition, 0, this);
}

When a new record comes in:

synchronized(this)
{
if (effectInProgress)
{
killPaintingThread = true;
try
{
wait();
}
catch(InterruptedException e)
{
System.out.println("Couldn't wait on the painting thread.");
}
}
repainter = new Thread(this);
repainter.setPriority(Thread.MIN_PRIORITY);
repainter.setName("Painter");
// Notify the object that a new element is inserted
newElementInserted = true;
killPaintingThread = false;
paintHorizontal(getGraphics(), true);
repainter.start();
}

Pretty much I repaint the buffer and redisplay or start a thread to scroll
it (to simulate insertion)

As you see I am calling drawImage with this as observer.

Thanks for the help !
 
B

Babu Kalakrishnan

MaSTeR said:
Right, unfortunately the code is almost 3 thousands line, I'll try to
summarize it.
/*
* Forces repaint of the ticker
*/
public void paint(Graphics g)
{
if (offScreenImage == null)
offScreenImage = createImage(this.getBounds().width, this.getBounds().height
+ rowHeight);
// I need a template to paint something
if (template == null)
return;
slideLeft(g); // THIS JUST REDRAW THE BUFFER
}

private void slideLeft(Graphics g)
{
int drawAtPosition = drawHorizontalAt+distance+scrollingOffset;
g.drawImage(offScreenImage, drawAtPosition, 0, this);
}

When a new record comes in:

synchronized(this)
{
if (effectInProgress)
{
killPaintingThread = true;
try
{
wait();
}
catch(InterruptedException e)
{
System.out.println("Couldn't wait on the painting thread.");
}
}
repainter = new Thread(this);
repainter.setPriority(Thread.MIN_PRIORITY);
repainter.setName("Painter");
// Notify the object that a new element is inserted
newElementInserted = true;
killPaintingThread = false;
paintHorizontal(getGraphics(), true);
repainter.start();
}

Pretty much I repaint the buffer and redisplay or start a thread to scroll
it (to simulate insertion)

As you see I am calling drawImage with this as observer.

What does paintHorizontal(Graphics, boolean) do ?
It looks like it isn't being run from the EDT - so see if you're doing
any unsafe operations in that method.

BK
 
M

MaSTeR

What does paintHorizontal(Graphics, boolean) do ?
It looks like it isn't being run from the EDT - so see if you're doing
any unsafe operations in that method.

BK

It does repaint the buffer. Now if there are several threads accessing
without sync the buffer but only one writing would it matter? Even if the
buffer it's being written during a display operation a refresh lateron
should redisplay it correctly...

Am I wrong ?
 
B

Babu Kalakrishnan

MaSTeR said:
It does repaint the buffer. Now if there are several threads accessing
without sync the buffer but only one writing would it matter? Even if the
buffer it's being written during a display operation a refresh lateron
should redisplay it correctly...

Yes - as long as a refresh does take place. But if you're triggering a
refresh only when a change has taken place, it may never be repainted.

Also I assume you're also ensuring that the paint() method gets called
from the update() method. (i.e. if you're overriding update).

BK
 
M

MaSTeR

It does repaint the buffer. Now if there are several threads accessing
Yes - as long as a refresh does take place. But if you're triggering a
refresh only when a change has taken place, it may never be repainted.
It does, the ticker can be moved back and forth and that forces redrawing
the buffer. Also iconizing it and refocusing it will repaint.
Also I assume you're also ensuring that the paint() method gets called
from the update() method. (i.e. if you're overriding update).
I do, the super class implementation clears the screen, causing flickering
and it was unnecessary since my ticker repaints the whole area.

Again I am stuck I can't get rid of this annoying bug.

Anyway, thanks a lot for your time
 
B

Babu Kalakrishnan

MaSTeR said:
It does, the ticker can be moved back and forth and that forces redrawing
the buffer. Also iconizing it and refocusing it will repaint.

I think I lost you there. Do you mean to say that the display looks
damaged even when you force a refresh by doing the above ?? I'd expect
the display to become trashed as soon as it is updated from the other
thread, and continue to be that way till something else (like the
iconizing / restoring) forces it to refresh itself (at which point it
should display properly). If this is the observed behaviour, that isn't
very surprising at all. Proper synchronization should fix the issue.
I do, the super class implementation clears the screen, causing flickering
and it was unnecessary since my ticker repaints the whole area.

This means that the way you're performing double buffering is broken.
The clearing of the screen is exactly what you *avoid* by using double
buffering.

Of course there is a limit to what one can speculate without seeing the
relevant code.

BK
 
M

MaSTeR

I think I lost you there. Do you mean to say that the display looks
damaged even when you force a refresh by doing the above ?? I'd expect
the display to become trashed as soon as it is updated from the other
thread, and continue to be that way till something else (like the
iconizing / restoring) forces it to refresh itself (at which point it
should display properly). If this is the observed behaviour, that isn't
very surprising at all. Proper synchronization should fix the issue.
That's exactly what I would expect from a sync issue. Unfortunately it
behaves the opposite way (!). When I redraw the buffer from scratch it
paints correctly and as soon as I refresh it (by scrolling it manually or
forcing repainting in any ither way) it gets corrupted. Also I tried some
extreme synchronization measure like sync the whole methods and didn't work,
it just slowed down insertion of new records.

This means that the way you're performing double buffering is broken.
The clearing of the screen is exactly what you *avoid* by using double
buffering.
Not sure about this last point, I do avoid clearing of the screen by
overriding update in this way:

// Overrides this method to prevent the superclass to clear the screen at
each repaint.

public void update(Graphics g)

{

paint(g);

}
 
A

Andrew Thompson


I suggest you prepare an SSCCE[1] rather than point people
toward a 467 Kb Jar with no source.

[1] <http://www.physci.org/codes/sscce.jsp>

As an aside, I am not prepared to run a .jar that comes from
'some dude on usenet', but I should warn you that the 387Kb
JClients.jar will not be read correctly from within another
..jar file. Are you extracting it to disk first?

[ FWIW, thank you for not repeating the posting mistake you made
on your first crack at this problem. ]
 
M

MaSTeR

Andrew Thompson said:

I suggest you prepare an SSCCE[1] rather than point people
toward a 467 Kb Jar with no source.
It is not feasible to make an SSCCE, it is a big amount of code and besides
I am building it on top of those two libraries.
As an aside, I am not prepared to run a .jar that comes from
'some dude on usenet', but I should warn you that the 387Kb
JClients.jar will not be read correctly from within another
.jar file. Are you extracting it to disk first?

I replied to my post with two other links to Jclient and JAPI, that's all
you need. What I should have them is remove them from Ticker.jar because
they're unuseful there. I did it now.

[ FWIW, thank you for not repeating the posting mistake you made
on your first crack at this problem. ]

[FWIW You're welcome.]
 
B

Babu Kalakrishnan

MaSTeR said:
That's exactly what I would expect from a sync issue. Unfortunately it
behaves the opposite way (!). When I redraw the buffer from scratch it
paints correctly and as soon as I refresh it (by scrolling it manually or
forcing repainting in any ither way) it gets corrupted. Also I tried some
extreme synchronization measure like sync the whole methods and didn't work,
it just slowed down insertion of new records.

Then the most probable cause is that the way you're creating the
buffered image is at fault. Very difficult to speculate any further
without seeing the code.
Not sure about this last point, I do avoid clearing of the screen by
overriding update in this way:

// Overrides this method to prevent the superclass to clear the screen at
each repaint.

public void update(Graphics g)

{

paint(g);

}

You said in your last post that the super class implementation clears
the screen. In standard double-buffered painting implementations , you
normally never call the super.paint() (or super.update()) - that's the
reason I said that the implementation is broken.

By the way about your reluctance/inability to create a smaller example
that exhibits the problem : If you are certain that it is only the
combination of all those multiple jar files that causes this anomaly,
then your problem is far too complicated to find a solution in a
newsgroup - you had better hire a consultant to do it for you.

Normally, it should be possible to rig up a small example by removing
all unnecessary code, and by substituting any complex painting code with
simpler ones. (It is also possible that you might even find the cause of
your problem when you do this exercise). Yes - it does mean spending a
certain amount of time, but you can't expect others to spend time
on _your_ problem if you aren't willing to do so yourselves.

BK
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,585
Members
45,081
Latest member
AnyaMerry

Latest Threads

Top