compareLists function

Future<ComparisonResult> compareLists(
  1. List<int>? test,
  2. List<int>? master
)

Returns a ComparisonResult to describe the pixel differential of the test and master image bytes provided.

Implementation

Future<ComparisonResult> compareLists(List<int>? test, List<int>? master) async {
  if (test == null || master == null || test.isEmpty || master.isEmpty) {
    return ComparisonResult(
      passed: false,
      diffPercent: 1.0,
      error: 'Pixel test failed, null image provided.',
    );
  }

  if (listEquals(test, master)) {
    return ComparisonResult(
      passed: true,
      diffPercent: 0.0,
    );
  }

  final Codec testImageCodec =
      await instantiateImageCodec(Uint8List.fromList(test));
  final Image testImage = (await testImageCodec.getNextFrame()).image;
  final ByteData? testImageRgba = await testImage.toByteData();

  final Codec masterImageCodec =
      await instantiateImageCodec(Uint8List.fromList(master));
  final Image masterImage = (await masterImageCodec.getNextFrame()).image;
  final ByteData? masterImageRgba = await masterImage.toByteData();

  final int width = testImage.width;
  final int height = testImage.height;

  if (width != masterImage.width || height != masterImage.height) {
    final ComparisonResult result =  ComparisonResult(
      passed: false,
      diffPercent: 1.0,
      error: 'Pixel test failed, image sizes do not match.\n'
        'Master Image: ${masterImage.width} X ${masterImage.height}\n'
        'Test Image: ${testImage.width} X ${testImage.height}',
        diffs: <String, Image>{
          'masterImage': masterImage,
          'testImage': testImage,
        },
    );
    return result;
  }

  int pixelDiffCount = 0;
  final int totalPixels = width * height;
  final ByteData invertedMasterRgba = _invert(masterImageRgba!);
  final ByteData invertedTestRgba = _invert(testImageRgba!);

  final Uint8List testImageBytes = (await testImage.toByteData())!.buffer.asUint8List();
  final ByteData maskedDiffRgba = ByteData(testImageBytes.length);
  maskedDiffRgba.buffer.asUint8List().setRange(0, testImageBytes.length, testImageBytes);
  final ByteData isolatedDiffRgba = ByteData(width * height * 4);

  for (int x = 0; x < width; x++) {
    for (int y =0; y < height; y++) {
      final int byteOffset = (width * y + x) * 4;
      final int testPixel = testImageRgba.getUint32(byteOffset);
      final int masterPixel = masterImageRgba.getUint32(byteOffset);

      final int diffPixel = (_readRed(testPixel) - _readRed(masterPixel)).abs()
        + (_readGreen(testPixel) - _readGreen(masterPixel)).abs()
        + (_readBlue(testPixel) - _readBlue(masterPixel)).abs()
        + (_readAlpha(testPixel) - _readAlpha(masterPixel)).abs();

      if (diffPixel != 0 ) {
        final int invertedMasterPixel = invertedMasterRgba.getUint32(byteOffset);
        final int invertedTestPixel = invertedTestRgba.getUint32(byteOffset);
        // We grab the max of the 0xAABBGGRR encoded bytes, and then convert
        // back to 0xRRGGBBAA for the actual pixel value, since this is how it
        // was historically done.
        final int maskPixel = _toRGBA(math.max(
          _toABGR(invertedMasterPixel),
          _toABGR(invertedTestPixel),
        ));
        maskedDiffRgba.setUint32(byteOffset, maskPixel);
        isolatedDiffRgba.setUint32(byteOffset, maskPixel);
        pixelDiffCount++;
      }
    }
  }

  if (pixelDiffCount > 0) {
    final double diffPercent = pixelDiffCount / totalPixels;
    return ComparisonResult(
      passed: false,
      diffPercent: diffPercent,
      error: 'Pixel test failed, '
        '${(diffPercent * 100).toStringAsFixed(2)}%, ${pixelDiffCount}px '
        'diff detected.',
      diffs:  <String, Image>{
        'masterImage' : masterImage,
        'testImage' : testImage,
        'maskedDiff' : await _createImage(maskedDiffRgba, width, height),
        'isolatedDiff' : await _createImage(isolatedDiffRgba, width, height),
      },
    );
  }
  masterImage.dispose();
  testImage.dispose();
  return ComparisonResult(passed: true, diffPercent: 0.0);
}