Reduce screenshot size for failures

Introduce support to JPEG and GIF format.
Resize image to half if bigger than 720p.
Keep default behavior of PNG images.

Bug: 24515441
Change-Id: Idfde33df038038f46fb908ca09288ddfc7ecfe67
diff --git a/prod-tests/src/com/android/monkey/MonkeyBase.java b/prod-tests/src/com/android/monkey/MonkeyBase.java
index 29eae08..3dba973 100644
--- a/prod-tests/src/com/android/monkey/MonkeyBase.java
+++ b/prod-tests/src/com/android/monkey/MonkeyBase.java
@@ -401,9 +401,9 @@
     protected void takeScreenshot(ITestInvocationListener listener, String screenshotName)
             throws DeviceNotAvailableException {
         if (mScreenshot) {
-            InputStreamSource screenshot = mTestDevice.getScreenshot();
+            InputStreamSource screenshot = mTestDevice.getScreenshot("JPEG");
             try {
-                listener.testLog(screenshotName, LogDataType.PNG, screenshot);
+                listener.testLog(screenshotName, LogDataType.JPEG, screenshot);
             } finally {
                 screenshot.cancel();
             }
diff --git a/src/com/android/tradefed/device/ITestDevice.java b/src/com/android/tradefed/device/ITestDevice.java
index dc92712..2b95113 100644
--- a/src/com/android/tradefed/device/ITestDevice.java
+++ b/src/com/android/tradefed/device/ITestDevice.java
@@ -781,6 +781,16 @@
     public InputStreamSource getScreenshot() throws DeviceNotAvailableException;
 
     /**
+     * Grabs a screenshot from the device.
+     * Recommended to use getScreenshot(format) instead with JPEG encoding for smaller size
+     * @param format supported PNG, JPEG
+     * @return a {@link InputStreamSource} of the screenshot in format, or <code>null</code> if
+     *         the screenshot was not successful.
+     * @throws DeviceNotAvailableException
+     */
+    public InputStreamSource getScreenshot(String format) throws DeviceNotAvailableException;
+
+    /**
      * Clears the last connected wifi network. This should be called when starting a new invocation
      * to avoid connecting to the wifi network used in the previous test after device reboots.
      */
diff --git a/src/com/android/tradefed/device/TestDevice.java b/src/com/android/tradefed/device/TestDevice.java
index ce47f77..73fd29e 100644
--- a/src/com/android/tradefed/device/TestDevice.java
+++ b/src/com/android/tradefed/device/TestDevice.java
@@ -48,6 +48,7 @@
 import com.android.tradefed.util.StreamUtil;
 
 import java.awt.image.BufferedImage;
+import java.awt.Image;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FilenameFilter;
@@ -1878,11 +1879,23 @@
      */
     @Override
     public InputStreamSource getScreenshot() throws DeviceNotAvailableException {
+        return getScreenshot("PNG");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public InputStreamSource getScreenshot(String format) throws DeviceNotAvailableException {
+        if (!format.equalsIgnoreCase("PNG") && !format.equalsIgnoreCase("JPEG")){
+            CLog.e("Screenshot: Format %s is not supported, defaulting to PNG.", format);
+            format = "PNG";
+        }
         ScreenshotAction action = new ScreenshotAction();
         if (performDeviceAction("screenshot", action, MAX_RETRY_ATTEMPTS)) {
-            byte[] pngData = compressRawImageAsPng(action.mRawScreenshot);
-            if (pngData != null) {
-                return new ByteArrayInputStreamSource(pngData);
+            byte[] imageData = compressRawImage(action.mRawScreenshot, format.toUpperCase());
+            if (imageData != null) {
+                return new ByteArrayInputStreamSource(imageData);
             }
         }
         return null;
@@ -1903,9 +1916,18 @@
         }
     }
 
-    private byte[] compressRawImageAsPng(RawImage rawImage) {
-        BufferedImage image = new BufferedImage(rawImage.width, rawImage.height,
-                BufferedImage.TYPE_INT_ARGB);
+    private byte[] compressRawImage(RawImage rawImage, String format) {
+        BufferedImage image = null;
+
+        if ("JPEG".equalsIgnoreCase(format)) {
+            //JPEG does not support ARGB without a special encoder
+            image = new BufferedImage(rawImage.width, rawImage.height,
+                    BufferedImage.TYPE_3BYTE_BGR);
+        }
+        else {
+            image = new BufferedImage(rawImage.width, rawImage.height,
+                    BufferedImage.TYPE_INT_ARGB);
+        }
 
         // borrowed conversion logic from platform/sdk/screenshot/.../Screenshot.java
         int index = 0;
@@ -1917,13 +1939,26 @@
                 image.setRGB(x, y, value);
             }
         }
+
+        // Rescale to reduce size if needed
+        // Screenshot default format is 1080 x 1920, 8-bit/color RGBA
+        // By cutting in half we can easily keep good quality and smaller size
+        int shortEdge = Math.min(image.getHeight(), image.getWidth());
+        if (shortEdge > 720) {
+            Image resized = image.getScaledInstance(image.getWidth() / 2, image.getHeight() / 2,
+                    Image.SCALE_SMOOTH);
+            image = new BufferedImage(image.getWidth() / 2, image.getHeight() / 2,
+                    Image.SCALE_REPLICATE);
+            image.getGraphics().drawImage(resized, 0, 0, null);
+        }
+
         // store compressed image in memory, and let callers write to persistent storage
         // use initial buffer size of 128K
-        byte[] pngData = null;
+        byte[] imageData = null;
         ByteArrayOutputStream imageOut = new ByteArrayOutputStream(128*1024);
         try {
-            if (ImageIO.write(image, "png", imageOut)) {
-                pngData = imageOut.toByteArray();
+            if (ImageIO.write(image, format, imageOut)) {
+                imageData = imageOut.toByteArray();
             } else {
                 CLog.e("Failed to compress screenshot to png");
             }
@@ -1932,7 +1967,7 @@
             CLog.e(e);
         }
         StreamUtil.close(imageOut);
-        return pngData;
+        return imageData;
     }
 
     /**
diff --git a/src/com/android/tradefed/result/LogDataType.java b/src/com/android/tradefed/result/LogDataType.java
index a50c952..cd89506 100644
--- a/src/com/android/tradefed/result/LogDataType.java
+++ b/src/com/android/tradefed/result/LogDataType.java
@@ -25,6 +25,7 @@
     HTML("html", true, true),
     PNG("png", true, false),
     ZIP("zip", true, false),
+    JPEG("jpeg", true, false),
     GZIP("gz", true, false),
     COVERAGE("ec", false, false),  /* Emma coverage file */
     /* Specific text file types */
diff --git a/tests/src/com/android/tradefed/device/StubTestDevice.java b/tests/src/com/android/tradefed/device/StubTestDevice.java
index 201c037..1049c59 100644
--- a/tests/src/com/android/tradefed/device/StubTestDevice.java
+++ b/tests/src/com/android/tradefed/device/StubTestDevice.java
@@ -697,6 +697,14 @@
      * {@inheritDoc}
      */
     @Override
+    public InputStreamSource getScreenshot(String format) throws DeviceNotAvailableException {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public void setEmulatorProcess(Process p) {
         // ignore
     }