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 ;
}
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>
Solution: First using reduce image quality, I defined
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;
}
}
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 :)