Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cache image rasterized

I'm doing one of them stop-shot-scroll-controlled-playback sites like Sony's Be Moved.

The problem that I'm facing, considering the stop-shot technique, is the time that it takes for image to be rasterized before browser draws it on screen. It takes a lot on mobile. Probably resizing the image takes most of the cpu, but I'm not sure. This is how I show the frames:

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

The question:

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

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

var image = new Image();
image.src = '...';
like image 954
Daniel Birowsky Popeski Avatar asked Oct 29 '25 09:10

Daniel Birowsky Popeski


1 Answers

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

Cache Process

  • Create an "off-line" video element
  • Set video source with preload set to auto
  • You need to know the frame rate (typical: 30 fps for USA/Japan, 25 fps for Europe), calculate a time delta based on this, ie. 1 / FPS.
  • Use the timeupdate event for every currentTime update as setting current time is asynchronous.

Chose an in-point in the video, cache (this can take a while due to the event cycle), store to a frame buffer using a canvas element for each frame. Then playback the buffer when and as needed (this also gives you the ability to play video backwards as shown below, a feature not yet supported in the browsers).

Example

This example will load a video from net, cache 90 (3 sec @ 30 fps) frames to memory, then play back the sequence ping-pong in the window (the images you see are from the cache obviously):

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>

Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!