Search code examples
javaimageseleniumbase64screenshot

Using Selenium Webdriver TakesScreenshot method to capture a base64 image. Can I reduce the image size?


I am using Selenium's TakesScreenshot method getting a base64 string into my HTML report. The screenshot produced in the report (extentreports) is a little too big, and is partially pushed offscreen. Shrinking it would also reduce the report's file size.

Can I take a smaller base64 screenshot? Shrinking it by about 20% would be perfect... Or would I have to create the image as a File first, then shrink it, then convert to base64.

 public static String CaptureScreen(WebDriver driver) {
    TakesScreenshot newScreen = (TakesScreenshot) driver;
    String scnShot = newScreen.getScreenshotAs(OutputType.BASE64);
    return "data:image/jpg;base64, " + scnShot ;
}

Solution

  • If you want to take a smaller screenshot file. You should be reduced image quality or write file with a fixed dimension (height x width). Let check my solution hope this help:

    LIB:

    <!-- SELENIUM LIBs and Image Process LIBs -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-firefox-driver</artifactId>
            <version>2.41.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>2.41.0</version>
        </dependency>
        <dependency>
            <groupId>xml-apis</groupId>
            <artifactId>xml-apis</artifactId>
            <version>1.4.01</version>
        </dependency>
        <dependency>
            <groupId>com.sun.media</groupId>
            <artifactId>jai_codec</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>javax.media</groupId>
            <artifactId>jai_core</artifactId>
            <version>1.1.3</version>
        </dependency>
    
    1. Solution: First using reduce image quality, I defined

      • maximum image size is: ex: 1048576

      Take a screenshot to a file (you choose is a base64 is up to you)

      import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ImageObserver; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; import java.util.Iterator; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.FileImageOutputStream;

      /**

      • @author daviddoan * */ public class ScreenShotUtils implements ImageObserver {

        private static final Color BKGROUND_COLOR = Color.WHITE; private boolean imageLoaded = false;

        private static BufferedImage matrixTransparentPixels(BufferedImage originalBufferedImage, Color fillColor) { int w = originalBufferedImage.getWidth(); int h = originalBufferedImage.getHeight(); BufferedImage newGeneratedBufferedImage; if (originalBufferedImage.getType() == BufferedImage.TYPE_4BYTE_ABGR) { newGeneratedBufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); } else { newGeneratedBufferedImage = new BufferedImage(w, h, originalBufferedImage.getType()); } // clone the original image Graphics2D graphics2D = null; try { graphics2D = newGeneratedBufferedImage.createGraphics(); graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); final boolean isDrawingSuccess = graphics2D.drawImage(originalBufferedImage, 0, 0, w, h, fillColor, null); if (!isDrawingSuccess) { while (!imageLoaded) { System.err.println("DRAWING IMAGE, PLEASE WAITING..."); Thread.sleep(10000); } } } finally { if (graphics2D != null) { graphics2D.dispose(); } }

        return newGeneratedBufferedImage; }

        public static void reduceScreenShotQuality(File screenshotSource, String location, String fileName, String fileExtension) throws Exception {

        int sizeThreshold = 1048576; float quality = 1.0f; long fileSize = screenshotSource.length();

        if (fileSize <= sizeThreshold) { // Image file size is small, no need reduced any more.; writeImageToLocation(screenshotSource, location, fileName, "jpeg"); return; }

        // Need to reduce 'cos over sizeThreshold

        ImageWriter writer = createImageWriter();

        ImageWriteParam iwp = writer.getDefaultWriteParam();

        iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

        InputStream inputStream = new FileInputStream(screenshotSource);

        FileImageOutputStream output = getOutputStream(location, fileName, fileExtension);

        BufferedImage originalImage = ImageIO.read(inputStream); BufferedImage newBufferedImage;

        newBufferedImage = matrixTransparentPixels(originalImage, BKGROUND_COLOR);

        float percent = 0.1f; // 10% of 1

        while (fileSize > sizeThreshold) { if (percent >= quality) { percent = percent * 0.1f; }

        quality -= percent;

        iwp.setCompressionQuality(quality);

        writeImage(writer, newBufferedImage, output, iwp);

        File fileOut2 = new File(location + fileName + "." + fileExtension); long newFileSize = fileOut2.length(); if (newFileSize == fileSize) { // cannot reduce more break; } else { fileSize = newFileSize; } } // Flush all stream inputStream.close(); originalImage.flush(); newBufferedImage.flush(); writer.dispose(); }

        private static void writeImage(ImageWriter writer, BufferedImage bufferedImage, FileImageOutputStream output, ImageWriteParam iwp) { try { IIOImage image = new IIOImage(bufferedImage, null, null); writer.setOutput(output);

        if (iwp != null) { writer.write(null, image, iwp); } else { writer.write(image); } output.close(); } catch (IOException e) { System.err.println("writeImage: IOException- " + e.getMessage()); } finally { System.err.println("End writeImage"); }

        }

        private static FileImageOutputStream getOutputStream(String location, String fileName, String fileExtension) throws FileNotFoundException, IOException { File fileOut = new File(location + fileName + "." + fileExtension); if (fileOut.exists()) { fileOut.delete(); } FileImageOutputStream output = new FileImageOutputStream(fileOut);

        return output; }

        private static ImageWriter createImageWriter() { Iterator iter = ImageIO.getImageWritersByFormatName("jpeg"); ImageWriter writer = (ImageWriter) iter.next(); return writer; }

        private static void writeImageToLocation(File filePng, String location, String fileName, String extension) throws IOException { ImageWriter writer = createImageWriter();

        ImageWriteParam iwp = writer.getDefaultWriteParam();

        SeekableStream seekableStream = new FileSeekableStream(filePng); PNGDecodeParam pngParams = new PNGDecodeParam(); ImageDecoder dec = ImageCodec.createImageDecoder("png", seekableStream, pngParams); RenderedImage pngImage = dec.decodeAsRenderedImage();

        BufferedImage newImage = new BufferedImage(pngImage.getWidth(), pngImage.getHeight(), BufferedImage.TYPE_INT_RGB);

        newImage.createGraphics().drawImage(renderedImageToBufferedImage(pngImage), 0, 0, Color.BLACK, null);

        FileImageOutputStream output = getOutputStream(location, fileName, extension); writeImage(writer, newImage, output, iwp);

        newImage.flush(); seekableStream.close(); writer.dispose(); }

        private static BufferedImage renderedImageToBufferedImage(RenderedImage img) { if (img instanceof BufferedImage) { return (BufferedImage) img; } ColorModel cm = img.getColorModel(); int width = img.getWidth(); int height = img.getHeight(); WritableRaster raster = cm.createCompatibleWritableRaster(width, height); boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); Hashtable properties = new Hashtable(); String[] keys = img.getPropertyNames(); if (keys != null) { for (String key : keys) { properties.put(key, img.getProperty(key)); } } BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties); img.copyData(raster); return result; } } @Override public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { if (infoflags != ALLBITS) { // Image has not finished loading! // Return true to tell the image loading thread to keep drawing until image fully loads. return true; } else { imageLoaded = true; return false; } }

      1. Solution: Write as a thumbnail image (heigh x width)
    2. import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream;

      import javax.imageio.ImageIO; import javax.media.jai.JAI; import javax.media.jai.RenderedOp;

      import org.apache.log4j.Logger;

      import com.sun.media.jai.codec.SeekableStream;

      /**

        • @author daviddoan * */

      public class ThumbnailUtils implements ImageObserver {

      private static final String CLASS_NAME = ThumbnailUtils.class.getSimpleName();
      
      private final Color BKGROUND_COLOR = Color.WHITE;
      private boolean imageLoaded = false;
      
      public void writeThumbnail(String originalImagePath, int thumbWidth, int thumbHeight, String thumbImagePath)
              throws Exception {
      
          InputStream originalImageStream = new FileInputStream(originalImagePath);
          File fileOut = new File(thumbImagePath);
          if (fileOut.exists()) {
              fileOut.delete();
          }
          OutputStream thumbImageOutStream = new FileOutputStream(fileOut);
          writeThumbnail(originalImageStream, thumbWidth, thumbHeight, thumbImageOutStream);
          thumbImageOutStream.close();
          originalImageStream.close();
      }
      
      public void writeThumbnail(File file, int thumbWidth, int thumbHeight, String thumbImagePath) throws Exception {
      
          InputStream originalImageStream = new FileInputStream(file);
          File fileOut = new File(thumbImagePath);
          if (fileOut.exists()) {
              fileOut.delete();
          }
          OutputStream thumbImageOutStream = new FileOutputStream(fileOut);
      
          writeThumbnail(originalImageStream, thumbWidth, thumbHeight, thumbImageOutStream);
      
          thumbImageOutStream.close();
          originalImageStream.close();
      
      }
      
      private void writeThumbnail(InputStream originalImageStream, int thumbWidth, int thumbHeight,
              OutputStream thumbImageOutStream) throws Exception {
      
          ByteArrayOutputStream byteArrOutputStream = null;
          try {
              byteArrOutputStream = makeThumbnail(originalImageStream, thumbWidth, thumbHeight);
              byteArrOutputStream.writeTo(thumbImageOutStream);
              byteArrOutputStream.flush();
          } finally {
              if (byteArrOutputStream != null) {
                  byteArrOutputStream.close();
              }
          }
      
      }
      
      private Image makeThumbnail(final Image originalImage, final int maxWidth, final int maxHeight) throws Exception {
          // ImageOveserve is ingnored for BufferedImages
          final int photoWidth = originalImage.getWidth(null);
          final int photoHeight = originalImage.getHeight(null);
      
          int thumbWidth = maxWidth;
          int thumbHeight = maxHeight;
          final double thumbRatio = (double) thumbWidth / (double) thumbHeight;
          final double photoRatio = (double) photoWidth / (double) photoHeight;
      
          if (thumbRatio < photoRatio)
              thumbHeight = (int) (thumbWidth / photoRatio);
          else
              thumbWidth = (int) (thumbHeight * photoRatio);
      
          // Checking if the photo's size is smaller than thumbnail's size: getting the original size
          if (photoWidth < thumbWidth) {
              thumbWidth = photoWidth;
          }
          if (photoHeight < thumbHeight) {
              thumbHeight = photoHeight;
          }
      
          final BufferedImage thumbnailBufferedImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);
          final Graphics2D graphics2D = thumbnailBufferedImage.createGraphics();
      
          // Best Quality Render Hints!
          graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
          graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
          graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
          graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
          graphics2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
          graphics2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
                  RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
      
          final AffineTransform t = new AffineTransform();
          t.scale((double) thumbWidth / photoWidth, (double) thumbHeight / photoHeight);
      
          graphics2D.setBackground(BKGROUND_COLOR);
          final boolean complete = graphics2D.drawImage(originalImage, t, this);
      
          if (!complete) {
              while (!imageLoaded) {
                  try {
                      wait(100);
                  } catch (InterruptedException ie) {
                  }
              }
          }
      
          graphics2D.dispose();
      
          return thumbnailBufferedImage;
      }
      
      private synchronized ByteArrayOutputStream makeThumbnail(final InputStream originalImageStream, final int maxWidth,
              final int maxHeight) throws Exception {
      
          SeekableStream seekableStream = SeekableStream.wrapInputStream(originalImageStream, true);
          RenderedOp reop = JAI.create("stream", seekableStream);
          Image photo = reop.getAsBufferedImage();
      
          final BufferedImage thumbnail = (BufferedImage) makeThumbnail(photo, maxWidth, maxHeight);
      
          final ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
          ImageIO.write(thumbnail, GlobalResourceUtils.getFileExtension(), byteArrayStream);
      
          return byteArrayStream;
      }
      
      @Override
      public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
          if (infoflags != ALLBITS) {
              // Image has not finished loading!
              // Return true to tell the image loading thread to keep drawing until image fully loads.
              return true;
          } else {
              imageLoaded = true;
              return false;
          }
      }
      

      }

    Hope this help :)