anchor.diff:    _ImageMatcher(expected,
         [this._mismatchThreshold = 0, this._rgbaThreshold = 0])
         : _expected = expected,
  -        _expectedPixelData = _resolvePixelData(expected);
  +        _expectedCanvas = _resolveCanvas(expected);
   
     bool matches(item, Map matchState) {
  -    var pixelData;
  +    var canvas;
   
       try {
  -      pixelData = _resolvePixelData(item);
  +      canvas = _resolveCanvas(item);
       } catch (e) {
         return false;
       }
   
  -    final width = pixelData.width;
  -    final height = pixelData.height;
  +    final width = canvas.width;
  +    final height = canvas.height;
  +    final context = canvas.context2D;
  +    final imageData = context.getImageData(0, 0, width, height);
   
  -    if (width != _expectedPixelData.width) {
  +    if (width != _expectedCanvas.width) {
         return false;
  -    } else if (height != _expectedPixelData.height) {
  +    } else if (height != _expectedCanvas.height) {
         return false;
       } else {
  -      final data = pixelData.data;
  -      final expectedData = _expectedPixelData.data;
  +      final data = imageData.data;
  +      final expectedData =
  +          _expectedCanvas.context2D.getImageData(0, 0, width, height).data;
         final pixelCount = width * height;
  +      final diffData = new Uint8ClampedList(width * height * 4);
         var mismatchCount = 0;
   
         for (var i = 0; i < height; i++) {
  
positive.code: 
    Description describeMismatch(
        item, Description mismatchDescription, Map matchState, bool verbose) {
      if (item is ImageElement || item is CanvasElement || item is ImageData) {
        final pixelData = _resolvePixelData(item);
  
        if (pixelData.width != _expectedPixelData.width) {
          return mismatchDescription.add('does not have the expected width.');
        } else if (pixelData.height != _expectedPixelData.height) {
          return mismatchDescription.add('does not have the expected width.');
        } else {
          final mismatchRatio = matchState['mismatchRatio'] ?? 0;
  
          return mismatchDescription
              .add('differs at ${mismatchRatio * 100}% of pixels.');
        }
      } else {
        return mismatchDescription
            .add('is not a canvas element, image element, or image data');
      }
    }
  }
  
  class _PixelData {
    final Uint8List data;
  
    final int width;
  
    final int height;
  
    _PixelData(this.width, this.height, this.data);
  }
  
  _PixelData _resolvePixelData(value) {
    if (value is ImageData) {
      return new _PixelData(value.width, value.height, value.data);
    } else if (value is CanvasElement) {
      final width = value.width;
      final height = value.height;
  
      if (value.context2D != null) {
        final imageData = value.context2D.getImageData(0, 0, width, height);
        final data = new Uint8List.fromList(imageData.data);
  
        return new _PixelData(width, height, data);
      } else {
        final canvas = new CanvasElement(width: width, height: height);
        final context = canvas.context2D;
  
        context.drawImage(value, 0, 0);
  
        final imageData = context.getImageData(0, 0, width, height);
        final data = new Uint8List.fromList(imageData.data);
  
        return new _PixelData(width, height, data);
      }
    } else if (value is ImageElement) {
      final width = value.naturalWidth;
  
negative.code: 
            if (mismatch) {
              mismatchCount++;
  
              diffData[(i * width + j) * 4] = 256;
              diffData[(i * width + j) * 4 + 2] = 256;
              diffData[(i * width + j) * 4 + 3] = 256;
            }
          }
        }
  
        final mismatchRatio = mismatchCount / pixelCount;
        final diffImageData = new ImageData(diffData, width, height);
        final diff = new CanvasElement(width: width, height: height);
  
        diff.context2D.putImageData(diffImageData, 0, 0);
  
        matchState['mismatchRatio'] = mismatchRatio;
        matchState['diff'] = diff;
  
        if (mismatchCount / pixelCount < _mismatchThreshold) {
          return true;
  