?
Solved

Problem displaying animated gifs in a web applet

Posted on 2003-02-27
57
Medium Priority
?
625 Views
Last Modified: 2013-12-22
// This problem goes back to one I was discussing here last night... http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_20531492.html

// Here's the bottom line. I have a web based applet that among other things is displaying a little over 150 small (15x15 to 109x30 or so) icons in two windows. One window contains 48 icons and the other a little over 100). Of those icons, just under 1/2 are animated GIFs and the remaining are all static GIF images.

// After last night's exchange, I modified the code to use the MediaTracker class to ensure image source files are loaded before the image is drawn on screen. That has solved part of the problem I was having... but not all of.

// Where I am now is that all static images are drawn just fine and without an issue. However, only a handful (7) of the nearly 50 animated GIFs are ever drawn. The remaining images will never paint no matter what. I can "fix" the problem by forcing the applet to wait 2-3 seconds between the first attempt to display each image. If I do that, then all of the images will display just fine.

// So... the problem is not related to loading the source file as the MediaTracker has verified that all image files are loaded and without error. Nor is the problem related to memory or something like that since slowing the applet down will "fix" it. The issue appears to be directly related to how many objects are calling g.drawImage() for anitmated gifs for the FIRST TIME within a given period of time.

// The question: What is causing this problem and how can I detect (programmatically) that the image is not being drawn and, better still, how do I prevent this problem in the first place without asking users to wait almost 7 mintutes (150 images x 3 seconds each) to load all of the images? I seriously need a fix to this -- so I'm offering a respectable number of points.

// Here's a simplified view of the problem in question...

// Note the images are loaded in individual threads. This is done because the server is feeding the list of icons to the client applet by reading them from an SQL database on the server side and sending them via a TCP/IP Socket to the client applet. The applet reads these icon details, creates an icon instance and starts loading it in the background while it goes on to get other icons and start the main purpose of the applet itself. The idea is that images will "pop-in" to the icon windows as they are successfully loaded while the user can start working at the same time.

import java.awt.*;
import java.net.*;
import java.util.*;
import java.awt.image.*;

public class fubar extends Panel implements ImageObserver, Runnable
{
  private MediaTracker tracker = null;
  private Image        image   = null;
  private String       uri     = null;
  private int          id      = 0;
  private int          width   = 0;
  private int          height  = 0;

  public fubar()
  { tracker = new MediaTracker( this ); }

  public void loadImage( String uri, int id, int width, int height )
  {
      this.uri    = uri;
      this.id     = id;
      this.width  = width;
      this.height = height;

      setSize( width, height );
      new Thread( this ).start();
  }

  public void run()
  {
      try
      {
        Image img = Toolkit.getDefaultToolkit().getImage( new URL( uri ) );

        tracker.addImage( img, id, width, height );
        tracker.waitForID( id );

        if( tracker.isErrorID( id ) )
            throw new Exception( "Error loading ICON at " + uri );

        image = img;

        repaint( 10 );
      }
      catch( Exception e )
      {
        e.printStackTrace( System.err );
      }
  }

  public void update( Graphics g )
  {  paint( g ); }

  public void paint( Graphics g )
  {
      super.paint( g );

      if( isShowing() && image != null && g != null )
        g.drawImage( image, 0, 0, width, height, this );
  }

  public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height)
  {
      if( (flags & (FRAMEBITS|ALLBITS))!= 0 )
        repaint( 0 );

      return (flags & (ALLBITS|ABORT)) == 0;
  }
}

  // This method will load all of the images and paint all of the static gif images but only the first seven animated gifs will paint

  public void broken( MyServerSocket mySocket )
  {
      IconDef def;

      while( (def = getIconDef( mySocket )) != null )
      {
        fubar icon = new fubar();

        add( icon, null );

        icon.loadImage( def.uri, def.id, def.width, def.height );
        icon.setLocation( def.x, def.y );
      }
  }

  // This method will load and paint all of the images

  public void works( MyServerSocket mySocket )
  {
      IconDef def;

      while( (def = getIconDef( mySocket )) != null )
      {
        fubar icon = new fubar();

        icon.loadImage( def.uri, def.id, def.width, def.height );
        add( icon, null );
        icon.setLocation( def.x, def.y );

        try { Thread.sleep( 3000 ); }
        catch( InterruptedException e ) {}
      }
  }
}
0
Comment
Question by:spiel2001
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 28
  • 28
57 Comments
 
LVL 92

Expert Comment

by:objects
ID: 8037582
Try something like the following:

import java.awt.*;
import java.net.*;
import java.util.*;
import java.awt.image.*;

public class fubar extends Panel implements ImageObserver
{
 private Image        image   = null;
 private String       uri     = null;
 private int          id      = 0;
 private int          width   = 0;
 private int          height  = 0;

 public fubar()
 { }

 public Image loadImage( String uri, int id, int width, int height )
 {
     this.uri    = uri;
     this.id     = id;
     this.width  = width;
     this.height = height;

     setSize( width, height );
     
     image = Toolkit.getDefaultToolkit().getImage( new URL( uri ) );
     return image;
 }

 public void update( Graphics g )
 {  paint( g ); }

 public void paint( Graphics g )
 {
     super.paint( g );

     if( isShowing() && image != null && g != null )
       g.drawImage( image, 0, 0, width, height, this );
 }

 public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height)
 {
     System.out.println(flags);
     
     if( (flags & (FRAMEBITS|ALLBITS))!= 0 )
       repaint( 0 );

     return (flags & (ALLBITS|ABORT)) == 0;
 }
}

 // This method will load all of the images and paint all of the static gif images but only the first seven animated gifs will paint

 public void broken( MyServerSocket mySocket )
 {
     IconDef def;
     
     MediaTracker tracker = new MediaTracker(this);
     int id = 0;
     while( (def = getIconDef( mySocket )) != null )
     {
       fubar icon = new fubar();

       add( icon, null );

       Image i = icon.loadImage( def.uri, def.id, def.width, def.height );
       tracker.addImage( img, id++, width, height );
       icon.setLocation( def.x, def.y );
     }
     tracker.waitForAll();
     System.out.println("Any errors "+tracker.isErrorAny());

 }

}
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8037705
objects --

Been there and done that ~frown~

While part of hte initial problem we discussed last night was related to the files not having been loaded yet, the MediaTracker has dealt with that.

The problem now is clearly related to the number of *first time* simultaneous calls to g.drawImage() for animated gifs specifically. Waiting until every image is loaded before allowing any paints actually makes the problem worse.

The "fix" to the problem I have at the moment is to stagger the interval between calls to g.drawImage() top 2.5 to 3 seconds between each image -- but this is *after* having already confirmed the image was loaded without error by way of MediaTracker.

In my first effort last night, I did use a single instance of MediaTracker and waited for all images to be loaded before allowing any icons to paint. I thought it would be a nice visual touch when the images all sort of popped in together. That was a disaster.

Then I switched it over to allowing each individual image to paint as soon as it was confirmed to have been loaded without error by MediaTracker.

The next attempt was to use the fragment

  while( Thread.activeCount() > 7 )
  try { Thread.sleep( 10 ); }
  catch( Exception e ) {}

  new Thread( this ).start();

in the fubar.loadImage() method. This allows most of the images to paint properly but appears to be highly sensitive to the power of the PC the browser is running on. Reducing the number of concurrent threads improves reliability.

I then tried the Thread.sleep( 3000 ) approach to stager when the images are allowed to paint and found that to be the most reliable, but still not foolproof "fix"

The issue appears to be related to the use of g.drawImage() specifically on animated GIF and the occurance of the problem seems to be keyed on the number of concurrent first time attempts to pain the image all the while knowing the image is loaded.

The actual applet in question is a real-time chat and the windows are for interactive sounds and emoticons. If anyone wants to have a look-see at the working application (with the broken animated GIFs) it can be seen at http://www.marketbeasts.com/members/applets/Chat.html

You'll have to join the site first via http://www.marketbeasts.com/join to access the member area though -- I promise -- no spam and no sales.

I honestly have a hammering headache from too many consecutive hours of trying to figure out a usable fix to this problem. It's just not reasonable to expect to wait 7 minutes plus to load the images. ~frown~

Again -- many thanks.
0
 
LVL 92

Expert Comment

by:objects
ID: 8037805
In your code that works why do you start a new thread to wait for image to load instead of simply waiting for the image to load directly? Saving you the need to sleep for an arbitrary time (which may be to long or short).

Also are you sure the problem is not with lots of concurrent queries to the the database?

Have you tried with static images?
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 92

Expert Comment

by:objects
ID: 8037887
Also as you are painting your images on the fly then I don't see that even using MediaTracker the way you are using it will have any effect. It's purpose it to track the loading of the image.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8037912
The actual structure of the program is as follows...

1) The user connects to server via a socket on port 531 from the applet

2) The applet sends the user's login information and which chat room they're connecting to

3) The server feeds back the user's configuration, member marks, ignores and the default emoticons/sounds as a single threaded stream of datagrams... that is... the server sends a record, the client processes it and then reads the next record, etc.

4) The server starts sending new messages from other room participants as they are recieved and starts waiting for messages from the user to send to the room

The benefit of using a thread to do the loading of the emoticons and sounds is that the user does not have to wait that out before they can start participating in the room. If I single thread it on the client side, then the user has a long delay between connecting to the room and participating in the room.

In any case... in answer to your question, there's a single query to the database at a time from the user. I have verified that the client applet has received all of the correct icon data from the server and that it has fetched the source files without error.

I though I had noted earlier, but perhaps I did not, that the code works flawlessly with static GIF images. The only time it's a problem with when dealing with animated GIFs.

Thanks for the patience and for the effort. It's very much appreciated.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8037939
The way the code is written, the member variable "image" is not set until after the MediaTracker has reported that the load is complete (returned from waitForID() and isErrorID() is not set) -- so the call to paint() does not attempt to paint the image until I know it has been loaded successfully.

Also note that once the image is loaded and the member variable "image" is set non-null, I call "repaint( 10 )" so that the image will get drawn given that I am in a delayed thread and the panel has probably already painted in the master thread. This just ensures the panel is marked as dirty after we have the image.

And, actually, in context, the fubar class is an image cache... so when someone posts a message needing an emoticon to be displayed, the image for that message is pulled from the fubar instance for that emoticon and painted into the message object, too.

The point of that is that if it were simply a case of paint not getting called after the image loads, then when I paint the image into a new message object, it would definately get painted there. But it does not -- a broken image is a broken image and will not paint under any cicumstance.

One of thing of importance that I think I forgot to mention -- the ImageObserver's "updateImage()" is never called for the "broken" animated gifs even though I have proven that the d.drawImage() was called in the paint() for the observer.

It's a really odd bug
0
 
LVL 92

Expert Comment

by:objects
ID: 8038062
> The benefit of using a thread to do the loading of the emoticons

Images are loaded asyncronously. There is no need to use a seperate thread for image loading.

And you sleep until the image is (hopefully) loaded anyway.

Instead I'm saying you should get an image, use MediaTracker to wait until it is loaded, and then load the next one etc.

> there's a single query to the database at a time from the user.

So all the image data is coming in one stream.
Then you need to load your images one at a time. And ensure that each image is completed loading before loading the next one.

> Also note that once the image is loaded and the member variable "image" is set non-null

Sorry I missed that. You are correct.

> I call "repaint( 10 )"

Why the 10 msec delay?

> The only time it's a problem with when dealing with animated GIFs.

Let me check how the loading of animated images is handled.
It may only load the 1st frame initially, which would explain your problem.

0
 
LVL 92

Expert Comment

by:objects
ID: 8038134
What is def.uri pointing to?
0
 
LVL 92

Expert Comment

by:objects
ID: 8038144
to sequentially load all the images I'd do something like:

import java.awt.*;
import java.net.*;
import java.util.*;
import java.awt.image.*;

public class fubar extends Panel implements ImageObserver
{
 private Image        image   = null;
 private int          id      = 0;
 private int          width   = 0;
 private int          height  = 0;

 public fubar( Image image, int id, int width, int height )
 {
     this.image    = image;
     this.id     = id;
     this.width  = width;
     this.height = height;

     setSize( width, height );    
 }

 public void update( Graphics g )
 {  paint( g ); }

 public void paint( Graphics g )
 {
     super.paint( g );

     if( isShowing() && image != null && g != null )
       g.drawImage( image, 0, 0, width, height, this );
 }
}

 // This method will load all of the images and paint all of the static gif images but only the first seven animated gifs will paint

 public void loadimages( MyServerSocket mySocket ) thows Exception
 {
     IconDef def;
     
     MediaTracker tracker = new MediaTracker(this);
     int id = 0;
     while( (def = getIconDef( mySocket )) != null )
     {
        Image img = Toolkit.getDefaultToolkit().getImage( new URL( def.uri ) );

       tracker.addImage( img, def.id, def.width, def.height );
       tracker.waitForID( def.id );
       
       fubar icon = new fubar(img, def.id, def.width, def.height);
       add( icon, null );
       icon.setLocation( def.x, def.y );
     }
 }

}
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8038209
>> Images are loaded asyncronously. There is no need to use a seperate thread for image loading.

True -- except that the MediaTracker.waitForID() serves to eliminate that asyncronous behavior

>> And you sleep until the image is (hopefully) loaded anyway.

Well... I'm sleeping only to ensure the image is loaded before I try to paint. But I don't want or need to sleep until the images are all loaded as the "need" for them is asyncronous to the issue of the chat itself.

Let's assume for a second that it takes 1 second per image to load 150 images -- then we are asking the user to wait for two minutes before they see chat messages or can enter messages. That's unreasonable.

Put another way, I am using threads *because* I am using waitForID() and do not want to force the user to wait for all the images to be loaded before gaining access to the room. The use of the threads moves the waitForID() into the background to ensure the image is loaded before it is painted while at the same time letting the user access the chat room as quickly as possible.

However, let's keep in mind that the issue (problem) is not about the image loading as I have stated already. The issue relates to image painting via d.drawImage() for an image already known to have been loaded without error by way of waitForID().

>> So all the image data is coming in one stream.
>> Then you need to load your images one at a time. And ensure that each image is completed loading before loading the next one

Let's be careful not to confuse "image data" with image definitions... the definitions are in a stream but retrieval is dependent on the size of the image file itself. The definition is sending me the URI for the GIF file, the image's dimensions, the "shorthand" and "longhand" names for the images in the chat text and so on. The actual image file is retrieved asyncronously to the process of defining which images are supported in the chat room.

>> Why the 10 msec delay?

Just an artifact from seeing if giving the java engine a little more time would help. One of those things where you've tried so many things you start forgetting what was designed in and what was just trying to fix the problem ~smile~

>> Let me check how the loading of animated images is handled.
>> It may only load the 1st frame initially, which would explain your problem.

I have a sense the issue does relate to the loading of frames in animated gifs as static gifs work without fail every time and the "fix" is related to how long I wait between sucessive "first calls" to g.drawImage() -- the problem is how do we know the "right" amount of time to wait before making the next call? There has to be some way of knowing that it's okay to get the next image.

There are no exceptions being thrown, no funny values (null objects, etc.) or anything else I've been able to find to signal that a given GIF is broken other than that you call g.drawImage() and nothing shows up in the display area.


Anyway, once again, thanks for the time and effort you're putting in here. Keep it up and eventually you'll force me to explain to you why it's broken ~smile~ Funny how that works isn't it? It always seems that the more carefully you have to explain how and why you are doing something, the more likely you are to discover the problem youself.

I know this is tedious -- your help is greatly appreciated.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8038250
FWIW: This does not work -- it's already been tried...

public void loadimages( MyServerSocket mySocket ) thows Exception
{
    IconDef def;
   
    MediaTracker tracker = new MediaTracker(this);
    int id = 0;
    while( (def = getIconDef( mySocket )) != null )
    {
       Image img = Toolkit.getDefaultToolkit().getImage( new URL( def.uri ) );

      tracker.addImage( img, def.id, def.width, def.height );
      tracker.waitForID( def.id );
     
      fubar icon = new fubar(img, def.id, def.width, def.height);
      add( icon, null );
      icon.setLocation( def.x, def.y );
    }
}

In spite of the fact that the images are being loaded sequentially, we're still calling g.drawImage() too fast and the animated GIFs blow up. To make it work, I can change your code to this...

public void loadimages( MyServerSocket mySocket ) thows Exception
{
    IconDef def;
   
    MediaTracker tracker = new MediaTracker(this);
    int id = 0;
    while( (def = getIconDef( mySocket )) != null )
    {
       Image img = Toolkit.getDefaultToolkit().getImage( new URL( def.uri ) );

      tracker.addImage( img, def.id, def.width, def.height );
      tracker.waitForID( def.id );
     
      fubar icon = new fubar(img, def.id, def.width, def.height);
      add( icon, null );
      icon.setLocation( def.x, def.y );

      try { Thread.sleep( 3000 ); }
      catch( Exception e ) {}
    }
}

That's the key to the problem that I keep trying to make clear here -- the problem IS NOT realted to the loading of the image... it's related to how many times g.drawImage() is called in a given period of time, for the first time, for a series of animated GIFs.

Think of it this way -- if I load the images in parallel, but don't call paint until every one is loaded, it's broken. If I load the images in series and call paint as soon as waitForID() returns, it's broken. The *ONLY* way I've found to consistently get it to pain every animated GIF without error is to wait more than two seconds between successive calls to g.drawImage() on animated GIFs only. It doesn't seem to matter on iota how many static gifs I paint in parallel -- only the animated ones and regardless of how they were loaded.
0
 
LVL 92

Expert Comment

by:objects
ID: 8038329
> , I am using threads *because* I am using waitForID()
> and do not want to force the user to wait for all the
> images to be loaded before gaining access to the room.

Yes but only the method that loads the images should be in a seperate thread. ie. loadimages in my example above.

> The issue relates to image painting via d.drawImage()
> for an image already known to have been loaded

I'm not convinced :)

> the problem is how do we know the "right" amount of time
> to wait before making the next call?

thats what media tracker is for, as used in my example.

> There are no exceptions being thrown, no funny values (null objects, etc.)

As it's asynchonous there won't be. getImage() does not really do much.


> This does not work -- it's already been tried...
> we're still calling g.drawImage() too fast and the
> animated GIFs blow up.

Sorry the example was not intended to fix problem with animated images. It was to show how to load images sequentialy without having an (unnecessary) arbitrary sleep().



0
 
LVL 92

Expert Comment

by:objects
ID: 8038345
> The definition is sending me the URI for the GIF file

So the images are simply gif files on the server.
0
 
LVL 92

Expert Comment

by:objects
ID: 8038506
Have you tried including the gifs in your applet jar?
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8038511
>>> The issue relates to image painting via d.drawImage()
>>> for an image already known to have been loaded

> I'm not convinced :)

As already stated, however, I've done it syncronously and it didn't help at all. Made no difference. The only thing that has shown any variation in behavior is the amount of time between calls to g.drawImage().

> Sorry the example was not intended to fix problem with animated images. It was to show how to load images sequentialy without having an (unnecessary) arbitrary sleep().

This is understood. Remember, however, that sleep is not designed in... it's just the thing that makes it work.

> So the images are simply gif files on the server.

That is correct. The image definition received from the SQL database is just a space delimited string consisting of the image id number, the image width, the image height, the "shorthand" for that emoticon (ie: ;) for wink), the longhand for the emoticon (ie: ~wink~) and the URI for the GIF file. The program then calls Toolkit.getDefaultToolkit().getImage() for that URL via the loadImage() method.

I have done this several different ways so far:

1) Load images one at a time and display as soon as they are confirmed loaded

2) Load images one at a time and display only after all are loaded

3) Load images in parallel via threads and display each image when it has been loaded

4) Load images in parallel via threads but do not display until all are loaded

(please know I'm not just paying lip service -- I have physically coded and tested all sixteen permutations of the loading/painting combinations laid out here)

No matter which of those approaches I use I get the pretty much the same outcome give or take a  few more or less "broken" images. OTOH: I can take any of the above methods of loading the images and then paint the images one at a time with a three second interval between paints and they will all paint -- no matter which way I went about loading them. This is why I am absolutely convinced that the image loading is not the issue but the concurrent calls to g.drawImage() is the issue.

Now, I'll hasten to add I do believe it is possible the images are not being fully loaded for animated gifs (first frame only, perhaps, as you suggested?) in which chase it is technically a load issue but is would not appear to be related to the means by which is call getImage() and waitForID() so much as it is the way I call g.drawImage() and that, in turn does or does not call updateImage() in the ImageObserver

(now you know why I have a headache ~smile)
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8038533
>> Have you tried including the gifs in your applet jar?


Umm... this is the part where I get embarrased I guess ~smile~ I tried to do that and couldn't get it to work just because I cound't figure out how to getImage() from the jar and was pressed for time.

However, there is a viable reason for not doing that -- the web site uses both message boards and chat rooms and offers the chat rooms both as HTML refresh formatted forms and as a java applet. All three share the same emoticons -- all I have to do is to add the image file to the site and add a record to the SQL database and all the services are aware of the new emoticon (or sound clip)

If I include them in the JAR, then I have to rebuild the jar independently of everything else every time I change sound clips and/or emoticons.

Buuuuuuuuuuuuuuuuuuuuuuttt.... having said that, feel free to explain how to load images from the JAR file and I'll be more than happy to pitch in some extra points ~smile~ (no.... I've never bribed anyone in the my life - honest ~evil grin~)
0
 
LVL 92

Expert Comment

by:objects
ID: 8038712
> This is why I am absolutely convinced that the image
> loading is not the issue but the concurrent calls to
> g.drawImage() is the issue.

Yes but you'll get the same concurrent calls to drawImage() regardless of how you load them. And still once the images have been loaded.

> then paint the images one at a time with a three second
> interval between paints

the 3 sec interval is not between painting, it is between loading.

> concurrent calls to g.drawImage()

Haven't worked with AWT for a while but I'm pretty sure all paint calls are made from the same thread. ie. the calls cannot be concurrent. Easy to check.


0
 
LVL 92

Expert Comment

by:objects
ID: 8038742
> feel free to explain how to load images from the JAR file

URL imageurl = getClass().getResource(imagename);

> all I have to do is to add the image file to the site
> and add a record to the SQL database and all the
> services are aware of the new emoticon (or sound clip)

Easy enough to set up a script to do the above, and exapand that script to also rebuild the jar.


0
 
LVL 1

Author Comment

by:spiel2001
ID: 8038906
>> the 3 sec interval is not between painting, it is between loading

Well -- I'll grant you that the code I used at the start of this particular thread is organized that way. However, the code originally did not include a "run()" method and was fully single threaded but just as broken.

Here's what I'm going to do... I'm going to code an ultra-simple version of the problem, single thread it and demonstrate the problem. Perhaps it will help to approach this from the simplest possible angle.

I'll put up example code as soon as I can get it hammered out.

Once again, thanks.
0
 
LVL 92

Expert Comment

by:objects
ID: 8038982
> I'm going to code an ultra-simple version of the problem

Good idea.
If I can reproduce the problem here, it'll be a lot easier to identify problem :)
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8040081
Okay... here we go. The following is a simple test applet I've written and tested to work with this problem. The compiled applets and source files are at http://www.marketbeasts.com/testApps/testAp.html

This test is single threaded and serializes both the loading and the display of the emoticon images without using the SQL server or anything like that. Jsut a simple, straight forward, load and paint. Running Internet Explorer 6 with the MS JavaVM only shows a small handful of the animated emoticons though *all* the static emoticons are displayed.

FWIW: Opera 6.03 seems to work. Netscape 6.2.3 (Java Plug-in 1.3.1_02 for Netscape Navigator) has the same problems as the IE 6

I'll post the sources for the three test case classes as they are now defined here. If we do any tweaking, however, I'll only post that code on the site.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8040084
// This is the applet wrapper class

package emoticons;

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class testAp extends Applet
{
     boolean         isStandalone = false;
     BorderLayout    borderLayout = new BorderLayout();
     EmoticonDefs    emoticons;

     public testAp()
     {
     }

     public void init()
     {
          try
          {
               jbInit();
          }
          catch(Exception e)
          {
               e.printStackTrace();
          }
     }

     private void jbInit() throws Exception
     {
          this.setLayout(borderLayout);
     }

     public void start()
     {
          emoticons = new EmoticonDefs();

          this.add(emoticons, BorderLayout.CENTER);

          emoticons.loadIcons();
     }

     public void stop()
     {
          if( emoticons != null )
          {
               remove( emoticons );
               emoticons.destroy();
               emoticons = null;
          }
     }

     public void destroy()
     {
     }

     public String getAppletInfo()
     {
          return "Applet Information";
     }

     public String getParameter(String key, String def)
     {
          return isStandalone ? System.getProperty(key, def) : (getParameter(key) != null ? getParameter(key) : def);
     }

     public String[][] getParameterInfo()
     {
          return null;
     }
}
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8040088
// this is all of th emoticon definitions and the loader

package emoticons;

import java.awt.*;

public class EmoticonDefs extends Panel
{
      GridLayout gridLayout = new GridLayout();

      private String [] initializers =
      {
            // ID | WIDTH | HEIGHT | SHORTHAND | LONGHAND | URL

            "1|42|23|o;^)|~angel~|http://www.marketbeasts.com/images/emoticons/angel.gif",
            "2|22|21|!->|~attack~|http://www.marketbeasts.com/images/emoticons/attack.gif",
            "3|15|18|!:*|~baby~|http://www.marketbeasts.com/images/emoticons/baby.gif",
            "4|24|18|!bb|~bangbang~|http://www.marketbeasts.com/images/emoticons/bangbang.gif",
            "5|25|20|@||~banging head~|http://www.marketbeasts.com/images/emoticons/banghead.gif",
            "6|15|15|;^S|~bewildered~|http://www.marketbeasts.com/images/emoticons/bewildered.gif",
            "7|39|18|!bl|~blah~|http://www.marketbeasts.com/images/emoticons/blah.gif",
            "8|15|15|[;^)|~blockhead~|http://www.marketbeasts.com/images/emoticons/blockhead.gif",
            "9|15|15|;%)|~blush~|http://www.marketbeasts.com/images/emoticons/blush.gif",
            "10|22|21|o>-<|~boogie~|http://www.marketbeasts.com/images/emoticons/boogie.gif",
            "11|38|18|;^l|~bored~|http://www.marketbeasts.com/images/emoticons/bored.gif",
            "12|27|22|!bd|~bow~|http://www.marketbeasts.com/images/emoticons/bow.gif",
            "13|25|20|[;^(]|~boxed in~|http://www.marketbeasts.com/images/emoticons/boxedin.gif",
            "14|35|30|!bs|~bs~|http://www.marketbeasts.com/images/emoticons/bs.gif",
            "15|20|15|;^B|~butthead~|http://www.marketbeasts.com/images/emoticons/butthead.gif",
            "16|34|15|;^X|~censored~|http://www.marketbeasts.com/images/emoticons/censored.gif",
            "17|32|16|('<o|~chomp~|http://www.marketbeasts.com/images/emoticons/chomp.gif",
            "18|30|15|!c|~clap~|http://www.marketbeasts.com/images/emoticons/clap.gif",
            "19|25|22|;*)|~clown~|http://www.marketbeasts.com/images/emoticons/clown.gif",
            "20|27|27|!U|~coffee~|http://www.marketbeasts.com/images/emoticons/coffee.gif",
            "21|15|23|?;^/|~confused~|http://www.marketbeasts.com/images/emoticons/confused.gif",
            "22|16|16|B^)|~cool~|http://www.marketbeasts.com/images/emoticons/cool.gif",
            "23|45|15|<^(|~crying~|http://www.marketbeasts.com/images/emoticons/crying.gif",
            "24|33|34|!@#|~cursing~|http://www.marketbeasts.com/images/emoticons/cursing.gif",
            "25|41|46|x^(|~die~|http://www.marketbeasts.com/images/emoticons/die.gif",
            "26|36|36|:<P|~dog~|http://www.marketbeasts.com/images/emoticons/dog.gif",
            "27|27|27|!d|~doh~|http://www.marketbeasts.com/images/emoticons/doh.gif",
            "28|15|15|!-|~down~|http://www.marketbeasts.com/images/emoticons/down.gif",
            "29|15|15|<;^)|~dunce~|http://www.marketbeasts.com/images/emoticons/dunce.gif",
            "30|37|15|!?x|~dunno~|http://www.marketbeasts.com/images/emoticons/dunno.gif",
            "31|18|30|!e|~eek~|http://www.marketbeasts.com/images/emoticons/eek.gif",
            "32|31|31|>j<|~elephant~|http://www.marketbeasts.com/images/emoticons/elephant.gif",
            "33|17|24|>B^)|~evil grin~|http://www.marketbeasts.com/images/emoticons/evilgrin.gif",
            "34|37|15|!ft|~fighting~|http://www.marketbeasts.com/images/emoticons/fighting.gif",
            "35|21|15|!&|~fingers crossed~|http://www.marketbeasts.com/images/emoticons/fingerscrossed.gif",
            "36|24|26|!f|~flushed~|http://www.marketbeasts.com/images/emoticons/flushed.gif",
            "37|18|17|;^(d|~foot in mouth~|http://www.marketbeasts.com/images/emoticons/footinmouth.gif",
            "38|17|17|8:Q|~frog~|http://www.marketbeasts.com/images/emoticons/frog.gif",
            "39|15|15|;^(|~frown~|http://www.marketbeasts.com/images/emoticons/frown.gif",
            "40|15|15|8^)|~glasses~|http://www.marketbeasts.com/images/emoticons/glasses.gif",
            "41|15|15|;^D|~grin~|http://www.marketbeasts.com/images/emoticons/grin.gif",
            "42|31|20|!g|~grumble~|http://www.marketbeasts.com/images/emoticons/grumble.gif",
            "43|81|26|!hb|~happy birthday~|http://www.marketbeasts.com/images/emoticons/bday.gif",
            "44|25|29|!h|~hello~|http://www.marketbeasts.com/images/emoticons/hello.gif",
            "45|23|15|!??|~help~|http://www.marketbeasts.com/images/emoticons/help.gif",
            "46|19|19|^5|~high five~|http://www.marketbeasts.com/images/emoticons/highfive.gif",
            "47|15|15|;^/|~hmmm~|http://www.marketbeasts.com/images/emoticons/hmmm.gif",
            "48|38|15|!()|~hug~|http://www.marketbeasts.com/images/emoticons/hug.gif",
            "49|15|32|!;^)|~idea~|http://www.marketbeasts.com/images/emoticons/idea.gif",
            "50|15|15|;^<|~irked~|http://www.marketbeasts.com/images/emoticons/irked.gif",
            "51|97|29|!jm|~jamming~|http://www.marketbeasts.com/images/emoticons/jamming.gif",
            "52|35|24|!jt|~jetting~|http://www.marketbeasts.com/images/emoticons/jetting.gif",
            "53|31|31|!jj|~jump for joy~|http://www.marketbeasts.com/images/emoticons/jumpforjoy.gif",
            "54|34|15|;^*|~kiss~|http://www.marketbeasts.com/images/emoticons/kiss.gif",
            "55|25|19|!le|~lecture~|http://www.marketbeasts.com/images/emoticons/lecture.gif",
            "56|29|27|8^O|~lmao~|http://www.marketbeasts.com/images/emoticons/lmao.gif",
            "57|15|15|8^D|~lol~|http://www.marketbeasts.com/images/emoticons/lol.gif",
            "58|15|15|!xo|~love~|http://www.marketbeasts.com/images/emoticons/love.gif",
            "59|15|15|!lu|~lurking~|http://www.marketbeasts.com/images/emoticons/lurking.gif",
            "60|24|23|!md|~make a deal~|http://www.marketbeasts.com/images/emoticons/makeadeal.gif",
            "61|15|15|!Y|~martini~|http://www.marketbeasts.com/images/emoticons/martini.gif",
            "62|45|45|8<O|~monkey~|http://www.marketbeasts.com/images/emoticons/monkey.gif",
            "63|25|25|!m|~moon~|http://www.marketbeasts.com/images/emoticons/moon.gif",
            "64|22|19|!n|~no~|http://www.marketbeasts.com/images/emoticons/no.gif",
            "65|15|15|:o)|~nosey~|http://www.marketbeasts.com/images/emoticons/nosey.gif",
            "66|41|46|!ot|~off topic~|http://www.marketbeasts.com/images/emoticons/offtopic.gif",
            "67|42|34|!pt|~party~|http://www.marketbeasts.com/images/emoticons/party.gif",
            "68|30|30|!ph|~phone~|http://www.marketbeasts.com/images/emoticons/phone.gif",
            "69|60|15|!~@|~poke~|http://www.marketbeasts.com/images/emoticons/poke.gif",
            "70|21|15|?;^)|~ponder~|http://www.marketbeasts.com/images/emoticons/ponder.gif",
            "71|15|17|!pr|~praying~|http://www.marketbeasts.com/images/emoticons/praying.gif",
            "72|15|15|/;^)|~raised eyebrow~|http://www.marketbeasts.com/images/emoticons/raisedeyebrow.gif",
            "73|15|15|;^P|~razz~|http://www.marketbeasts.com/images/emoticons/razz.gif",
            "74|39|15|(8^D)|~roflol~|http://www.marketbeasts.com/images/emoticons/roflol.gif",
            "75|17|20|!sh|~shhh~|http://www.marketbeasts.com/images/emoticons/shhh.gif",
            "76|51|18|!st|~shooting~|http://www.marketbeasts.com/images/emoticons/shooting.gif",
            "77|50|15|!sd|~shot down~|http://www.marketbeasts.com/images/emoticons/shotdown.gif",
            "78|15|15|:^D|~slaphappy~|http://www.marketbeasts.com/images/emoticons/slaphappy.gif",
            "79|30|15|!zz|~sleeping~|http://www.marketbeasts.com/images/emoticons/sleeping.gif",
            "80|15|15|;^]|~sly grin~|http://www.marketbeasts.com/images/emoticons/sly.gif",
            "81|15|15|:)|~smile~|http://www.marketbeasts.com/images/emoticons/smile.gif",
            "82|21|15|;^)_|~smokin~|http://www.marketbeasts.com/images/emoticons/smokin.gif",
            "83|15|15|B^>|~sniker~|http://www.marketbeasts.com/images/emoticons/snicker.gif",
            "84|40|20|!s|~spank~|http://www.marketbeasts.com/images/emoticons/spank.gif",
            "85|15|15|!*|~star~|http://www.marketbeasts.com/images/emoticons/star.gif",
            "86|16|15|;'(|~tear~|http://www.marketbeasts.com/images/emoticons/tear.gif",
            "87|15|15|:^P|~tease~|http://www.marketbeasts.com/images/emoticons/tease.gif",
            "88|25|18|!v|~thumbs down~|http://www.marketbeasts.com/images/emoticons/thumbsdown.gif",
            "89|25|18|!^|~thumbs up~|http://www.marketbeasts.com/images/emoticons/thumbsup.gif",
            "90|70|35|!tk|~thunk~|http://www.marketbeasts.com/images/emoticons/thunk.gif",
            "91|60|20|!t|~toast~|http://www.marketbeasts.com/images/emoticons/toast.gif",
            "92|15|15|;^?|~tongue in cheek~|http://www.marketbeasts.com/images/emoticons/tongueincheek.gif",
            "93|23|15|!2|~two cents~|http://www.marketbeasts.com/images/emoticons/twocents.gif",
            "94|15|15|!+|~up~|http://www.marketbeasts.com/images/emoticons/up.gif",
            "95|45|35|!w|~whack~|http://www.marketbeasts.com/images/emoticons/whack.gif",
            "96|19|15|8^.|~whoa~|http://www.marketbeasts.com/images/emoticons/whoa.gif",
            "97|15|15|;)|~wink~|http://www.marketbeasts.com/images/emoticons/wink.gif",
            "98|119|25|!!w|~woohoo~|http://www.marketbeasts.com/images/emoticons/woohoo.gif",
            "99|24|17|!yk|~yacking~|http://www.marketbeasts.com/images/emoticons/yacking.gif",
            "100|19|16|;^O|~yawn~|http://www.marketbeasts.com/images/emoticons/yawn.gif",
            "101|19|25|!ys|~yes~|http://www.marketbeasts.com/images/emoticons/yes.gif",
            "102|19|25|8^@|~yikes~|http://www.marketbeasts.com/images/emoticons/yikes.gif",
            "103|19|24|!yp|~yippie~|http://www.marketbeasts.com/images/emoticons/yippie.gif",
            "104|15|15|:^p|~yuck~|http://www.marketbeasts.com/images/emoticons/yuck.gif",
            "105|15|15|!ym|~yummy~|http://www.marketbeasts.com/images/emoticons/yummy.gif"
      };

      private IconImage []    icons   = new IconImage[initializers.length];
      private MediaTracker    tracker;

      public EmoticonDefs()
      {
            try
            {
                  for( int i = 0; i < icons.length; i++ )
                        icons[i] = null;

                  jbInit();
            }
            catch(Exception ex)
            {
                  ex.printStackTrace();
            }
      }

      void jbInit() throws Exception
      {
            tracker = new MediaTracker( this );

            gridLayout.setRows(11);
            gridLayout.setColumns(10);
            gridLayout.setHgap(5);
            gridLayout.setVgap(5);

            this.setLayout(gridLayout);
      }

      public void loadIcons()
      {
            for( int i = 0; i < initializers.length; i++ )
            try
            {
                  icons[i] = new IconImage( initializers[i] );

                  tracker.addImage( icons[i].image, icons[i].id );
                  tracker.waitForID( icons[i].id );

                  if( tracker.isErrorID( icons[i].id ) )
                        throw new Exception( "Error loading " + icons[i].url );

                  add( icons[i], null );
            }
            catch( Exception e )
            {
                  e.printStackTrace( System.err );
            }
      }

      public void destroy()
      {
            for( int i = 0; i < icons.length; i++ )
            {
                  if( icons[i] != null )
                  {
                        icons[i].image.flush();
                        icons[i].image = null;
                        icons[i] = null;
                  }
            }
      }
}
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8040094
// and this is the icon image itself

package emoticons;

import java.awt.*;
import java.util.*;
import java.net.*;
import java.awt.image.*;

public class IconImage extends Panel implements ImageObserver
{
     public  int     id,
                         width,
                         height;

     public String   shorthand,
                         longhand,
                         url;

     public Image    image = null;

     public IconImage( String initStr ) throws Exception
     {
          StringTokenizer t = new StringTokenizer( initStr, "|" );

          id        = Integer.parseInt( t.nextToken() );
          width     = Integer.parseInt( t.nextToken() );
          height    = Integer.parseInt( t.nextToken() );
          shorthand = t.nextToken();
          longhand  = t.nextToken();
          url       = t.nextToken();
          image     = Toolkit.getDefaultToolkit().getImage( new URL( url ) );

          setSize( width, height );
     }

     public void update( Graphics g )
     { paint( g ); }

     public void paint( Graphics g )
     {
          super.paint( g );

          if( isShowing() && image != null && g != null )
               g.drawImage( image, 0, 0, width, height, getBackground(), this );
     }

     public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height)
     {
          if( (flags & (FRAMEBITS|ALLBITS))!= 0 )
               repaint( 0 );

          return (flags & (ALLBITS|ABORT)) == 0;
     }
}
0
 
LVL 92

Expert Comment

by:objects
ID: 8040105
Don't u sleep :-)
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8040133
Sleep -- what's sleep? ~grin~

Actually -- I've been trying to get this done and catch up all my other work. It's almost 3am here so now that this code is done, work's all caught up, etc... I'm outta here for a bit.

g'day mate ~smile~

Talk to you tomorrow and thanks again.

0
 
LVL 92

Expert Comment

by:objects
ID: 8040171
did a quick test, and it works fine with IE5/Sun VM1.3.1
Didn't work with IE5/MS VM.
0
 
LVL 92

Expert Comment

by:objects
ID: 8040331
NS7/VM1.4 loads ok

may just be a problem with MS VM.
0
 

Expert Comment

by:urbiman
ID: 8041881
The problem is:when you tell java to repaint,java does not necessary repaint.It just skips repaints if you repaint to often.
So only a few Images are displayed.To solve this problem use following code
backup.update(backup.getGraphics());

backup is your JFrame.
It does not work allways,too.But it works 500% better than repaint.

good luck.

Using JAI (Java advanced Imageing) could also be helpful

0
 
LVL 1

Author Comment

by:spiel2001
ID: 8042357
urbiman --

Appreciate the input but two things: First, no swing code -- it's straight AWT. Second, we've already proven that g.drawImage() is being called but the image is not displaying even though it's being drawn... so it's not a matter of getting into the paint() routine.

objects --

Thanks for compatibility info. In case you missed my earlier comment: Netscape 6.2.3 (Java Plug-in 1.3.1_02 for Netscape Navigator) has the same problems as the IE so I don't think it's MS VM only -- the JAVA 1.3.1 plugin doesn't rely on MS VM does it?
0
 
LVL 92

Expert Comment

by:objects
ID: 8044822
> backup.update(backup.getGraphics());

You should *never* do that.

> the JAVA 1.3.1 plugin doesn't rely on MS VM does it?

no
0
 
LVL 92

Expert Comment

by:objects
ID: 8046086
NS7/1.3.1_06 loads ok.

0
 
LVL 92

Expert Comment

by:objects
ID: 8046092
Changing it to load the images from the jar would be a good idea, as that should remove any dependance on the browser.
Be interesting to see if the problem then still exists with NS6.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8046094
Okay -- so the $50,000 question is: Why doesn't it work in the MS VM and some other browsers and what can we do to make it work reliably in the "broken" implementations?
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8046111
Objects -- I'll give it a go, but you know -- I think it will still be broken as I still believe the issue is in the Threading of the drawImage() and not the loading itself. We'll see.

I have an all day meeting tomorrow and Sunday (I hate working weekends) so it may be Monday before I can play with it. If you want to give it a go, be my guest.
0
 
LVL 92

Expert Comment

by:objects
ID: 8046143
> Why doesn't it work in the MS VM

The non-useful answer is that it's a piece of crap ;)

> what can we do to make it work reliably in the "broken" implementations?

1st thing I'd try is loading from a jar.

As far as not loading from a jar goes, a few test to start with would be:
1. see if you can reproduce the problem as an application (instead of an applet). Makes testing a hell of a lot easier.

2. determine at how many images you start having problems
eg. does it work with 1 image, 10 images, 50 images, etc.

3. once an image is supposedly loaded correctly, dump the image pixels to verify that it really is loaded correctly.

4. try loading all the images, but only displaying a few.
0
 
LVL 92

Expert Comment

by:objects
ID: 8046150
> Threading of the drawImage()

afaik there is no threading involved. the drawImage calls are made concurrently.
print the thread name in your paint method to check.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8057986
Hello Objects --

Hope you had a good weekend. ~smile~

As promised, I gave it a go this morning and modified the code to load the icons from the JAR file. And, as I had suspected, it did not resolve the issue.

As before, the test app is here...

http://www.marketbeasts.com/testApps/testAp.html

The IconImage constructor was modified to use the getResource() call...

     public IconImage( String initStr ) throws Exception
     {
          StringTokenizer t = new StringTokenizer( initStr, "|" );

          id        = Integer.parseInt( t.nextToken() );
          width     = Integer.parseInt( t.nextToken() );
          height    = Integer.parseInt( t.nextToken() );
          shorthand = t.nextToken();
          longhand  = t.nextToken();
          url       = t.nextToken();
          image     = Toolkit.getDefaultToolkit().getImage( getClass().getResource( url ) );

          setSize( width, height );
     }


and the icons placed into the JAR file. FWIW -- I also removed the icons from the site just to ensure that those being loaded were loading out of the JAR and could not possibly be coming from anywhere else. The testApps.java, EmoticonDefs.java and IconImage.java sources show the current state of the source.

It looks fairly certain the issue is tied to the drawImage() call and not the getImage()

So... now what? How to we determine when an image is not painting right (and fix it) and/or prevent the image from not painting correctly?

(now you know why I'm so frustrated by this problem ~smile~)
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8058085
FWIW -- On closer inspection, that might have been a partial fix as the version of Netscape I was testing (previously broken) now works. However, the Microsloth IE running Java VM is still broken.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8058781
I've made a modification to the IconImage class to print info to the System.err output as follows...

public class IconImage extends Panel implements ImageObserver
{
      public  int     id,
                              width,
                              height;

      public String   shorthand,
                              longhand,
                              url;

      public Image    image = null;

      public IconImage( String initStr ) throws Exception
      {
            StringTokenizer t = new StringTokenizer( initStr, "|" );

            id        = Integer.parseInt( t.nextToken() );
            width     = Integer.parseInt( t.nextToken() );
            height    = Integer.parseInt( t.nextToken() );
            shorthand = t.nextToken();
            longhand  = t.nextToken();
            url       = t.nextToken();
            image     = Toolkit.getDefaultToolkit().getImage( getClass().getResource( url ) );

            setSize( width, height );
      }

      public void update( Graphics g )
      { paint( g ); }

      private boolean drawn = false;

      public void paint( Graphics g )
      {
            super.paint( g );

            if( !isShowing() )  System.err.println( longhand + " is not showing" );
            if( image == null ) System.err.println( longhand + " has null image" );
            if( g == null )     System.err.println( longhand + " has null g" );

            if( isShowing() && image != null && g != null )
            {
                  if( !drawn ) System.err.println( "Drawing " + longhand );
                  g.drawImage( image, 0, 0, width, height, getBackground(), this );
                  drawn = true;
            }
      }

      private boolean updated = false;

      public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height)
      {
            if( !isShowing() )
            {
                  System.err.println( longhand + " is not showing" );
            }
            else if( !updated )
            {
                  System.err.println( "Updating " + longhand );
                  updated = true;

                  if( (flags & ABORT) != 0 ) System.err.println( longhand + " got abort flag" );
                  if( (flags & ERROR) != 0 ) System.err.println( longhand + " got error flag" );
                  if( (flags & ALLBITS) != 0 ) System.err.println( longhand + " got allbits flag" );
                  if( (flags & FRAMEBITS) != 0 ) System.err.println( longhand + " got framebits flag" );
            }


            if( (flags & (FRAMEBITS|ALLBITS))!= 0 )
                  repaint();

            return (flags & (ALLBITS|ABORT)) == 0;
      }
}

Now, running the program in IE6 using the Microsloth java VM produces the following output... note how Update is only called for a handful of the animated gifs even though drawImage() was called for all of the gifs...

Drawing ~angel~
Drawing ~attack~
Drawing ~baby~
Drawing ~bangbang~
Drawing ~banging head~
Drawing ~bewildered~
Drawing ~blah~
Drawing ~blockhead~
Drawing ~blush~
Drawing ~boogie~
Drawing ~bored~
Drawing ~bow~
Drawing ~boxed in~
Drawing ~bs~
Drawing ~butthead~
Drawing ~censored~
Drawing ~chomp~
Drawing ~clap~
Drawing ~clown~
Drawing ~coffee~
Drawing ~confused~
Drawing ~cool~
Drawing ~crying~
Drawing ~cursing~
Drawing ~die~
Drawing ~dog~
Drawing ~doh~
Drawing ~down~
Drawing ~dunce~
Drawing ~dunno~
Drawing ~eek~
Drawing ~elephant~
Drawing ~evil grin~
Drawing ~fighting~
Drawing ~fingers crossed~
Drawing ~flushed~
Drawing ~foot in mouth~
Drawing ~frog~
Drawing ~frown~
Drawing ~glasses~
Drawing ~grin~
Drawing ~grumble~
Drawing ~happy birthday~
Drawing ~hello~
Drawing ~help~
Drawing ~high five~
Drawing ~hmmm~
Drawing ~hug~
Drawing ~idea~
Drawing ~irked~
Drawing ~jamming~
Drawing ~jetting~
Drawing ~jump for joy~
Drawing ~kiss~
Drawing ~lecture~
Drawing ~lmao~
Drawing ~lol~
Drawing ~love~
Drawing ~lurking~
Drawing ~make a deal~
Drawing ~martini~
Drawing ~monkey~
Drawing ~moon~
Drawing ~no~
Drawing ~nosey~
Drawing ~off topic~
Drawing ~party~
Drawing ~phone~
Drawing ~poke~
Drawing ~ponder~
Drawing ~praying~
Drawing ~raised eyebrow~
Drawing ~razz~
Drawing ~roflol~
Drawing ~shhh~
Drawing ~shooting~
Drawing ~shot down~
Drawing ~slaphappy~
Drawing ~sleeping~
Drawing ~sly grin~
Drawing ~smile~
Drawing ~smokin~
Drawing ~sniker~
Drawing ~spank~
Drawing ~star~
Drawing ~tear~
Drawing ~tease~
Drawing ~thumbs down~
Drawing ~thumbs up~
Drawing ~thunk~
Drawing ~toast~
Drawing ~tongue in cheek~
Drawing ~two cents~
Drawing ~up~
Drawing ~whack~
Drawing ~whoa~
Drawing ~wink~
Drawing ~woohoo~
Drawing ~yacking~
Drawing ~yawn~
Drawing ~yes~
Drawing ~yikes~
Drawing ~yippie~
Drawing ~yuck~
Drawing ~yummy~
Updating ~baby~
Updating ~angel~
Updating ~banging head~
Updating ~attack~
Updating ~bangbang~
Updating ~boogie~
Updating ~blush~
Updating ~blah~
Updating ~bewildered~
Updating ~bored~
Updating ~bow~
Updating ~bs~
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8058997
Another not(e:

Certainly not without coincidence)

The "public void update( Graphics g )" method in IconImage is only being called on those images for which imageUpdate() has been called. I would presume that the net effect of calling imageUpdate() is eventually a call to update( getGraphics() ) or something to that affect.

So the bottom line appears to be that if too many new animated images are thrown at drawImage() at the same time, the Java VM only attempts to paint a handful -- the rest seem to be ignored.

I should also note that imageUpdate() is never called on the static GIFs either... I would have thought we would get at least one call. Perhaps the use of MediaTracker to ensure the image is loaded before drawImage() is called is eliminating the need to call imageUpdate for static gifs? We don't get a width, height, allbits or somebits call on static images yet they do paint.

Anyway... So -- how do we determine which animated images will not be drawn or otherwise work around the problem?

Any ideas?
0
 
LVL 92

Expert Comment

by:objects
ID: 8060715

> The "public void update( Graphics g )" method in
> IconImage is only being called on those images for which
> imageUpdate() has been called.

Why do you say that? paint() appears to be being called for all images.

> I would presume that the net effect of calling
> imageUpdate() is eventually a call to update( getGraphics() )
> or something to that affect.

repaint() will result in the panel being repainted.

> So the bottom line appears to be that if too many new
> animated images are thrown at drawImage() at the same
> time

The calls to drawImage() afaik cannot occur at the same time as they are being called from the same thread. Print out the thread name to verify.

> Perhaps the use of MediaTracker to ensure the image is
> loaded before drawImage() is called is eliminating the
> need to call imageUpdate for static gifs?

That would be the case if you were waiting for the images to be loaded before painting them, but you aren't.
I'd say the reason is that the image has completed loading before drawImage() is called.
0
 
LVL 92

Expert Comment

by:objects
ID: 8060749
Try changing your imageUpdate() method to the following, I wouldn't expect this to make a difference but worth trying:

public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height)
{
   System.err.println("update "+flags);
   if( !isShowing() )
   {
      System.err.println( longhand + " is not showing" );
   }
   else if( !updated )
   {
      System.err.println( "Updating " + longhand );
      updated = true;

      if( (flags & ABORT) != 0 ) System.err.println( longhand + " got abort flag" );
      if( (flags & ERROR) != 0 ) System.err.println( longhand + " got error flag" );
      if( (flags & ALLBITS) != 0 ) System.err.println( longhand + " got allbits flag" );
      if( (flags & FRAMEBITS) != 0 ) System.err.println( longhand + " got framebits flag" );
   }

   return super.imageUpdate(image, flags, x, y, width, height);
}

0
 
LVL 92

Expert Comment

by:objects
ID: 8061845
> That would be the case if you were waiting for the
> images to be loaded before painting them, but you aren't.

I take that back, I just noticed the panel doesn't get added until image is loaded.

0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062133
> The "public void update( Graphics g )" method in
> IconImage is only being called on those images for which
> imageUpdate() has been called.

>> Why do you say that? paint() appears to be being
>> called for all images.

because I modified the method to be

public void update( Graphics g )
{
  System.err.println( "update( " + longhand + " )" );
  paint( g );
}

and the only icons that the message printed on were the same ones that the "updating image" message got printed for even though the "painting" message was printed for every icon.

>> I take that back, I just noticed the panel doesn't get
>> added until image is loaded.

lmao -- I was writing you back a reply almost yelling at you out of frustration because I couldn't understand why you had said that several times now. I had pasted in all the code and highlighted the points and everything to be perfectly sure I wasn't losing what little of my mind remains before I saw that "I take that back" -- I laughed so hard I had tears in my eyes.

>>  return super.imageUpdate(image, flags, x, y, width, height);

As expected it made no difference which would stand to reason given that imageUpdate is never called at all for the "broken" images.

Are we having fun yet?
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062134
> The "public void update( Graphics g )" method in
> IconImage is only being called on those images for which
> imageUpdate() has been called.

>> Why do you say that? paint() appears to be being
>> called for all images.

because I modified the method to be

public void update( Graphics g )
{
  System.err.println( "update( " + longhand + " )" );
  paint( g );
}

and the only icons that the message printed on were the same ones that the "updating image" message got printed for even though the "painting" message was printed for every icon.

>> I take that back, I just noticed the panel doesn't get
>> added until image is loaded.

lmao -- I was writing you back a reply almost yelling at you out of frustration because I couldn't understand why you had said that several times now. I had pasted in all the code and highlighted the points and everything to be perfectly sure I wasn't losing what little of my mind remains before I saw that "I take that back" -- I laughed so hard I had tears in my eyes.

>>  return super.imageUpdate(image, flags, x, y, width, height);

As expected it made no difference which would stand to reason given that imageUpdate is never called at all for the "broken" images.

Are we having fun yet?
0
 
LVL 92

Expert Comment

by:objects
ID: 8062208
Think I found a fix, after adding the ImageIcon panel revalidate the panel:

...
if( tracker.isErrorID( icons[i].id ) )
   throw new Exception( "Error loading " + icons[i].url );

add( icons[i], null );
invalidate();
validate();

Basically this causes the container to re-layout it's children and appears to fix the problem in the tests I've done.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062214
>>> So the bottom line appears to be that if too many new
>>> animated images are thrown at drawImage() at the same
>>> time

> The calls to drawImage() afaik cannot occur at the same
> time as they are being called from the same thread.
> print out the thread name to verify.

I am of a mind that g.drawImage() is threaded in some way or at a minimum there are timers of something going off to cue when the next frame is to be painted in an animated gif. There has to be some way of timing the repaints to match the timing of the frame intervals as defined in the GIF file. Yes / No?

I'll do some testing to see what Thread names look like and if I can catch any variants. If it isn't threads, then it's timers. Obviously something permits you to draw multiple animated gifs on the same panel and keep updating them to provide the animation. It appears to do it without interfering with the main thread, so I have assumed it (perhaps erroneously) to be an asyncronous process similar to the loading via getImage().
0
 
LVL 92

Expert Comment

by:objects
ID: 8062250
All painting is done from the event dispatch thread.
The cycling of frames is handled by the image loading thread, not the paint thread. All drawImage basically does is paint the current frame.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062269
I was all excited looking forward to posting that "u da man" message and no go. ~frown~

Current code with test messages and all that are at the link. I modifed the loadIcons() methods as follows and it's still broken in the POS Java VM.

     public void loadIcons()
     {
          for( int i = 0; i < initializers.length; i++ )
          try
          {
               icons[i] = new IconImage( initializers[i] );

               tracker.addImage( icons[i].image, icons[i].id );
               tracker.waitForID( icons[i].id );

               if( tracker.isErrorID( icons[i].id ) )
                    throw new Exception( "Error loading " + icons[i].url );

               add( icons[i], null );

               invalidate();
               validate();
          }
          catch( Exception e )
          {
               e.printStackTrace( System.err );
          }
     }

As always.. current results are http://www.marketbeasts.com/testApps/testAp.html

>> The cycling of frames is handled by the image loading
>> thread, not the paint thread.

Okay.. so the paint thread calls drawImage() which I presume somehow gets a load thread going. Who calls updateImage? The load thread right?

I'll add print messages and find out.

>> All drawImage basically
>> does is paint the current frame.

I'm not so sure about that -- no frame ever pains for the broken images even though we know we called drawImage() -- something is happening inside drawImage.

Let me add the extra print messages and we'll find out.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062316
I knew it... the call to drawImage() is working another bakcground thread for animated gifs.

This is the print right before calling drawImage()

***    Drawing ~angel~ in Thread "AWT-EventQueue-0"

which was then followed by a call to updateImage() that printed

***    Updating ~angel~ (flags = 3) in Thread "Image Animator"

and ultimately we find update() called back in the event thread...

***    Update( ~angel~ ) in Thread "AWT-EventQueue-0"

This confirms what I suspected that drawImage is starting a process in a background thread. That means that the event thread can be throwing a new drawImage() call before the animator thread has completed the last drawImage call on a different image.

It is my opinion that we're overflowing something inside drawImage() after N calls to drawImage() before the first call has completed in the animate thread. The question is how to prove it, detect it and prevent it?

Or, am I being too quick to pass the buck... is it something else maybe?
0
 
LVL 92

Expert Comment

by:objects
ID: 8062327
Hmm, it's working fine here (as an application).
(though there may be variations between my code and yours by now)

> so the paint thread calls drawImage() which I presume
> somehow gets a load thread going.

No (in your case) the load thread is already going.

> Who calls updateImage? The load thread right?

correct.



0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062363
>> It is my opinion that we're overflowing something
>> inside drawImage() after N calls to drawImage() before
>> the first call has completed in the animate thread.
>> The question is how to prove it, detect it and prevent it?

This is, actually, no longer an opinion... it's a proven point. doh! I'm an idiot -- look at the Java Console messages from the version at the web site...

All 100+ calls are made to drawImage() before the first call to imageUpdate() executes.

Then we see the following "updating" messages sprawled out in and amongst the update() messages (always an updating message before an update() message. I would guess the image animator thread by way of the imageUpdate() call to repaint() is queing events for the AWT event thread resulting in a call to update().

Updating ~attack~ (flags = 3) in Thread "Image Animator"
Updating ~shot down~ (flags = 16) in Thread "Image Animator"
Updating ~shooting~ (flags = 16) in Thread "Image Animator"
Updating ~lol~ (flags = 16) in Thread "Image Animator"
Updating ~smokin~ (flags = 16) in Thread "Image Animator"
Updating ~toast~ (flags = 16) in Thread "Image Animator"
Updating ~bewildered~ (flags = 3) in Thread "Image Updating ~angel~ (flags = 3) in Thread "Image Animator"
Updating ~baby~ (flags = 3) in Thread "Image Animator"
Updating ~blah~ (flags = 3) in Thread "Image Animator"
Updating ~blush~ (flags = 3) in Thread "Image Animator"
Updating ~bow~ (flags = 3) in Thread "Image Animator"
Updating ~bored~ (flags = 3) in Thread "Image Animator"
Updating ~banging head~ (flags = 3) in Thread "Image Animator"
Updating ~bangbang~ (flags = 3) in Thread "Image Animator"
Updating ~confused~ (flags = 3) in Thread "Image Animator"
Updating ~coffee~ (flags = 3) in Thread "Image Animator"
Updating ~clap~ (flags = 3) in Thread "Image Animator"
Updating ~chomp~ (flags = 3) in Thread "Image Animator"
Updating ~bs~ (flags = 3) in Thread "Image Animator"
Updating ~boogie~ (flags = 3) in Thread "Image Animator"

-- now -- let me note that if I remove all the System.err.println() calls the program runs a little faster and paints  a few less of the animated images. Thus, the little bit of extra slowdown is painting a few extra images and we're painting 21 images at this speed and my original testing showed "about 7" images would paint before it broke (though I had not counted them exactly -- I was guestimating)

I would bet dollars to donuts there's an 8 element buffer array somewhere inside drawImage() that this program is overflowing (they never thought animated images would load fast enough to overflow it) -- maybe a 16 element array.

Whaddaya think? And, we're still eft with how do we prevent/fix this?

0
 
LVL 92

Accepted Solution

by:
objects earned 2000 total points
ID: 8062384
> Whaddaya think?

If I was a betting man, I'd take that bet :-)



Try calling loadIcons() from a new thread.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062576
That, my good man, did the job. -- at least I drew all the images. ~smile~

The extra Thread put the loader into it's own thread which left the AWT-event thread free to deal with dispatching and thus allows the calls to imageUpdate() and update() to execute well before we've called drawImage() enough time to overflow it.

Outstanding.

you, Sir, are da man ~smile~

As promised -- I've upped the points from 500 to 750, too.

I can't thank you enough for having stuck it out and helping me work this thing out. I think we'll both agree it was not an easy one.
0
 
LVL 1

Author Comment

by:spiel2001
ID: 8062600
0
 
LVL 92

Expert Comment

by:objects
ID: 8062615
Excellent news, I can sleep easy tonight :-)

Good luck with getting your chat all working.

http://www.objects.com.au/staff/mick
Brainbench MVP for Java 1
http://www.brainbench.com

0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

INTRODUCTION Working with files is a moderately common task in Java.  For most projects hard coding the file names, using parameters in configuration files, or using command-line arguments is sufficient.   However, when your application has vi…
Introduction Java can be integrated with native programs using an interface called JNI(Java Native Interface). Native programs are programs which can directly run on the processor. JNI is simply a naming and calling convention so that the JVM (Java…
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…
How to fix incompatible JVM issue while installing Eclipse While installing Eclipse in windows, got one error like above and unable to proceed with the installation. This video describes how to successfully install Eclipse. How to solve incompa…
Suggested Courses

719 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question