HTML Image Viewer- LOTS of images

Hey All,

I’ve read several conversations here about people using HTML Viewer to browse or load images. The thread that got me to try the HTML tables was one where it was recommended by Michael Bujardet (it was for iOS). I’m using Windows and I’ve constructed an HTML viewer that uses a table to load my images. It’s intended to be an SD Card browser/selector. It would be common for the browser to need to work with an sd card containing thousands of images. I planning on using pagination for that but I wanted to use as few pages as possible. My problem is that I started with 100 images and I’ve now scaled it back all the way to 25 images (per page) and the result is the same: SUPER SLOW and glitchy scrolling in the HTML Viewer. The clicks on the scroll bar don’t show a response for close to 2 seconds. Arrow buttons on the keyboard aren’t much better and same goes for the scroll wheel on the mouse.

When I do scroll the CPU Time eaten up by the debug application spikes as high as 33% (very briefly) and the memory usage goes up to about 300MB and then goes back down to 224MB (for 25 loaded images). I’ve got plenty more CPU time and Memory when this happens. My guess is this is somehow related to Xojo’s handling of the HTML viewer- would that be right? I know, regardless of what size the HTML () loads the image as, the images are being loaded full size and merely displayed smaller but given that I’m working with several thousand images I don’t think that resizing the images is going to be an option because of how long it would take to resize that many images. Anyone have any ideas about how/why it browses the html viewer the way that it does?

Thanks!
Ben

How does IE react if you load the same html? Do you have an example html file for testing?

I’m using Webkit/Chrome and it loads like any other web page and runs just fine. :confused:

I suspect your biggest problem is that you’re using the full sized images. Some cameras create a separate thumbnail file “.thm” next to the image which you can use, otherwise you may want to consider making your own thumbnails or even using a custom canvas.

I am sure that there are similar functions on Windows, on the Mac you can use API to ask for a thumbnail from an image file, if it contains one, it will return that one to you, or it can generate you a thumbnail. Which is a lot quicker than loading the full resolution image and drawing it down into a smaller version.

It’s possible with some JavaScript and a thread, you could display the page and then create the thumbnails on the fly and update each element with the thumbnail your code created, I would suggest caching these thumbnails, maybe use the “.thm” file so it’s compatible with other applications also.

I assumed as much (image sizes) but was surprised at how poorly the HTML viewer performed compared to a browser.

In this case the cameras are from a niche industry (trail cameras) and definitely don’t have thumbnail files. It’s possible that JavaScript could do a better/quicker job at resizing images. I’ll look for a library that does it and see.

If you find a JavaScript library I would be interested, too, because I want to optimise the html that my app creates.

I’ll let you know.

Here is what I’ve come up with. It will load 48 images in 3.5 seconds. Not great but it scrolls as smoothly as a browser so I’m happy with it for now. One important thing to keep in mind is that a canvas is part of html5 and uses CORS which restricts using resources from other domains unless explicitly allowed. The problem is that when looking at resources on a local computer it appears to treat all resources as being from another domain and without the explicit response header saying it’s permitted, the JS code below will fail. So I’ve spent several days testing portable, small-footprint web servers with CLI that would allow me to configure the response headers and allow for cross-domain resource usage. I finally found caddy (http://www.caddyserver.com). So with that running the following code will load images from an array (the path is in the array at the top line) and then put them in the in the HTML code that have the id that you can see I’m building 2nd line into the for loop.

I know the code is kind of a mess and not refined yet but it got ugly when I started having context problems in the onload event.

var myPics = ["http://localhost:8080/STC_0002.JPG","http://localhost:8080/STC_0003.JPG"]; //my images var imgWidth = 283; //the width I want them to display at function getThumbs() { var i; var id; var canvas = document.createElement("canvas"); //declaring canvas 1 time outside of loop for (i = 0; i < myPics.length; i++) { var img = new Image(); //create the image obj id = 'img'+(i+1); //build the element ID name for using below img.crossOrigin="Anonymous" //necessary to for CORS img.myID = id; //setting this to an img property so it can be used in the "that" array in event img.can = canvas; //for access in event img.cnt = i; //for access in event img.onload = function() { var that = this; //giving access to img object without context issues var currHeight = that.height; //original image height var currWidth = that.width; //original image width var perc = imgWidth / currWidth; //getting new width percentage to make new height proportionate that.can.width = Math.round(that.width * perc); //setting the canvas width that.can.height = Math.round(that.height * perc); //setting the canvas height that.can.getContext("2d").drawImage(that, 0, 0, that.can.width, that.can.height); //creating the image inside of the convas at the correct size document.getElementById(that.myID).src = that.can.toDataURL("image/jpeg"); //creating a URL from the canvas blob that can be fed to the img src. } img.src = myPics[i]; //once the source loads it will call the onload event above and process the image source } } getThumbs(); //calls the function that runs through the array and assigns shrunken images to the <img> in the html code

and the corresponding images that would take the new shrunken image:

<img id="img2" src="">

[quote=402374:@Ben Scofield]I assumed as much (image sizes) but was surprised at how poorly the HTML viewer performed compared to a browser.

In this case the cameras are from a niche industry (trail cameras) and definitely don’t have thumbnail files. It’s possible that JavaScript could do a better/quicker job at resizing images. I’ll look for a library that does it and see.[/quote]

The problem, I think, is that unless the images are pre-resized they still need to load completely into memory, and by then you’re already experiencing the problem (both in speed and RAM).

There’s also the added problem that rendering images “feels” slow. This in the past for web browsers (the 90s) was a big problem because while it may take a while to load images (over a modem), at least the main layout shouldn’t wait for that (it was common on tables to wait for all resources to be loaded so it knew what size the cells should be).

Back then this was solved by creating LOWSRC images that pre-load and complete the layout while the full images load (and later, until they needed to be loaded). Also common were tables with fixed widths and heights and nowadays it’s loading images on a separate thread asynchronously as page scrolls.

For the actual speed of loading images, there is little that can be done other than pre-resizing the images. Ben’s code takes care of this by generating thumbnails to feed the list. I would add here that Mac OS and possibly Windows have native ways to generate and load thumbnails that may be faster through declares (I imagine it’s faster for CoreImage in Mac OS to provide the same thumbnail the Finder would use than it is for Xojo to do the same, and it may already exist in some cases). I haven’t looked into these declares but I’d add to the method a way to first see if you can get a thumbnail from the OS through declares and only generate it yourself afterwards.

Here is a thread discussing thumbnail reading through Declares in Xojo: https://forum.xojo.com/13154-read-explorer-thumbnail/0

Also, several solutions here: https://forum.xojo.com/7258-best-xojo-thumbnail-class/0

In the end it’s a bit of a race: If Xojo can produce thumbnails faster than HTML can load the full images then pre-generating thumbnails should be better. If it’s slower, you’d be taking longer in displaying a smaller image.

(An advantage of pre-resizing is used RAM. If you load full-size images in HTMLVIewer it’ll take loads more RAM than if you use a thumbnail)

If you’re doing your own layout instead of using an existing javascript library, you can also optimize “feeling” of speed by settling on a pre-dimensioned grid (a table or a bunch of DIVs) that can be fully drawn while images load (using fixed widhts and heights).

Lastly: If you use a JavaScript library to generate thumbnails then the images will each be loaded fully and discarded. Total memory will be much lower until you load the full image, but generation speed should also be slower than pre-generating the images. You also wouldn’t have the option of storing the thumbnails for later, even temporarily).

(I just realized I always capitalize JavaScript in this way, and now I feel old)

[quote=402954:@Ben Scofield]

<img id="img2" src="">

Best practices are to pre-calculate the width and height of the image and include that in the tag so the HTML engine can do the layout before the images themselves are loaded. If you can do that it may help.

This would imply reading all the images in advance, which would in turn delay all drawing until all images have been pre-loaded.

An acceptable compromise is to predefine widths and heights or the cells/DIVS and this way the whole layout can be drawn before even the first image has been loaded. The image can be put into the div/cell without resizing it, so the layout is not affected.

This mostly affects tables in IE. Most other rendering engines start “painting” the layout and resize as needed, I think, which looks bad but is faster.

For the DIV or Cell this should be enough CSS to fit the image full height or full width:

max-width: 100%; and height: auto;

My img tags have a width set to 100% to stretch to the width of the table that they are contained in. The columns are using a % width and it stretches/resizes nicely with the xojo window.

What’s the width of the container? As long as the layout engine can figure out absolute dimensions the rest flows nicely. If it doesn’t, it’ll try to adjust minimum dimensions to the fixed-size elements. This is why in the past 1-pixel transparent gifs were a common way to force sizes.

I’m not saying this is your issue, by the way. Without seeing the HTML it’s impossible to diagnose. These are all just ideas on what to look for :slight_smile:

[quote=403436:@Eduardo Gutierrez de Oliveira]What’s the width of the container? As long as the layout engine can figure out absolute dimensions the rest flows nicely. If it doesn’t, it’ll try to adjust minimum dimensions to the fixed-size elements. This is why in the past 1-pixel transparent gifs were a common way to force sizes.

I’m not saying this is your issue, by the way. Without seeing the HTML it’s impossible to diagnose. These are all just ideas on what to look for :)[/quote]
Eduardo, I didn’t properly thank you for the very helpful replies earlier. I appreciate that very much. In the middle of this conversation and looking into Xojo image loads vs html/js image loads I realized something. I very large item on my roadmap moving forward is image recognition. There are several libraries available for js so I decided to try harder to work with that platform specifically for the image previewing process because it’s the logical section to include a note about what the image recognition engine found.

Even though right now I appreciate the simplicity with which the htmlviewer and the images expand when the xojo window has the size changed, I’m going to set some absolute dimensions on the table in HTML and see if this improves the load speeds. If it does I may just start with that as my new starting point and figure out how to work with resizing later.

Thanks again for you helpful answer above!

I’m really glad it was of use. :slight_smile: