Camera: support more variant of imageCopy/isImageStronglyEqual

Test: partner testing on testYuvImageWriterReaderOperation
Bug: 122707142
Change-Id: I387794cbb47e8c6184f47b462a557b392798aae6
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index d473975..47a5003 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -1868,7 +1868,7 @@
      * @param src The source image to be copied from.
      * @param dst The destination image to be copied to.
      * @throws IllegalArgumentException If the source and destination images have
-     *             different format, or one of the images is not copyable.
+     *             different format, size, or one of the images is not copyable.
      */
     public static void imageCopy(Image src, Image dst) {
         if (src == null || dst == null) {
@@ -1882,6 +1882,13 @@
             throw new IllegalArgumentException("PRIVATE format images are not copyable");
         }
 
+        Size srcSize = new Size(src.getWidth(), src.getHeight());
+        Size dstSize = new Size(dst.getWidth(), dst.getHeight());
+        if (!srcSize.equals(dstSize)) {
+            throw new IllegalArgumentException("source image size " + srcSize + " is different"
+                    + " with " + "destination image size " + dstSize);
+        }
+
         // TODO: check the owner of the dst image, it must be from ImageWriter, other source may
         // not be writable. Maybe we should add an isWritable() method in image class.
 
@@ -1891,16 +1898,100 @@
         ByteBuffer dstBuffer = null;
         for (int i = 0; i < srcPlanes.length; i++) {
             srcBuffer = srcPlanes[i].getBuffer();
+            dstBuffer = dstPlanes[i].getBuffer();
             int srcPos = srcBuffer.position();
             srcBuffer.rewind();
-            dstBuffer = dstPlanes[i].getBuffer();
             dstBuffer.rewind();
-            dstBuffer.put(srcBuffer);
+            int srcRowStride = srcPlanes[i].getRowStride();
+            int dstRowStride = dstPlanes[i].getRowStride();
+            int srcPixStride = srcPlanes[i].getPixelStride();
+            int dstPixStride = dstPlanes[i].getPixelStride();
+
+            if (srcPixStride > 2 || dstPixStride > 2) {
+                throw new IllegalArgumentException("source pixel stride " + srcPixStride +
+                        " with destination pixel stride " + dstPixStride +
+                        " is not supported");
+            }
+
+            if (srcRowStride == dstRowStride && srcPixStride == dstPixStride) {
+                // Fast path, just copy the content in the byteBuffer all together.
+                dstBuffer.put(srcBuffer);
+            } else {
+                Size effectivePlaneSize = getEffectivePlaneSizeForImage(src, i);
+                int srcRowByteCount = srcRowStride;
+                byte[] srcDataRow = new byte[srcRowByteCount];
+
+                if (srcPixStride == dstPixStride) {
+                    // Row by row copy case
+                    for (int row = 0; row < effectivePlaneSize.getHeight(); row++) {
+                        if (row == effectivePlaneSize.getHeight() - 1) {
+                            // Special case for interleaved planes: need handle the last row
+                            // carefully to avoid memory corruption. Check if we have enough bytes
+                            // to copy.
+                            int remainingBytes = srcBuffer.remaining();
+                            if (srcRowByteCount > remainingBytes) {
+                                srcRowByteCount = remainingBytes;
+                            }
+                        }
+                        srcBuffer.get(srcDataRow, /*offset*/0, srcRowByteCount);
+                        dstBuffer.put(srcDataRow, /*offset*/0, srcRowByteCount);
+                    }
+                } else {
+                    // Row by row per pixel copy case
+                    int dstRowByteCount = dstRowStride;
+                    byte[] dstDataRow = new byte[dstRowByteCount];
+                    for (int row = 0; row < effectivePlaneSize.getHeight(); row++) {
+                        if (row == effectivePlaneSize.getHeight() - 1) {
+                            // Special case for interleaved planes: need handle the last row
+                            // carefully to avoid memory corruption. Check if we have enough bytes
+                            // to copy.
+                            int remainingBytes = srcBuffer.remaining();
+                            if (srcRowByteCount > remainingBytes) {
+                                srcRowByteCount = remainingBytes;
+                            }
+                            remainingBytes = dstBuffer.remaining();
+                            if (dstRowByteCount > remainingBytes) {
+                                dstRowByteCount = remainingBytes;
+                            }
+                        }
+                        srcBuffer.get(srcDataRow, /*offset*/0, srcRowByteCount);
+                        int pos = dstBuffer.position();
+                        dstBuffer.get(dstDataRow, /*offset*/0, dstRowByteCount);
+                        dstBuffer.position(pos);
+                        for (int x = 0; x < effectivePlaneSize.getWidth(); x++) {
+                            dstDataRow[x * dstPixStride] = srcDataRow[x * srcPixStride];
+                        }
+                        dstBuffer.put(dstDataRow, /*offset*/0, dstRowByteCount);
+                    }
+                }
+            }
             srcBuffer.position(srcPos);
             dstBuffer.rewind();
         }
     }
 
+    private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
+        switch (image.getFormat()) {
+            case ImageFormat.YUV_420_888:
+                if (planeIdx == 0) {
+                    return new Size(image.getWidth(), image.getHeight());
+                } else {
+                    return new Size(image.getWidth() / 2, image.getHeight() / 2);
+                }
+            case ImageFormat.JPEG:
+            case ImageFormat.RAW_SENSOR:
+            case ImageFormat.RAW10:
+            case ImageFormat.RAW12:
+            case ImageFormat.DEPTH16:
+                return new Size(image.getWidth(), image.getHeight());
+            case ImageFormat.PRIVATE:
+                return new Size(0, 0);
+            default:
+                throw new UnsupportedOperationException(
+                        String.format("Invalid image format %d", image.getFormat()));
+        }
+    }
+
     /**
      * <p>
      * Checks whether the two images are strongly equal.
@@ -1964,9 +2055,42 @@
         for (int i = 0; i < lhsPlanes.length; i++) {
             lhsBuffer = lhsPlanes[i].getBuffer();
             rhsBuffer = rhsPlanes[i].getBuffer();
-            if (!lhsBuffer.equals(rhsBuffer)) {
-                Log.i(TAG, "byte buffers for plane " +  i + " don't matach.");
-                return false;
+            lhsBuffer.rewind();
+            rhsBuffer.rewind();
+            // Special case for YUV420_888 buffer with different chroma layout
+            if (lhsImg.getFormat() == ImageFormat.YUV_420_888 && (i != 0) &&
+                    (lhsPlanes[i].getPixelStride() != rhsPlanes[i].getPixelStride() ||
+                     lhsPlanes[i].getRowStride() != rhsPlanes[i].getRowStride())) {
+                int width = lhsImg.getWidth() / 2;
+                int height = lhsImg.getHeight() / 2;
+                int rowSizeL = lhsPlanes[i].getRowStride();
+                int rowSizeR = rhsPlanes[i].getRowStride();
+                byte[] lhsRow = new byte[rowSizeL];
+                byte[] rhsRow = new byte[rowSizeR];
+                int pixStrideL = lhsPlanes[i].getPixelStride();
+                int pixStrideR = rhsPlanes[i].getPixelStride();
+                for (int r = 0; r < height; r++) {
+                    if (r == height -1) {
+                        rowSizeL = lhsBuffer.remaining();
+                        rowSizeR = rhsBuffer.remaining();
+                    }
+                    lhsBuffer.get(lhsRow, /*offset*/0, rowSizeL);
+                    rhsBuffer.get(rhsRow, /*offset*/0, rowSizeR);
+                    for (int c = 0; c < width; c++) {
+                        if (lhsRow[c * pixStrideL] != rhsRow[c * pixStrideR]) {
+                            Log.i(TAG, String.format(
+                                    "byte buffers for plane %d row %d col %d don't match.",
+                                    i, r, c));
+                            return false;
+                        }
+                    }
+                }
+            } else {
+                // Compare entire buffer directly
+                if (!lhsBuffer.equals(rhsBuffer)) {
+                    Log.i(TAG, "byte buffers for plane " +  i + " don't match.");
+                    return false;
+                }
             }
         }