#1
  1. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2003
    Location
    Montreal
    Posts
    2
    Rep Power
    0

    Javascript image preload race condition


    Hello all.

    I'm trying to resolve a race condition I'm having with Netscape 4.7, Opera, and Mozilla (don't seem to have it with IE yet, but that doesn't mean it can't happen).

    What I have to do is load an image, calculate it's height/width, and based on what I get back determine if I have to resize the image (and proportionally resize it as opposed to trying to slam it into a window and skew the image all around).

    My problem is that the image.height and image.width only returns proper values once the image is preloaded.

    So, here is the logic which I think I need to implement in a function for all of this to work:
    1) preload the image based on URL
    2) wait for the image to complete (have some trigger tell me that it's complete)
    3) extract the original height/width of the image and return the value to the caller.

    What I get however is problems. I was able to dig up one trigger image.onload which can fire once the image is loaded. I also have the image.complete flag which I can test to see if the image is finished loading into cache. However, it's my while loop which is causing me problems. It seems that if I stick myself in a while loop and check to see when the image is finished preloading, the thread of execution is never given back to the browser to finish loading up the image (likely in the background). So, as long as I'm in the while loop, the image does not seem to load. However, until the image is loaded, I have to remain in the loop. Catch-22.

    The other possibility (using the image.onload trigger) can't work either, as I cannot seem to guarentee when it will go off (not serializable -- I cannot seem to direct the thread of execution back to the function which called the image to be loaded). Fubar.

    Here is the code which demonstrates the problem. To set it up, simply have two images (image1.jpg and image2.jpg)in the same directory as this html file and it'll go:

    [code]
    <html>
    <head>
    <title>javascript image test</title>
    </head>
    <body>
    <script language="JavaScript1.2">
    <!--
    var TRUE = 1;
    var FALSE = 0;

    var image01 = new Image();
    image01.src='image1.jpg';

    function getImgHeight(path) {
    if (document.images) {
    var tmpImage = new Image();
    tmpImage.src=path;

    if (tmpImage.complete) {
    return tmpImage.height;
    } else {

    while (!tmpImage.complete) {
    continue;
    };

    return tmpImage.height;
    }
    }
    }

    function getImgWidth(path) {
    if (document.images) {
    var tmpImage = new Image();
    tmpImage.src=path;

    if (tmpImage.complete) {
    return tmpImage.width;
    } else {

    while (!tmpImage.complete) {
    continue;
    };

    return tmpImage.width;
    }
    }
    }

    document.write("<table border=1>");
    document.write("<tr><td></td><td></td><td>IMAGE 1</td><td></td><td>IMAGE 2</td></tr>");

    document.write("<tr><td>Height</td>");
    document.write("<td></td><td>" + image01.height+ "</td>");
    document.write("<td></td><td>" + getImgHeight('image2.jpg') + "</td></tr>");

    document.write("<tr><td>Width</td>");
    document.write("<td></td><td>" + image01.width + "</td>");

    document.write("<td></td><td>" + getImgWidth('image2.jpg') + "</td></tr>");

    document.write("</table>");

    // -->
    </script>
    <p>
    <table border=1>
    <tr>
    <td>Image1: </td>
    <td><img src=image1.jpg></td>
    <td>Image2: </td>
    <td><img src=image2.jpg></td>
    </tr>
    </table>
    </body>
    </html>

    Also notice that the image I preloaded (image01) seems to work (likely because the race condition doesn't seem to come up on my workstation). It's the image I preload in the functions which I can't ever get to work properly.

    Any clue as to what I can do here? How can I get the image size reliably from a function call without having to wonder if that race condition is going to occure?

    Thanks for any/all help,

    Chris
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed God 1st Plane (5500 - 5999 posts)

    Join Date
    Oct 2000
    Location
    Back in the real world.
    Posts
    5,966
    Rep Power
    190
    you could poll for the image finishing loading in a window.setTimeout() function...
  4. #3
  5. No Profile Picture
    Senior Citizen
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2001
    Location
    leftcoast
    Posts
    2,019
    Rep Power
    16
    Thought you made up that phrase 'race condition' - so I looked it up - hah! Excellent...shows what I know.

    A question: since the 'race condition' seems to be created by running two scripts - the preloader and the table writer - why don't you use a different methodology altogether? No sense pitting a scripting routine which hinges on network download speed (glacially slow compared to a client program) against a tiny JavaScript. This:

    while (!tmpImage.complete) {
    continue;
    };

    makes me wanna reboot...

    If I'm seeing your purpose here, what you want is a prompt message - 'Please wait - images loading' - and, when the onload handlers of all the preload objects have run, output the rest of the page. As M. Hirsch suggested, you could poll for this; I prefer keeping it event-driven, setting up each onload handler to call a routine which checks a (persistent) counter which starts at images.length and is decremented by each load. Either way should be fine.

    You should always set a preload object's onload handler, or set up to check its .complete property, before setting the .src property...running off a local drive, with cached images, load time is very fast.

    btw, PHP eats this stuff for lunch...
  6. #4
  7. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2003
    Location
    Montreal
    Posts
    2
    Rep Power
    0
    Yeah, don't let that "while - continue" loop get to you, it's just to prove a point.

    In most languages, you can release the thread of execution to another function and wait to have it returned to you. However, I'm not sure Javascript allows anything similar to this, so I was trying to think of some way to loop until I get a positive responce that my image is loaded. From what I can tell, most browsers pass the image loading onto a background process (this is efficient, because it lets your browser become responsive while downloading images).

    Harumph. Maybe I'm going about it the wrong way (I'm new at Javascript, and am trying to learn it's erratic behaviours).

    Here's my scenario. Maybe someone can find a new approach which I can take.

    I have a list of images which I have to fit inside a box (say 100x100 px). It a slide show of images found on a database (I don't control the image content, just the presentation). When these are passed to me (via JSP tag library), I want to resize the image if needed in an intelligent manner.

    My first impression was to preload the image, calculate the height/width, and output the image onto the document by setting the <img height = y, width = x) tag accordingly (a little highschool math and my images don't get stretched). My problem is that for this function to work, I have to have the images completely preloaded before the image.height and .width to work.

    Another thought that just occured to me is that I could set a trigger which automatically resizes all images after they have all registered being loaded. Can you resize the height/width of an image? I was given the impression that these were read-only values, and that once defined I'm bound by them.


    Chris
  8. #5
  9. No Profile Picture
    Senior Citizen
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2001
    Location
    leftcoast
    Posts
    2,019
    Rep Power
    16
    Hmm...how does one intelligently resize a rectangular image to fit a square frame? If all the images are square, this:

    <img width="100" height="100" src="......

    ..will, believe it or no, do the trick. You don't even need to set both dimensions: whether in HTML or JS, resize an image on one side and the browser proportionately resizes it on the other. If the supplied images are rectangular, something like this might do:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    <head>
    <title>untitled</title>
    <script type="text/javascript" language="javascript">

    function smartResize(oImg, size) {
    if (oImg.width>oImg.height) oImg.width = size;
    else oImg.height = size;
    if (typeof oImg.style != 'undefined')
    setTimeout('document["'+oImg.name+'"].style.visibility = "visible"',1000);
    }

    </script>
    </head>
    <body>
    <img name="resizer1" src="http://www.michellesgifts.com/figi/SWITCHPLATES/fruit_s.jpg"
    onload="smartResize(this,100)" border="2" style="visibility:hidden;">
    <img name="resizer2" src="http://www.michellesgifts.com/figi/SWITCHPLATES/fruit2_s.jpg"
    onload="smartResize(this,100)" border="2" style="visibility:hidden;">
    <img name="resizer3" src="http://www.trengovestudios.com/images/vegetables.jpg"
    onload="smartResize(this,100)" border="2" style="visibility:hidden;">
    </body>
    </html>

    If not, a description of the slide show workings would help (me!)...

    cheers, adios
  10. #6
  11. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2003
    Posts
    138
    Rep Power
    12
    Don't know if this helps because I'm not exactly sure what you are doing, but all of the things in the <head> of the
    html page should be loaded and complete before anything in the <body> is displayed.

    Why no try get initial size in the head and do your resizing in the body?
    --

    ngibsonau

IMN logo majestic logo threadwatch logo seochat tools logo