How to cache a bitmap

I'm working on one of these sites where you can stop playback by scrolling, like Sony Move .

The problem I ran into considering the freeze frame technique is the time it takes to rasterize an image before the browser draws it to the screen. It takes a lot on mobile. Resizing the image probably takes up most of the CPU, but I'm not sure. This is how I display the frames:

<div 
  style="
    position: fixed;
    top:0; right:0; bottom:0; left:0;
    background-image: url(...);
    background-position: center;
    background-size: cover;
  "
></div>

      

Question:

Is there a way to cache a rasterized version of an image? Maybe the canvas supports this? That way, when I decide to show it on the screen, it's ready.

Right now, this is the only way I know how to cache an image.

var image = new Image();
image.src = '...';

      

+3


source to share


2 answers


Ref comments - there is a way to pre-cache video frames. Each frame will use a full block of memory for the bitmap (which is also the case with preloaded image sequences anyway).

Caching process

  • Create video element "off-line"
  • Set video source from preload

    toauto

  • You need to know the frame rate (typical: 30fps for USA / Japan, 25fps for Europe), calculate the time delta based on that, i.e. 1 / FPS

    ...
  • Use an event timeupdate

    for each update currentTime

    , since the current install time is asynchronous.

Select a point in the video, cache (this may take a while due to the event loop), save to a frame buffer using a canvas element for each frame. Then play the buffer as needed (this also gives you the ability to play the video backwards like below, not yet supported in browsers).



Example

This example will load the video from the network, cache 90 (3s 30fps) into memory, and then play the ping-pong alternation in the window (the images you see from the cache are obvious):

var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    video = document.createElement("video"),
    frames = [],
    w = canvas.width, h = canvas.height;

video.addEventListener("canplay", cache);
video.preload = "auto";
video.src = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";

function cache() {
  this.removeEventListener("canplay", cache);         // remove to avoid recalls

  var fps = 30,                                       // assuming 30 FPS
      delta = 1 / fps,                                // time delta
      count = 0,                                      // current cached frame
      max = fps * 3,                                  // 3 seconds
      div = document.querySelector("div");            // just for info

  this.addEventListener("timeupdate", cacheFrame);    // time update is aync
  this.currentTime = 19;                              // start with initial time
  
  function cacheFrame() {
    div.innerHTML = "Caching frame: " + count;
    
    if (count++ < max) {
      
      // create canvas for frame-buffer;
      var canvas = document.createElement("canvas"),
          ctx = canvas.getContext("2d");

      canvas.width = this.videoWidth;                 // canvas size = video frame
      canvas.height = this.videoHeight;
      
      ctx.drawImage(video, 0, 0);                     // draw current frame
      frames.push(canvas);                            // store frame
      
      this.currentTime += delta;                      // update time, wait..
    }
    else {
      this.removeEventListener("timeupdate", cacheFrame); // remove!!
      play();                                         // play back cached sequence
    }
  }
}

// to demo the cached frames
function play() {
  var current = 0, max = frames.length, dlt = 1,
      div = document.querySelector("div"),
      toggle = false,
      mem = max * video.videoWidth * video.videoHeight * 4; // always RGBA
 
  mem = (mem / 1024) / 1024;                          //mb

  ctx.fillStyle = "red";

  (function loop() {
    toggle = !toggle;                                 // toggle FPS to 30 FPS
    requestAnimationFrame(loop);
    
    if (toggle) {
      div.innerHTML = "Playing frame: " + current + 
                      " (raw mem: " + mem.toFixed(1) + " mb)";

      ctx.drawImage(frames[current], 0, 0, w, h);     // using frame-buffer
      ctx.fillRect(0, 0, current/max * w, 3);
      
      current += dlt;
      if (!current || current === max-1) dlt = -dlt;  // pong-pong
    }
  })();
}
      

html, body {width:100%;height:100%}
body {margin:0; overflow:hidden;background:#aaa}
div {font:bold 20px monospace;padding:12px;color:#000}
canvas {z-index:-1;position:fixed;left:0;top:0;width:100%;height:100%;min-height:400px}
      

<div>Pre-loading video... wait for it, wait for it...</div>
<canvas width=600 height=360></canvas>
      

Run codeHide result


+4


source


Sources of canvas drawing are image and video objects (and some other sources that are not currently relevant). So if your unwanted lag occurs during bootstrapping and rendering, then the canvas will take longer, because the incoming image must first be mapped to the image object and then mapped back to the canvas - two steps instead of one.

Your answer is not in the canvas element, so you fall back to the usual solution: reduce the number of image bits being loaded by lowering the quality of your images (jpg with lower quality).



You can also (as you pointed out) preload and cache all your images in new Image

so that they can be used immediately when needed. Typical cost: Increased memory usage for cached images and a delay at the start of your application while all the required images are loaded.

+1


source







All Articles