Lazily create diff array, only when there is a diff.

Bug: 301261494
Test: all matcher tests
Change-Id: Ie67dcd6352e1046ac9838fae85bf8048ede1ce71
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/AlmostPerfectMatcher.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/AlmostPerfectMatcher.kt
index 2c96faf..588d926 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/AlmostPerfectMatcher.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/AlmostPerfectMatcher.kt
@@ -42,7 +42,9 @@
         var same = 0
         var ignored = 0
 
-        val diffArray = IntArray(width * height) { index ->
+        val diffArray = lazy { IntArray(width * height) { Color.TRANSPARENT } }
+
+        expected.indices.forEach { index ->
             when {
                 !filter[index] -> Color.TRANSPARENT.also { ignored++ }
                 areSame(expected[index], given[index]) -> Color.TRANSPARENT.also { same++ }
@@ -59,7 +61,7 @@
                 .build()
 
         if (different > (acceptableThreshold * width * height)) {
-            val diff = Bitmap.createBitmap(diffArray, width, height, Bitmap.Config.ARGB_8888)
+            val diff = Bitmap.createBitmap(diffArray.value, width, height, Bitmap.Config.ARGB_8888)
             return MatchResult(matches = false, diff = diff, comparisonStatistics = stats)
         }
         return MatchResult(matches = true, diff = null, comparisonStatistics = stats)
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/HumanEyeMatcher.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/HumanEyeMatcher.kt
index fd646df..c5956f7 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/HumanEyeMatcher.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/HumanEyeMatcher.kt
@@ -56,15 +56,19 @@
         fun isIndexSameForLargeArea(index: Int) = isSameForLargeArea(colorDiffArray[index])
 
         if (!accountForGrouping) {
-            val diffArray =
-                IntArray(width * height) { index ->
-                    if (isIndexSameForLargeArea(index)) Color.TRANSPARENT else Color.MAGENTA
+            val diffArray = lazy { IntArray(width * height) { Color.TRANSPARENT } }
+            var different = 0
+            expected.indices.forEach { index ->
+                if (!isIndexSameForLargeArea(index)) {
+                    diffArray.value[index] = Color.MAGENTA
+                    different++
                 }
+            }
             return createMatchResult(
                 width,
                 height,
-                diffArray.count { diff -> diff == Color.TRANSPARENT } - ignored,
-                diffArray.count { diff -> diff == Color.MAGENTA },
+                width * height - ignored - different,
+                different,
                 ignored,
                 diffArray,
             )
@@ -82,42 +86,40 @@
             }
         }
 
-        val diffArray =
-            IntArray(colorDiffArray.size) { index ->
-                // Also covers the ignored case
-                if (isIndexSameForLargeArea(index)) return@IntArray Color.TRANSPARENT
+        var different = 0
+        val diffArray = lazy { IntArray(colorDiffArray.size) { Color.TRANSPARENT } }
+        colorDiffArray.indices.forEach { index ->
+            // Also covers the ignored case
+            if (isIndexSameForLargeArea(index)) return@forEach
 
-                val x = index % width
-                val y = index / width
+            val x = index % width
+            val y = index / width
 
-                val currThreshold = getEasiestThresholdFailed(x, y)!!
-                // null = ignored or out of bounds of image
-                val upThreshold = if (y > 0) getEasiestThresholdFailed(x, y - 1) else null
-                val downThreshold =
-                    if (y < height - 1) getEasiestThresholdFailed(x, y + 1) else null
-                val leftThreshold = if (x > 0) getEasiestThresholdFailed(x - 1, y) else null
-                val rightThreshold =
-                    if (x < width - 1) getEasiestThresholdFailed(x + 1, y) else null
+            val currThreshold = getEasiestThresholdFailed(x, y)!!
+            // null = ignored or out of bounds of image
+            val upThreshold = if (y > 0) getEasiestThresholdFailed(x, y - 1) else null
+            val downThreshold = if (y < height - 1) getEasiestThresholdFailed(x, y + 1) else null
+            val leftThreshold = if (x > 0) getEasiestThresholdFailed(x - 1, y) else null
+            val rightThreshold = if (x < width - 1) getEasiestThresholdFailed(x + 1, y) else null
 
-                // Pixels with lower diff thresholds are not counted as neighbouring diffs
-                var neighbouringDiffs = 4
-                if (upThreshold != null && currThreshold > upThreshold) neighbouringDiffs--
-                if (downThreshold != null && currThreshold > downThreshold) neighbouringDiffs--
-                if (leftThreshold != null && currThreshold > leftThreshold) neighbouringDiffs--
-                if (rightThreshold != null && currThreshold > rightThreshold) neighbouringDiffs--
+            // Pixels with lower diff thresholds are not counted as neighbouring diffs
+            var neighbouringDiffs = 4
+            if (upThreshold != null && currThreshold > upThreshold) neighbouringDiffs--
+            if (downThreshold != null && currThreshold > downThreshold) neighbouringDiffs--
+            if (leftThreshold != null && currThreshold > leftThreshold) neighbouringDiffs--
+            if (rightThreshold != null && currThreshold > rightThreshold) neighbouringDiffs--
 
-                if (isSame(colorDiffArray[index], neighbouringDiffs)) {
-                    Color.TRANSPARENT
-                } else {
-                    Color.MAGENTA
-                }
+            if (!isSame(colorDiffArray[index], neighbouringDiffs)) {
+                diffArray.value[index] = Color.MAGENTA
+                different++
             }
+        }
 
         return createMatchResult(
             width,
             height,
-            diffArray.count { diff -> diff == Color.TRANSPARENT } - ignored,
-            diffArray.count { diff -> diff == Color.MAGENTA },
+            width * height - ignored - different,
+            different,
             ignored,
             diffArray,
         )
@@ -197,7 +199,7 @@
         samePixels: Int,
         differentPixels: Int,
         ignoredPixels: Int,
-        diffBitmapArray: IntArray,
+        diffBitmapArray: Lazy<IntArray>,
     ): MatchResult {
         val stats =
             ScreenshotResultProto.DiffResult.ComparisonStatistics.newBuilder()
@@ -208,7 +210,8 @@
                 .build()
 
         return if (differentPixels > 0) {
-            val diff = Bitmap.createBitmap(diffBitmapArray, width, height, Bitmap.Config.ARGB_8888)
+            val diff =
+                Bitmap.createBitmap(diffBitmapArray.value, width, height, Bitmap.Config.ARGB_8888)
             MatchResult(matches = false, diff = diff, comparisonStatistics = stats)
         } else {
             MatchResult(matches = true, diff = null, comparisonStatistics = stats)
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/PixelPerfectMatcher.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/PixelPerfectMatcher.kt
index 2eb3ef8..875716e 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/PixelPerfectMatcher.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/matchers/PixelPerfectMatcher.kt
@@ -41,11 +41,13 @@
         var same = 0
         var ignored = 0
 
-        val diffArray = IntArray(width * height) { index ->
+        val diffArray = lazy { IntArray(width * height) { Color.TRANSPARENT } }
+
+        expected.indices.forEach { index ->
             when {
-                !filter[index] -> Color.TRANSPARENT.also { ignored++ }
-                expected[index] == given[index] -> Color.TRANSPARENT.also { same++ }
-                else -> Color.MAGENTA.also { different++ }
+                !filter[index] -> ignored++
+                expected[index] == given[index] -> same++
+                else -> diffArray.value[index] = Color.MAGENTA.also { different++ }
             }
         }
 
@@ -58,7 +60,7 @@
             .build()
 
         return if (different > 0) {
-            val diff = Bitmap.createBitmap(diffArray, width, height, Bitmap.Config.ARGB_8888)
+            val diff = Bitmap.createBitmap(diffArray.value, width, height, Bitmap.Config.ARGB_8888)
             MatchResult(matches = false, diff = diff, comparisonStatistics = stats)
         } else {
             MatchResult(matches = true, diff = null, comparisonStatistics = stats)