anchor.diff:    /**
      * Pads the color table with zeros to the next power of 2 and sets bits.
      */
  -  void finish() {
  +  _ColorTable build() {
  +    return new _ColorTable(this);
  +  }
  +}
  +
  +class _ColorTable {
  +  final List<int> table = new List<int>();
  +  final colorToIndex = new Map<int, int>();
  +  int bits;
  +  
  +  _ColorTable(_ColorTableBuilder builder) {
  +    table.addAll(builder.table);
  +    colorToIndex.addAll(builder.colorToIndex);
       for (int bits = 1;; bits++) {
         int colors = 1 << bits;
         if (colors * 3 >= table.length) {
  
positive.code: class GifBuffer {
    final int width;
    final int height;
    final List<List<int>> _frames = new List<List<int>>();
    
    /// Creates an animation of the specified width and height, with zero frames.
    GifBuffer(this.width, this.height);
    
    /**
     * Adds a frame to the animation. The pixels are specified as rgba data but the alpha channel is
     * ignored.
     */
    void add(List<int> rgba) {
      _frames.add(rgba);  
    }
    
    /// Returns the bytes of an animated GIF.
    Uint8List build(int framesPerSecond) {
      return new _IndexedAnimation(width, height, _frames).encodeGif(framesPerSecond); 
    }
  }
  
  const maxColorBits = 7;
  const maxColors = 1<<maxColorBits;
  
  class _IndexedImage {
    final int width;
    final int height;
    final colors = new _ColorTable();
    List<int> pixels;
  
    _IndexedImage(this.width, this.height, List<int> rgba) {   
      pixels = colors.indexImage(width, height, rgba);    
      colors.finish();
    }
  
    Uint8List encodeGif() {
      return new Uint8List.fromList(
          _header(width, height, colors.bits)
          ..addAll(colors.table)
          ..addAll(_startImage(0, 0, width, height))
          ..addAll(lzw.compress(pixels, colors.bits))
          ..addAll(_trailer()));
    }
  }
  
  /// An animation with a restricted palette.
  class _IndexedAnimation {
    final int width;
    final int height;
    final colors = new _ColorTable();
    final frames = new List<List<int>>();
  
    /**
     * Builds an indexed image from a set of frames. Each frame contains rgba data for the pixels,
     * but the alpha channel is ignored.
     * Throws an exception if the the image has too many colors.
     * (The input format is the same used by the ImageData class, which can be created
     * from a canvas element.)
     */
    _IndexedAnimation(this.width, this.height, Iterable<List<int>> rgbaFrames) {
      for (var frame in rgbaFrames) {
        frames.add(colors.indexImage(width, height, frame));      
      }
      colors.finish();
    }
  
    /**
     * Converts the animation into an uncompressed GIF, represented as a list of bytes.
     */
    Uint8List encodeGif(int fps) {
      int delay = 100 ~/ fps;
      if (delay < 6) {
        delay = 6; // http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser-compatibility
      }
  
negative.code:   }
  }
  
  const maxColorBits = 7;
  const maxColors = 1<<maxColorBits;
  
  class _IndexedImage {
    final int width;
    final int height;
    _ColorTable colors;
    List<int> pixels;
  
    _IndexedImage(this.width, this.height, List<int> rgba) {   
      var builder = new _ColorTableBuilder();
      pixels = builder.indexImage(width, height, rgba);    
      colors = builder.build();
    }
  
    Uint8List encodeGif() {
      return new Uint8List.fromList(
          _header(width, height, colors.bits)
          ..addAll(colors.table)
          ..addAll(_startImage(0, 0, width, height))
          ..addAll(lzw.compress(pixels, colors.bits))
          ..addAll(_trailer()));
    }
  }
  
  class _ColorTableBuilder {
    final List<int> table = new List<int>();
    final colorToIndex = new Map<int, int>();
    int bits;
  