I am working on a "CAD" like program in HTML5 that requires drawing many thousands (700,000+) of images on the screen. I think, "fortunately" the many images are replicated uses of a unique set of images. The unique set is many orders of magnitude less than the instances of them. One example I am working with has 700,000+ instances from a set of 360 unique images. The images are actually generated on the fly when the program first loads and they are rectangles with the "name" of the image centered about the middle of the rectangle (no color within the rectangle, etc... just a rectangle outline with the text in the middle, text and rectangle outline is black). The program should be able to display the images at many magnification levels, and of course, be fast enough for it to be useful.

A first implementation worked well with SVG images loaded. Due to the vector aspect of the images, it worked nicely at all magnifications. But after about 70,000 or so instances the program started to run out of memory. I assume because the DOM is not able to keep up with so many instances...

After a lot of studying and experimentation, I was able to implement a working version of the program using WebGL. It works pretty well, and it is fast. I know I will have to implement all the fun things I got from SVG for free, like CSS colors, listeners, individual elements in the DOM, etc, but I was not using a lot of that anyhow.

So far I've been able to implement a system that can render a lot of different instances of a subset of rectangles using a shape atlas with 1 texture and then using the standard technique of passing the texture coordinates to the pixel shader for rendering. This worked OK, except that the strings in the center of the atlas shape did not look too good at all magnifications. For the program to be most useful, I need to be able to display the strings clearly. Generating the rectangle atlases from a canvas 2d, even at high resolutions, with the strings as part of the atlas rectangles, while quite straight forward to do, did not seem to render well at all magnifications. I looked into generating string atlases by themselves, which is fine, but it seems to me that I should be able to create an image atlas that can display pretty well at a reasonable number of magnifications including the text, and perhaps save a bunch of unique draw calls for simply generating the text using Signed Distance Fields.

This is where I have hit a wall... I found a few places that reference the paper "Improved Alpha-Tested Magnification for Vector Textures and Special Effects". It seems exactly what I am looking for. I think of my unique set of rectangle, text pairs as "decals" as outlined in the paper above. In my case, I would have all my unique instances generate a lower resolution SDF version (64x64) of a high resolution version (2048x2048) of the instances and then change my fragment shader to be able to render at any resolution as outlined here -> Drawing Text with Signed Distance Fields in Mapbox GL. However, I have not found anywhere that shows how to generate a lower resolution version SDF from a high resolution image.

My questions are the following:

  1. How do I generate a lower resolution SDF version (64x64) of a hi-res image (2048x2048) generated on the fly using canvas and JavaScript so I can create a texture to use in my fragment shader? Ideally, this would only have to be generated once at the start of the program.

  2. Am I thinking about his correctly? Is the text doomed to have to be drawn by itself separately using an SDF font atlas?

  • 1
    $\begingroup$ For generating SDFs, you can brute force it doing something like: For every empty pixel, find the nearest 'solid' pixel by searching the entire bitmap (smallest distance wins). Not a cheap method but plenty of room of optimisation later. For downscaling you should be taking averages of distance over the area you are sampling from. $\endgroup$
    – PaulHK
    Feb 21 '17 at 4:13
  • $\begingroup$ Will using ctx.drawImage would work for the downscaling? $\endgroup$
    – Jose
    Feb 21 '17 at 4:55
  • $\begingroup$ Also, how do I normalize the distance representation into apha values $\endgroup$
    – Jose
    Feb 21 '17 at 7:10
  • $\begingroup$ If you are encoding as 8bit alpha you would normalise with something like Alpha[n] = distField[n] * 255 / MaxDist; were MaxDist is the largest distance in your field, this requires you to pass MaxDist as a uniform to your shader so it can unpack your alpha buffer properly. You could maybe get away with not not needing MaxDist if you have fixed texture dimensions. Ideally you should be using a floating point format though. $\endgroup$
    – PaulHK
    Feb 21 '17 at 7:16
  • $\begingroup$ Hi Paul, thank you for your answer. Can you elaborate on the downscaling? $\endgroup$
    – Jose
    Feb 22 '17 at 18:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.