antimatter15 / jsgif

Save a HTML5 Canvas to GIF and Animations. A port of as3gif GIFPlayer to JS
http://antimatter15.com/
MIT License
1.07k stars 138 forks source link

Animated Gif comes out black w/ Web Workers #24

Closed jodyheavener closed 9 years ago

jodyheavener commented 9 years ago

I'm trying to use Web Workers to handle the Gif creation, and while I could get animated Gif generation working without using the worker, once I moved everything over to the worker I can only get a black image with the specified dimensions as the output.

In my main JS:

this.gifWorker = new Worker("scripts/worker.js");

... context setup ...

toggleSnapshotRecord() {
  this.isRecording = !this.isRecording;
  $(document.body).toggleClass("is-recording");

  let interval = (this.captureSecondsDelay - 1) * 1000;
  let snapCount = 0;

  if (this.isRecording) {
    this.snapshotInterval = setInterval(() => {
      // Capture context image data at a specified interval
      // until the desired frame count is met
      this.snapshotFrames.push(this.canvasContext.getImageData(0, 0, this.outputWidth, this.outputHeight));
      snapCount = snapCount + 1;
      if (snapCount === this.captureFramesNumber)
        return this.toggleSnapshotRecord();
    }, interval);
  } else {
    clearInterval(this.snapshotInterval);

    // Send the frames and the canvas dimensions
    // to our web worker
    this.gifWorker.postMessage({
      size: [this.outputWidth, this.outputHeight],
      frames: this.snapshotFrames
    });

    this.gifWorker.onmessage = function(event) {
      console.log(event.data);
    }
  };
};

In my Web Worker:

self.addEventListener("message", function(event) {
  var data = event.data;
  var encoder = new GIFEncoder();

  encoder.setRepeat(0);
  encoder.setDelay(300);
  encoder.setSize(data.size[0], data.size[1]);
  encoder.start();
  encoder.addFrame(data.frames, true);
  encoder.finish();

  self.postMessage("data:image/gif;base64," + encode64(encoder.stream().getData()));
}, false);

What would I be missing here? Any help is much appreciated!

jodyheavener commented 9 years ago

Also tried posting to the web worker on each interval:

Main JS

toggleSnapshotRecord() {
  this.isRecording = !this.isRecording;
  $(document.body).toggleClass("is-recording");

  let interval = (this.captureSecondsDelay - 1) * 1000;
  let snapCount = 0;

  if (this.isRecording) {
    this.snapshotInterval = setInterval(() => {
      let messageData = {
        size: [this.outputWidth, this.outputHeight],
        frame: this.canvasContext.getImageData(0, 0, this.outputWidth, this.outputHeight)
      };

      if (snapCount === 0)
        messageData["start"] = true;

      if (snapCount === this.captureFramesNumber - 1)
        messageData["finish"] = true;

      this.gifWorker.postMessage(messageData);

      snapCount = snapCount + 1;
      if (snapCount === this.captureFramesNumber)
        return this.toggleSnapshotRecord();
    }, interval);
  } else {
    clearInterval(this.snapshotInterval);

    this.gifWorker.onmessage = function(event) {
      console.log(event.data);
    };
  };
};

Web Worker

var encoder = new GIFEncoder();
    encoder.setRepeat(0);
    encoder.setDelay(300);

self.addEventListener("message", function(event) {
  var data = event.data;

  if (data.start) {
    encoder.start();
  } else {
    encoder.setProperties(true, true);
  }

  encoder.setSize(data.size[0], data.size[1]);
  encoder.addFrame(data.frame, true);

  if (data.finish) {
    encoder.finish();
    self.postMessage("data:image/gif;base64," + encode64(encoder.stream().getData()));
  }
}, false);
Joncom commented 9 years ago

Does the black output GIF at least appear to have the correct number of frames?

jodyheavener commented 9 years ago

Negative. It looks to be just the one frame (upon inspection in Apple Preview).

jodyheavener commented 9 years ago

Got it. In passing data.frame to encoder.addFrame() I was passing an ImageData object to it, when I should have been passing the ImageData's data – all in all I just had to change it to encoder.addFrame(data.frame.data, true).

jodyheavener commented 9 years ago

download

claybudin commented 8 years ago

Why are you calling:

encoder.setProperties(true, true);

? It seems like those are internal parameters managed by the encoder and don't need to be changed.

Also it looks like the setSize() call can be moved into the if (data.start) { ... } area.

Clay Budin NYC