1 /*
   2  * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.jemmy.image;
  24 
  25 import java.awt.AWTException;
  26 import java.awt.Rectangle;
  27 import java.awt.Robot;
  28 import java.awt.Toolkit;
  29 
  30 import java.awt.image.BufferedImage;
  31 
  32 import java.io.BufferedOutputStream;
  33 import java.io.ByteArrayOutputStream;
  34 import java.io.File;
  35 import java.io.FileNotFoundException;
  36 import java.io.IOException;
  37 import java.io.FileOutputStream;
  38 import java.io.OutputStream;
  39 
  40 import java.util.zip.CRC32;
  41 import java.util.zip.Deflater;
  42 import java.util.zip.DeflaterOutputStream;
  43 import org.jemmy.control.ScreenArea;
  44 
  45 /** This class allows to encode BufferedImage into B/W, greyscale or true color PNG
  46  * image format with maximum compression.<br>
  47  * It also provides complete functionality for capturing full screen, part of
  48  * screen or single component, encoding and saving captured image info PNG file.
  49  * @author Adam Sotona
  50  * @version 1.0 */
  51 public class PNGEncoder extends Object {
  52 
  53     /** black and white image mode. */
  54     public static final byte BW_MODE = 0;
  55     /** grey scale image mode. */
  56     public static final byte GREYSCALE_MODE = 1;
  57     /** full color image mode. */
  58     public static final byte COLOR_MODE = 2;
  59 
  60     OutputStream out;
  61     CRC32 crc;
  62     byte mode;
  63 
  64     /**
  65      *
  66      * @param file
  67      * @throws java.io.FileNotFoundException
  68      */
  69     public PNGEncoder(File file) throws FileNotFoundException {
  70         this(new FileOutputStream(file));
  71     }
  72     /** public constructor of PNGEncoder class with greyscale mode by default.
  73      * @param out output stream for PNG image format to write into
  74      */
  75     public PNGEncoder(OutputStream out) {
  76         this(out, GREYSCALE_MODE);
  77     }
  78 
  79     /** public constructor of PNGEncoder class.
  80      * @param out output stream for PNG image format to write into
  81      * @param mode BW_MODE, GREYSCALE_MODE or COLOR_MODE
  82      */
  83     public PNGEncoder(OutputStream out, byte mode) {
  84         crc=new CRC32();
  85         this.out = out;
  86         if (mode<0 || mode>2)
  87             throw new IllegalArgumentException("Unknown color mode");
  88         this.mode = mode;
  89     }
  90 
  91     void write(int i) throws IOException {
  92         byte b[]={(byte)((i>>24)&0xff),(byte)((i>>16)&0xff),(byte)((i>>8)&0xff),(byte)(i&0xff)};
  93         write(b);
  94     }
  95 
  96     void write(byte b[]) throws IOException {
  97         out.write(b);
  98         crc.update(b);
  99     }
 100 
 101     /** main encoding method (stays blocked till encoding is finished).
 102      * @param image BufferedImage to encode
 103      * @throws IOException IOException
 104      */
 105     public void encode(BufferedImage image) throws IOException {
 106         encode(image, true);
 107     }
 108 
 109     /** main encoding method (stays blocked till encoding is finished).
 110      * @param image BufferedImage to encode
 111      * @param closeStream requests method to close the stream after the image is written
 112      * @throws IOException IOException
 113      */
 114     public void encode(BufferedImage image, boolean closeStream) throws IOException {
 115         int width = image.getWidth(null);
 116         int height = image.getHeight(null);
 117         final byte id[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13};
 118         write(id);
 119         crc.reset();
 120         write("IHDR".getBytes());
 121         write(width);
 122         write(height);
 123         byte head[]=null;
 124         switch (mode) {
 125             case BW_MODE: head=new byte[]{1, 0, 0, 0, 0}; break;
 126             case GREYSCALE_MODE: head=new byte[]{8, 0, 0, 0, 0}; break;
 127             case COLOR_MODE: head=new byte[]{8, 2, 0, 0, 0}; break;
 128         }
 129         write(head);
 130         write((int) crc.getValue());
 131         ByteArrayOutputStream compressed = new ByteArrayOutputStream(65536);
 132         BufferedOutputStream bos = new BufferedOutputStream( new DeflaterOutputStream(compressed, new Deflater(9)));
 133         int pixel;
 134         int color;
 135         int colorset;
 136         switch (mode) {
 137             case BW_MODE:
 138                 int rest=width%8;
 139                 int bytes=width/8;
 140                 for (int y=0;y<height;y++) {
 141                     bos.write(0);
 142                     for (int x=0;x<bytes;x++) {
 143                         colorset=0;
 144                         for (int sh=0; sh<8; sh++) {
 145                             pixel=image.getRGB(x*8+sh,y);
 146                             color=((pixel >> 16) & 0xff);
 147                             color+=((pixel >> 8) & 0xff);
 148                             color+=(pixel & 0xff);
 149                             colorset<<=1;
 150                             if (color>=3*128)
 151                                 colorset|=1;
 152                         }
 153                         bos.write((byte)colorset);
 154                     }
 155                     if (rest>0) {
 156                         colorset=0;
 157                         for (int sh=0; sh<width%8; sh++) {
 158                             pixel=image.getRGB(bytes*8+sh,y);
 159                             color=((pixel >> 16) & 0xff);
 160                             color+=((pixel >> 8) & 0xff);
 161                             color+=(pixel & 0xff);
 162                             colorset<<=1;
 163                             if (color>=3*128)
 164                                 colorset|=1;
 165                         }
 166                         colorset<<=8-rest;
 167                         bos.write((byte)colorset);
 168                     }
 169                 }
 170                 break;
 171             case GREYSCALE_MODE:
 172                 for (int y=0;y<height;y++) {
 173                     bos.write(0);
 174                     for (int x=0;x<width;x++) {
 175                         pixel=image.getRGB(x,y);
 176                         color=((pixel >> 16) & 0xff);
 177                         color+=((pixel >> 8) & 0xff);
 178                         color+=(pixel & 0xff);
 179                         bos.write((byte)(color/3));
 180                     }
 181                 }
 182                 break;
 183              case COLOR_MODE:
 184                 for (int y=0;y<height;y++) {
 185                     bos.write(0);
 186                     for (int x=0;x<width;x++) {
 187                         pixel=image.getRGB(x,y);
 188                         bos.write((byte)((pixel >> 16) & 0xff));
 189                         bos.write((byte)((pixel >> 8) & 0xff));
 190                         bos.write((byte)(pixel & 0xff));
 191                     }
 192                 }
 193                 break;
 194         }
 195         bos.close();
 196         write(compressed.size());
 197         crc.reset();
 198         write("IDAT".getBytes());
 199         write(compressed.toByteArray());
 200         write((int) crc.getValue());
 201         write(0);
 202         crc.reset();
 203         write("IEND".getBytes());
 204         write((int) crc.getValue());
 205         out.flush();
 206         if (closeStream) {
 207             out.close();
 208         }
 209     }
 210 
 211     /** Static method performing screen capture into PNG image format file with given fileName.
 212      * @param rect Rectangle of screen to be captured
 213      * @param fileName file name for screen capture PNG image file */
 214     public static void captureScreen(Rectangle rect, String fileName) {
 215         captureScreen(rect, fileName, GREYSCALE_MODE);
 216     }
 217 
 218     /** Static method performing screen capture into PNG image format file with given fileName.
 219      * @param rect Rectangle of screen to be captured
 220      * @param mode image color mode
 221      * @param fileName file name for screen capture PNG image file */
 222     public static void captureScreen(Rectangle rect, String fileName, byte mode) {
 223         try {
 224             BufferedImage capture=new Robot().createScreenCapture(rect);
 225             BufferedOutputStream file=new BufferedOutputStream(new FileOutputStream(fileName));
 226             PNGEncoder encoder=new PNGEncoder(file, mode);
 227             encoder.encode(capture);
 228         } catch (AWTException awte) {
 229             awte.printStackTrace();
 230         } catch (IOException ioe) {
 231             ioe.printStackTrace();
 232         }
 233     }
 234 
 235      /** Static method performing one component screen capture into PNG image format file with given fileName.
 236       * @param comp Component to be captured
 237       * @param fileName String image target filename */
 238     public static void captureScreen(ScreenArea comp, String fileName) {
 239         captureScreen(comp, fileName, GREYSCALE_MODE);
 240     }
 241 
 242     /** Static method performing one component screen capture into PNG image format file with given fileName.
 243      * @param comp Component to be captured
 244      * @param fileName String image target filename
 245      * @param mode image color mode */
 246     public static void captureScreen(ScreenArea comp, String fileName, byte mode) {
 247         captureScreen(Utils.convert(comp.getScreenBounds()),
 248             fileName, mode);
 249     }
 250 
 251 
 252     /** Static method performing whole screen capture into PNG image format file with given fileName.
 253      * @param fileName String image target filename */
 254     public static void captureScreen(String fileName) {
 255         captureScreen(fileName, GREYSCALE_MODE);
 256     }
 257 
 258     /** Static method performing whole screen capture into PNG image format file with given fileName.
 259      * @param fileName String image target filename
 260      * @param mode image color mode */
 261     public static void captureScreen(String fileName, byte mode) {
 262         captureScreen(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()), fileName, mode);
 263     }
 264 }